From 4aaf939a7d64f26e8cde3183dab0a22538381495 Mon Sep 17 00:00:00 2001 From: Hamza Tahir Date: Thu, 3 Sep 2026 10:58:15 +0200 Subject: [PATCH 1/8] Installer: only the --upgrade hint means "older local server" `kitaru login --local` reports several failures with error kind "conflict", including "port 8000 is already in use by a deployment Kitaru does not own". The installer matched on the word and retried with --upgrade, which then failed with "there is no local deployment to upgrade", burying the real reason. Seen on a machine running the dev server on 8000. Match only the CLI's own --upgrade hint, and for any other failure show the CLI's message and hint, plus the --port alternative. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01UrD7eCgTu11F6qsYMS5JMP --- install.sh | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/install.sh b/install.sh index 4ce9a5420..034d054be 100755 --- a/install.sh +++ b/install.sh @@ -399,15 +399,24 @@ login_local() { if "$KITARU_BIN" login --local <"$in" 2>&1 | tee "$out"; then rm -f "$out"; LOGGED_IN=1; return 0 fi - if grep -qi -- "conflict\|--upgrade" "$out"; then + # Only the "older local server" conflict tells you to pass --upgrade. Other + # conflicts (say, port 8000 taken by something Kitaru does not own) share + # the same error kind and must not trigger an upgrade. + if grep -q -- "--upgrade" "$out"; then rm -f "$out" note "An older local server is running. Upgrading it to match kitaru $("$KITARU_BIN" --version); your database is kept." if "$KITARU_BIN" login --local --upgrade <"$in"; then LOGGED_IN=1; return 0; fi warn "Local server upgrade did not complete. Run: $LOCAL_UPGRADE_HINT" return 1 fi + # Surface the CLI's own explanation when it came back as JSON (no tty). + local msg hint + msg="$(sed -n 's/.*"message":"\([^"]*\)".*/\1/p' "$out" | head -1)" + hint="$(sed -n 's/.*"hint":"\([^"]*\)".*/\1/p' "$out" | head -1)" rm -f "$out" - warn "Local server did not start. Run: kitaru login --local" + warn "Local server did not start.${msg:+ $msg}" + if [ -n "$hint" ]; then note "$hint"; fi + note "Then run: kitaru login --local (or: kitaru login --local --port 9000)" return 1 } From 26c3aeccc7175792df613ce8c40c060292600408 Mon Sep 17 00:00:00 2001 From: Hamza Tahir Date: Thu, 3 Sep 2026 11:03:55 +0200 Subject: [PATCH 2/8] Installer: when port 8000 is taken, try 9000-9004 instead of stopping The CLI is the probe: it reports "port N is already in use", so step through the next few ports until one works. It remembers the chosen port for later logins and logout, so nothing else needs to know. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01UrD7eCgTu11F6qsYMS5JMP --- install.sh | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/install.sh b/install.sh index 034d054be..5dd5437ae 100755 --- a/install.sh +++ b/install.sh @@ -414,12 +414,35 @@ login_local() { msg="$(sed -n 's/.*"message":"\([^"]*\)".*/\1/p' "$out" | head -1)" hint="$(sed -n 's/.*"hint":"\([^"]*\)".*/\1/p' "$out" | head -1)" rm -f "$out" + # Port taken by something Kitaru does not own (a dev server, another app): + # try the next few ports, letting the CLI tell us which are free. It + # remembers the chosen port for later logins and logout. + if port_in_use "$msg"; then + local port + for port in 9000 9001 9002 9003 9004; do + note "$msg Trying port $port." + out="$(mktemp)" + if "$KITARU_BIN" login --local --port "$port" <"$in" 2>&1 | tee "$out"; then + rm -f "$out"; LOGGED_IN=1; return 0 + fi + msg="$(sed -n 's/.*"message":"\([^"]*\)".*/\1/p' "$out" | head -1)" + hint="$(sed -n 's/.*"hint":"\([^"]*\)".*/\1/p' "$out" | head -1)" + rm -f "$out" + port_in_use "$msg" || break + done + warn "Local server did not start.${msg:+ $msg}" + if [ -n "$hint" ]; then note "$hint"; fi + note "Then run: kitaru login --local --port " + return 1 + fi warn "Local server did not start.${msg:+ $msg}" if [ -n "$hint" ]; then note "$hint"; fi note "Then run: kitaru login --local (or: kitaru login --local --port 9000)" return 1 } +port_in_use() { printf '%s' "$1" | grep -qi "port .* in use"; } + LOGGED_IN=0 if [ "$KITARU_SKIP_LOGIN" = "1" ]; then note "Skipping login (--no-login)" From b4a63ac8e9919d7b15e0da6c1e2b23a5cea4ac8c Mon Sep 17 00:00:00 2001 From: Hamza Tahir Date: Thu, 3 Sep 2026 11:10:37 +0200 Subject: [PATCH 3/8] Installer: stop before login and print the two server options Login is the one real decision in the flow (local Docker or the managed cloud), and every edge case the installer accumulated (Docker down, port taken, older server, no terminal) came from guessing it. The script now ends after wiring the coding agent and prints: kitaru login --local local, in Docker. Free, open source. https://cloud.kitaru.ai managed cloud. 14-day trial, no credit card required. then: kitaru login --server still points the MCP server at a team server and the closing message then shows `kitaru login `. --no-login is gone. Docs and README updated; the smoke workflow no longer passes --no-login. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01UrD7eCgTu11F6qsYMS5JMP --- .github/workflows/installer.yml | 8 +- README.md | 4 +- docs/book/getting-started/installation.md | 10 +- install.sh | 135 ++++------------------ 4 files changed, 31 insertions(+), 126 deletions(-) diff --git a/.github/workflows/installer.yml b/.github/workflows/installer.yml index bef6ba475..9a55dbc1d 100644 --- a/.github/workflows/installer.yml +++ b/.github/workflows/installer.yml @@ -66,12 +66,12 @@ jobs: if [ "$SOURCE" = "url" ]; then curl -fsSL https://kitaru.ai/install; else cat install.sh; fi } for attempt in 1 2 3 4 5; do - get_script | bash -s -- --no-login && exit 0 + get_script | bash -s -- && exit 0 echo "installer attempt $attempt failed; retrying in 30s" >&2 sleep 30 done echo "::group::verbose rerun for diagnostics" - get_script | bash -s -- --no-login --verbose || true + get_script | bash -s -- --verbose || true echo "::endgroup::" exit 1 - name: Verify @@ -98,13 +98,13 @@ jobs: | grep -q ".local/bin" || { echo "PATH not persisted"; exit 1; } - name: Re-run is an upgrade, not an error shell: bash - run: cat install.sh | bash -s -- --no-login --no-skills --no-mcp --quiet + run: cat install.sh | bash -s -- --no-skills --no-mcp --quiet - name: A failed install must exit nonzero (quiet path) shell: bash # Regression for the status-swallowing bug in `quiet`: an impossible # version pin makes `uv tool install` fail, and that must surface. run: |- - if cat install.sh | bash -s -- --no-login --no-skills --no-mcp --version=99.99.99; then + if cat install.sh | bash -s -- --no-skills --no-mcp --version=99.99.99; then echo "installer exited 0 on a failed install" >&2; exit 1 fi echo "failed install correctly exited nonzero" diff --git a/README.md b/README.md index 2de44bf4a..f90bd93c3 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Kitaru turns that history into something you can test: ## ⚡ Get started -**1. Install and log in.** One line installs the CLI and MCP server (via `uv`), the coding-agent skills, registers the MCP server with Claude Code and Codex, and starts the local server if Docker is running: +**1. Install and log in.** One line installs the CLI and MCP server (via `uv`), the coding-agent skills, and registers the MCP server with Claude Code and Codex. It ends by printing the two ways to get a server: `kitaru login --local` (Docker, free) or the managed cloud (14-day trial, no credit card required): ```bash curl -fsSL https://kitaru.ai/install | bash @@ -53,7 +53,7 @@ curl -fsSL https://kitaru.ai/install | bash Already in Claude Code, Codex, or Cursor? Paste this instead and let it run the same installer: ``` -Set up Kitaru on this machine by following https://kitaru.ai/install.md. Use the one-line installer, tell me what it did, and stop before logging in if Docker is not running. +Set up Kitaru on this machine by following https://kitaru.ai/install.md. Use the one-line installer and tell me what it did. ``` Prefer to do it by hand, or want Kitaru inside your project's environment? The local server is FastAPI + Postgres, and `kitaru login --local` provisions it with Docker: diff --git a/docs/book/getting-started/installation.md b/docs/book/getting-started/installation.md index cf96937b5..064074672 100644 --- a/docs/book/getting-started/installation.md +++ b/docs/book/getting-started/installation.md @@ -5,7 +5,7 @@ icon: download # Installation -One command installs everything a first session needs. It needs [Docker](https://docs.docker.com/get-started/get-docker/) for the local server (the CLI installs without it) and works on macOS, Linux, WSL, and Git Bash on Windows: +One command installs everything a first session needs, on macOS, Linux, WSL, and Git Bash on Windows: ```bash curl -fsSL https://kitaru.ai/install | bash @@ -16,9 +16,9 @@ What it does, in order: 1. Installs the `kitaru` CLI and the `kitaru-mcp` server into an isolated [uv](https://docs.astral.sh/uv/) environment, installing uv first if you do not have it. No system Python is required. 2. Installs the [agent skills](../agent-native/setup.md) into `~/.agents/skills`, and into `~/.claude/skills` and `~/.codex/skills` when Claude Code or Codex is installed. 3. Registers the MCP server with Claude Code and Codex. -4. Runs `kitaru login --local`, which starts the server and PostgreSQL in Docker and logs you in. If Docker is not running, it prints that command for later instead. +4. Stops and prints the two ways to get a server: `kitaru login --local` for one on this machine in Docker (free, open source), or the managed cloud at https://cloud.kitaru.ai (14-day trial, no credit card required). -Nothing needs `sudo`, everything lands under your home directory, and running it again upgrades. Options: `--server https://your-team.kitaru.ai` logs in to a team server instead of starting a local one, `--with kitaru-pydantic-ai` adds an adapter to the same environment, `--no-login`, `--no-skills`, `--no-mcp` skip steps, and `--no-modify-path` leaves your shell rc files alone. `bash -s -- --help` after the pipe lists everything. +Nothing needs `sudo`, everything lands under your home directory, and running it again upgrades. Options: `--server https://your-team.kitaru.ai` points the MCP server at a team server instead of the local one, `--with kitaru-pydantic-ai` adds an adapter to the same environment, `--no-skills` and `--no-mcp` skip steps, and `--no-modify-path` leaves your shell rc files alone. `bash -s -- --help` after the pipe lists everything. Prefer to do it by hand? The installer is three commands, which you can run yourself: @@ -33,7 +33,7 @@ then point your assistant at `kitaru-mcp` as described in [Set up your coding ag Already inside Claude Code, Codex, or Cursor? Paste this instead and it runs the same installer for you: ``` -Set up Kitaru on this machine by following https://kitaru.ai/install.md. Use the one-line installer, tell me what it did, and stop before logging in if Docker is not running. +Set up Kitaru on this machine by following https://kitaru.ai/install.md. Use the one-line installer and tell me what it did. ``` ## Verify @@ -42,7 +42,7 @@ Set up Kitaru on this machine by following https://kitaru.ai/install.md. Use the kitaru doctor ``` -It checks the CLI, the server connection, authentication, and whether the skills are installed. If the installer skipped login because Docker was not running, start Docker and run `kitaru login --local`; the section below covers the local server's lifecycle. +It checks the CLI, the server connection, authentication, and whether the skills are installed. Server connection and authentication fail until you have run `kitaru login --local` (needs [Docker](https://docs.docker.com/get-started/get-docker/)) or logged in to a managed or team server; the sections below cover both. Then read the [Quickstart](quickstart.md). It is written as prompts for your coding agent, and everything it needs is now in place. diff --git a/install.sh b/install.sh index 5dd5437ae..e15d014d1 100755 --- a/install.sh +++ b/install.sh @@ -3,7 +3,6 @@ # # curl -fsSL https://kitaru.ai/install | bash # curl -fsSL https://kitaru.ai/install | bash -s -- --pre -# curl -fsSL https://kitaru.ai/install | bash -s -- --server https://team.kitaru.ai # # What it does, in order: # 1. Makes sure `uv` is available (installs it from astral.sh if not). @@ -15,8 +14,9 @@ # ~/.codex/skills when those CLIs are installed. No Node needed. # 4. Registers the Kitaru MCP server with Claude Code and Codex if their # CLIs are installed; prints the JSON for everything else. -# 5. If Docker is running and there is a terminal, runs `kitaru login --local` -# to start the local server. Otherwise tells you the one command to run. +# 5. Stops there and prints the two ways to get a server: local in Docker +# (`kitaru login --local`) or the managed cloud. Login is a decision, so +# the script does not make it for you. # # Nothing here needs sudo. Everything lands under $HOME. Re-running upgrades. # @@ -36,10 +36,9 @@ set -euo pipefail # --------------------------------------------------------------------------- KITARU_VERSION="${KITARU_VERSION:-}" # pin, e.g. 0.24.0 KITARU_PRE="${KITARU_PRE:-0}" # allow pre-releases -KITARU_SERVER="${KITARU_SERVER:-}" # team server URL instead of --local +KITARU_SERVER="${KITARU_SERVER:-}" # point the MCP server at a team server KITARU_SKIP_SKILLS="${KITARU_SKIP_SKILLS:-0}" KITARU_SKIP_MCP="${KITARU_SKIP_MCP:-0}" -KITARU_SKIP_LOGIN="${KITARU_SKIP_LOGIN:-0}" KITARU_QUIET="${KITARU_QUIET:-0}" KITARU_VERBOSE="${KITARU_VERBOSE:-0}" KITARU_WITH=() # extra packages, e.g. kitaru-pydantic-ai @@ -58,18 +57,17 @@ Usage: install.sh [options] --pre Allow pre-release versions --with=PKG Also install PKG into the same environment (repeatable), e.g. --with=kitaru-pydantic-ai --with=kitaru-langgraph - --server=URL Log in to a team/self-hosted server instead of starting - a local one + --server=URL Point the MCP server at a team/self-hosted server + (default: the local server, http://localhost:8000) --no-skills Skip installing the coding-agent skills --no-mcp Skip registering the MCP server with Claude Code / Codex - --no-login Skip `kitaru login` (just install the CLI) --no-modify-path Do not edit shell rc files; you add ~/.local/bin yourself --quiet Only print errors --verbose Print every command -h, --help This text Environment equivalents: KITARU_VERSION, KITARU_PRE=1, KITARU_SERVER, -KITARU_SKIP_SKILLS=1, KITARU_SKIP_MCP=1, KITARU_SKIP_LOGIN=1, +KITARU_SKIP_SKILLS=1, KITARU_SKIP_MCP=1, KITARU_NO_MODIFY_PATH=1, KITARU_QUIET=1, KITARU_VERBOSE=1, KITARU_PYTHON (default 3.12), NO_COLOR. USAGE @@ -86,7 +84,6 @@ while [ $# -gt 0 ]; do --server) shift; KITARU_SERVER="${1:-}" ;; --no-skills) KITARU_SKIP_SKILLS=1 ;; --no-mcp) KITARU_SKIP_MCP=1 ;; - --no-login) KITARU_SKIP_LOGIN=1 ;; --no-modify-path) KITARU_NO_MODIFY_PATH=1 ;; --quiet) KITARU_QUIET=1 ;; --verbose) KITARU_VERBOSE=1 ;; @@ -364,122 +361,30 @@ else fi fi -# --------------------------------------------------------------------------- -# 5. Login (local server via Docker, or a team server) -# --------------------------------------------------------------------------- -DOCKER_HINT="" -docker_running() { - have docker || { DOCKER_HINT="Docker is not installed."; return 1; } - local err; err="$(docker info 2>&1 >/dev/null)" && return 0 - case "$err" in - *"permission denied"*|*"Permission denied"*) - DOCKER_HINT="Docker is installed but this user cannot reach it. Add yourself to the docker group (sudo usermod -aG docker \$USER, then log out and in), or start Docker Desktop." ;; - *) DOCKER_HINT="Docker is not running." ;; - esac - return 1 -} -# A real terminal we can read from: stdin is one, or /dev/tty actually opens -# (checking the device node's permission bits is not enough in containers). -has_tty() { [ -t 0 ] || { : /dev/null; } - -login_cmd() { - if [ -n "$KITARU_SERVER" ]; then printf 'kitaru login %s' "$KITARU_SERVER" - else printf 'kitaru login --local'; fi -} - -# `kitaru login --local` refuses to replace an existing local server container -# from an older release unless told to with --upgrade (the database is kept). -# Running the installer is that approval: its documented contract is that a -# re-run upgrades. So on exactly that conflict, retry with --upgrade. -LOCAL_UPGRADE_HINT="kitaru login --local --upgrade" -login_local() { - # $1 = stdin source: /dev/tty when there is one, /dev/null otherwise - # (`kitaru login --local` needs no interaction). - local in="$1" out; out="$(mktemp)" - if "$KITARU_BIN" login --local <"$in" 2>&1 | tee "$out"; then - rm -f "$out"; LOGGED_IN=1; return 0 - fi - # Only the "older local server" conflict tells you to pass --upgrade. Other - # conflicts (say, port 8000 taken by something Kitaru does not own) share - # the same error kind and must not trigger an upgrade. - if grep -q -- "--upgrade" "$out"; then - rm -f "$out" - note "An older local server is running. Upgrading it to match kitaru $("$KITARU_BIN" --version); your database is kept." - if "$KITARU_BIN" login --local --upgrade <"$in"; then LOGGED_IN=1; return 0; fi - warn "Local server upgrade did not complete. Run: $LOCAL_UPGRADE_HINT" - return 1 - fi - # Surface the CLI's own explanation when it came back as JSON (no tty). - local msg hint - msg="$(sed -n 's/.*"message":"\([^"]*\)".*/\1/p' "$out" | head -1)" - hint="$(sed -n 's/.*"hint":"\([^"]*\)".*/\1/p' "$out" | head -1)" - rm -f "$out" - # Port taken by something Kitaru does not own (a dev server, another app): - # try the next few ports, letting the CLI tell us which are free. It - # remembers the chosen port for later logins and logout. - if port_in_use "$msg"; then - local port - for port in 9000 9001 9002 9003 9004; do - note "$msg Trying port $port." - out="$(mktemp)" - if "$KITARU_BIN" login --local --port "$port" <"$in" 2>&1 | tee "$out"; then - rm -f "$out"; LOGGED_IN=1; return 0 - fi - msg="$(sed -n 's/.*"message":"\([^"]*\)".*/\1/p' "$out" | head -1)" - hint="$(sed -n 's/.*"hint":"\([^"]*\)".*/\1/p' "$out" | head -1)" - rm -f "$out" - port_in_use "$msg" || break - done - warn "Local server did not start.${msg:+ $msg}" - if [ -n "$hint" ]; then note "$hint"; fi - note "Then run: kitaru login --local --port " - return 1 - fi - warn "Local server did not start.${msg:+ $msg}" - if [ -n "$hint" ]; then note "$hint"; fi - note "Then run: kitaru login --local (or: kitaru login --local --port 9000)" - return 1 -} - -port_in_use() { printf '%s' "$1" | grep -qi "port .* in use"; } - -LOGGED_IN=0 -if [ "$KITARU_SKIP_LOGIN" = "1" ]; then - note "Skipping login (--no-login)" -elif [ -z "$KITARU_SERVER" ] && ! docker_running; then - warn "$DOCKER_HINT The local Kitaru server was not started." - note "Fix that, then run: kitaru login --local" - note "Or use a team server: kitaru login https://.kitaru.ai" -elif [ -n "$KITARU_SERVER" ]; then - # Team login uses a device flow and needs a terminal. - if has_tty; then - step "Logging in ($(login_cmd))" - "$KITARU_BIN" login "$KITARU_SERVER" /dev/null 2>&1 || [ "${TOOL_BIN}" != "$(dirname "$(command -v kitaru)")" ]; then +if [ "$(PATH="$ORIG_PATH" command -v kitaru 2>/dev/null || true)" != "$KITARU_BIN" ]; then say " Open a new terminal so 'kitaru' is on your PATH." + say "" fi -if [ "$LOGGED_IN" = "0" ]; then - say " 1. $(login_cmd)" - say " 2. Open your coding agent in your agent's repo and say:" +if [ -n "$KITARU_SERVER" ]; then + say " Next, log in to your server:" + say "" + say " ${C_BOLD}kitaru login $KITARU_SERVER${C_RESET}" else - say " Open your coding agent in your agent's repo and say:" + say " Next, pick where your Kitaru server lives:" + say "" + say " ${C_BOLD}kitaru login --local${C_RESET} local, in Docker. Free, open source." + say " ${C_BOLD}https://cloud.kitaru.ai${C_RESET} managed cloud. 14-day trial, no credit card required." + say " then: kitaru login " fi say "" -say " ${C_BOLD}Use kitaru-investigation to investigate this agent.${C_RESET}" +say " Then, in your agent's repo, tell your coding agent:" +say " ${C_BOLD}Use kitaru-investigation to investigate this agent.${C_RESET}" say "" say " No agent yet? ${C_BOLD}Use kitaru-guided-tour to show me Kitaru on the example agent.${C_RESET}" say " Check setup: kitaru doctor" From 511394b8eaabac11b9c5ee218ad69dcb6565b867 Mon Sep 17 00:00:00 2001 From: Hamza Tahir Date: Thu, 3 Sep 2026 11:29:46 +0200 Subject: [PATCH 4/8] Installer: install into the project when run inside one The worker is the `kitaru` package itself (`kitaru worker start`), and agent replays run the agent in the worker's own environment. A `kitaru` in an isolated uv tool environment can therefore log in, import, and run evaluators, but never replay the user's agent. The one-liner was installing exactly that and not saying so. Now, run inside a Python project (pyproject.toml or uv.lock in the current directory), the installer does `uv add "kitaru[cli,mcp,worker]"` into that project's environment and registers the MCP server as `uv run --directory kitaru-mcp` at Claude Code project scope (./.mcp.json). Run anywhere else, it keeps the isolated tool install and the closing message says replays need Kitaru inside the agent's project. --project / --global force either. The success line reports where it installed in both modes, which was the question that prompted this. Docs and README say to run it inside the agent's repository. The smoke workflow gains a step that runs it inside a `uv init` project and checks pyproject.toml and `uv run kitaru`. Verified in Docker: project mode on ubuntu:24.04 (added to pyproject.toml, .venv/bin/kitaru, no global binary), global mode on alpine:3.21, and --project without a pyproject.toml fails with a clear message. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01UrD7eCgTu11F6qsYMS5JMP --- .github/workflows/installer.yml | 11 ++ README.md | 2 +- docs/book/getting-started/installation.md | 6 +- install.sh | 153 +++++++++++++++------- 4 files changed, 124 insertions(+), 48 deletions(-) diff --git a/.github/workflows/installer.yml b/.github/workflows/installer.yml index 9a55dbc1d..1e349e932 100644 --- a/.github/workflows/installer.yml +++ b/.github/workflows/installer.yml @@ -99,6 +99,17 @@ jobs: - name: Re-run is an upgrade, not an error shell: bash run: cat install.sh | bash -s -- --no-skills --no-mcp --quiet + - name: Inside a project, install into that project's environment + shell: bash + run: | + set -euxo pipefail + export PATH="$HOME/.local/bin:$PATH" + mkdir demo-agent && cd demo-agent + uv init --quiet -p 3.12 --name demo-agent . + cat ../install.sh | bash -s -- --no-skills --no-mcp + grep -q 'kitaru\[cli,mcp,worker\]' pyproject.toml + uv run kitaru --version + uv run kitaru-mcp --help >/dev/null - name: A failed install must exit nonzero (quiet path) shell: bash # Regression for the status-swallowing bug in `quiet`: an impossible diff --git a/README.md b/README.md index f90bd93c3..2505ef836 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Kitaru turns that history into something you can test: ## ⚡ Get started -**1. Install and log in.** One line installs the CLI and MCP server (via `uv`), the coding-agent skills, and registers the MCP server with Claude Code and Codex. It ends by printing the two ways to get a server: `kitaru login --local` (Docker, free) or the managed cloud (14-day trial, no credit card required): +**1. Install and log in.** Run this inside your agent's repository. It adds Kitaru to that project's environment with `uv` (so the worker can replay your agent next to its dependencies), installs the coding-agent skills, and registers the MCP server with Claude Code and Codex. Anywhere else it installs an isolated CLI instead. It ends by printing the two ways to get a server: `kitaru login --local` (Docker, free) or the managed cloud (14-day trial, no credit card required): ```bash curl -fsSL https://kitaru.ai/install | bash diff --git a/docs/book/getting-started/installation.md b/docs/book/getting-started/installation.md index 064074672..4c6eb1cd2 100644 --- a/docs/book/getting-started/installation.md +++ b/docs/book/getting-started/installation.md @@ -13,12 +13,12 @@ curl -fsSL https://kitaru.ai/install | bash What it does, in order: -1. Installs the `kitaru` CLI and the `kitaru-mcp` server into an isolated [uv](https://docs.astral.sh/uv/) environment, installing uv first if you do not have it. No system Python is required. +1. Installs the `kitaru` CLI and the `kitaru-mcp` server, installing [uv](https://docs.astral.sh/uv/) first if you do not have it. **Run it inside your agent's repository** (a directory with a `pyproject.toml` or `uv.lock`) and it does `uv add "kitaru[cli,mcp,worker]"` into that project's environment, which is where the worker has to live to replay your agent alongside its dependencies. Run it anywhere else and it installs an isolated `uv tool` environment with `kitaru` on your PATH, enough for the CLI, MCP server, imports and evaluators. `--project` and `--global` force either. No system Python is required. 2. Installs the [agent skills](../agent-native/setup.md) into `~/.agents/skills`, and into `~/.claude/skills` and `~/.codex/skills` when Claude Code or Codex is installed. 3. Registers the MCP server with Claude Code and Codex. 4. Stops and prints the two ways to get a server: `kitaru login --local` for one on this machine in Docker (free, open source), or the managed cloud at https://cloud.kitaru.ai (14-day trial, no credit card required). -Nothing needs `sudo`, everything lands under your home directory, and running it again upgrades. Options: `--server https://your-team.kitaru.ai` points the MCP server at a team server instead of the local one, `--with kitaru-pydantic-ai` adds an adapter to the same environment, `--no-skills` and `--no-mcp` skip steps, and `--no-modify-path` leaves your shell rc files alone. `bash -s -- --help` after the pipe lists everything. +Nothing needs `sudo`, everything lands in the project or under your home directory, and running it again upgrades. Options: `--version 0.24.0` pins a release, `--server https://your-team.kitaru.ai` points the MCP server at a team server instead of the local one, `--with kitaru-pydantic-ai` adds an adapter to the same environment, `--no-skills` and `--no-mcp` skip steps, and `--no-modify-path` leaves your shell rc files alone. `bash -s -- --help` after the pipe lists everything. Prefer to do it by hand? The installer is three commands, which you can run yourself: @@ -81,7 +81,7 @@ Node applications can also reuse a developer's selected CLI login without export ## Other ways to install -The installer is the right choice for the CLI on a laptop. The paths below are for when Kitaru has to live inside a specific environment: your agent's own code, a Node project, CI, or a machine where you only want the skills. +The installer run inside your agent's repository already installs into that project. The paths below are for adding the SDK by hand, Node projects, CI, or a machine where you only want the skills. Kitaru is three pieces: the **SDK + CLI**, a **server** your team shares (self-hosted, one per team), and **workers** that execute replays and evaluations in your environment. The CLI, server, and workers require **Python 3.11 or newer**; TypeScript agents use Node **22.22 or newer in the Node 22 release line** and connect to the same server. The server stores everything in **PostgreSQL**, provisioned for you locally by `kitaru login --local`; a [self-hosted deployment](../deploy/README.md) brings its own. Workers are plain processes (`kitaru worker start`) that run wherever your agent's environment lives; for containerized fleets, the published `zenmldocker/kitaru-worker` image works out of the box (see [Workers in production](../deploy/workers.md)). diff --git a/install.sh b/install.sh index e15d014d1..2cff965ed 100755 --- a/install.sh +++ b/install.sh @@ -6,9 +6,14 @@ # # What it does, in order: # 1. Makes sure `uv` is available (installs it from astral.sh if not). -# 2. `uv tool install kitaru[cli,mcp,worker]` into an isolated environment, -# with a uv-managed Python if the machine has none. Puts `kitaru` and -# `kitaru-mcp` on PATH (~/.local/bin) for future terminals. +# 2. Installs kitaru[cli,mcp,worker]. Where depends on where you run it: +# - inside a Python project (pyproject.toml or uv.lock in the current +# directory): `uv add` into that project's environment, so its worker +# can replay your agent alongside your agent's own dependencies; +# - anywhere else: `uv tool install` into an isolated environment, with +# `kitaru` and `kitaru-mcp` on PATH (~/.local/bin). Good for the CLI, +# MCP server, imports and evaluators; replays need the project form. +# --project / --global force either. # 3. Installs the Kitaru agent skills (zenml-io/kitaru-skills) from the # repository tarball into ~/.agents/skills, plus ~/.claude/skills and # ~/.codex/skills when those CLIs are installed. No Node needed. @@ -42,7 +47,8 @@ KITARU_SKIP_MCP="${KITARU_SKIP_MCP:-0}" KITARU_QUIET="${KITARU_QUIET:-0}" KITARU_VERBOSE="${KITARU_VERBOSE:-0}" KITARU_WITH=() # extra packages, e.g. kitaru-pydantic-ai -KITARU_PYTHON="${KITARU_PYTHON:-3.12}" # uv-managed Python if none suitable +KITARU_PYTHON="${KITARU_PYTHON:-3.12}" # uv-managed Python if none suitable (global mode) +KITARU_SCOPE="${KITARU_SCOPE:-auto}" # auto | project | global KITARU_SKILLS_REPO="${KITARU_SKILLS_REPO:-zenml-io/kitaru-skills}" KITARU_LOCAL_URL="${KITARU_LOCAL_URL:-http://localhost:8000}" KITARU_MCP_MODE="${KITARU_MCP_MODE:-standard}" @@ -57,6 +63,10 @@ Usage: install.sh [options] --pre Allow pre-release versions --with=PKG Also install PKG into the same environment (repeatable), e.g. --with=kitaru-pydantic-ai --with=kitaru-langgraph + --project Install into the Python project in the current directory + (uv add). Default when a pyproject.toml or uv.lock is here. + --global Install into an isolated uv tool environment on PATH. + Default anywhere else. --server=URL Point the MCP server at a team/self-hosted server (default: the local server, http://localhost:8000) --no-skills Skip installing the coding-agent skills @@ -67,7 +77,7 @@ Usage: install.sh [options] -h, --help This text Environment equivalents: KITARU_VERSION, KITARU_PRE=1, KITARU_SERVER, -KITARU_SKIP_SKILLS=1, KITARU_SKIP_MCP=1, +KITARU_SCOPE=project|global, KITARU_SKIP_SKILLS=1, KITARU_SKIP_MCP=1, KITARU_NO_MODIFY_PATH=1, KITARU_QUIET=1, KITARU_VERBOSE=1, KITARU_PYTHON (default 3.12), NO_COLOR. USAGE @@ -82,6 +92,8 @@ while [ $# -gt 0 ]; do --with) shift; KITARU_WITH+=("${1:-}") ;; --server=*) KITARU_SERVER="${1#*=}" ;; --server) shift; KITARU_SERVER="${1:-}" ;; + --project) KITARU_SCOPE=project ;; + --global) KITARU_SCOPE=global ;; --no-skills) KITARU_SKIP_SKILLS=1 ;; --no-mcp) KITARU_SKIP_MCP=1 ;; --no-modify-path) KITARU_NO_MODIFY_PATH=1 ;; @@ -215,42 +227,82 @@ persist_path() { } # --------------------------------------------------------------------------- -# 2. kitaru CLI + MCP server, in an isolated tool environment +# 2. kitaru CLI + MCP server: into this project, or an isolated tool env # --------------------------------------------------------------------------- SPEC="kitaru[cli,mcp,worker]" [ -n "$KITARU_VERSION" ] && SPEC="${SPEC}==${KITARU_VERSION}" +EXTRA_PKGS=() +for pkg in "${KITARU_WITH[@]:-}"; do [ -n "$pkg" ] && EXTRA_PKGS+=("$pkg"); done -UV_ARGS=(tool install --upgrade --quiet --python "$KITARU_PYTHON") -[ "$KITARU_PRE" = "1" ] && UV_ARGS+=(--prerelease allow) -for pkg in "${KITARU_WITH[@]:-}"; do - [ -n "$pkg" ] && UV_ARGS+=(--with "$pkg") -done - -step "Installing $SPEC" -quiet "$UV" "${UV_ARGS[@]}" "$SPEC" || die "uv tool install failed. Re-run with --verbose for details." +PROJECT_DIR="$PWD" +if [ "$KITARU_SCOPE" = "auto" ]; then + if [ -f "$PROJECT_DIR/pyproject.toml" ] || [ -f "$PROJECT_DIR/uv.lock" ]; then + KITARU_SCOPE=project + else + KITARU_SCOPE=global + fi +fi -# uv puts tool executables in its tool bin dir; make sure future shells see it. -TOOL_BIN="$("$UV" tool dir --bin 2>/dev/null || echo "$HOME/.local/bin")" -case "$(uname -s)" in MINGW*|MSYS*|CYGWIN*) TOOL_BIN="$(cygpath -u "$TOOL_BIN" 2>/dev/null || echo "$TOOL_BIN")" ;; esac -ensure_path "$TOOL_BIN" -if [ "$KITARU_NO_MODIFY_PATH" = "1" ]; then - note "Not editing shell rc files (--no-modify-path). Make sure $TOOL_BIN is on your PATH." +if [ "$KITARU_SCOPE" = "project" ]; then + # ---- into the project in the current directory --------------------------- + # The worker runs your agent, so it has to live where your agent's + # dependencies live. `uv add` puts kitaru into this project's environment + # (creating .venv if needed) and records it in pyproject.toml. + [ -f "$PROJECT_DIR/pyproject.toml" ] || die "--project needs a pyproject.toml in $PROJECT_DIR (run \`uv init\` first, or use --global)." + step "Installing $SPEC into this project ($PROJECT_DIR)" + UV_ADD=(add --quiet) + [ "$KITARU_PRE" = "1" ] && UV_ADD+=(--prerelease allow) + quiet "$UV" "${UV_ADD[@]}" "$SPEC" "${EXTRA_PKGS[@]:+${EXTRA_PKGS[@]}}" \ + || die "uv add failed (Kitaru needs Python >= 3.11; check requires-python). Re-run with --verbose for details." + VENV_DIR="${UV_PROJECT_ENVIRONMENT:-$PROJECT_DIR/.venv}" + case "$(uname -s)" in + MINGW*|MSYS*|CYGWIN*) TOOL_BIN="$VENV_DIR/Scripts" ;; + *) TOOL_BIN="$VENV_DIR/bin" ;; + esac + KITARU_BIN="$TOOL_BIN/kitaru" + [ -x "$KITARU_BIN" ] || [ -x "$KITARU_BIN.exe" ] || die "uv reported success but $KITARU_BIN is missing. Re-run with --verbose." + ok "kitaru $("$KITARU_BIN" --version 2>/dev/null) installed into this project's environment" + note "environment: $VENV_DIR (recorded in pyproject.toml)" + note "run it as: uv run kitaru ... (or activate the environment)" + # MCP: run through uv so the assistant gets this project's environment. + MCP_CMD=("$UV" run --directory "$PROJECT_DIR" kitaru-mcp) + CLAUDE_SCOPE=project else - quiet "$UV" tool update-shell || true - persist_path "$TOOL_BIN" -fi + # ---- isolated tool environment on PATH ----------------------------------- + UV_ARGS=(tool install --upgrade --quiet --python "$KITARU_PYTHON") + [ "$KITARU_PRE" = "1" ] && UV_ARGS+=(--prerelease allow) + for pkg in "${EXTRA_PKGS[@]:+${EXTRA_PKGS[@]}}"; do UV_ARGS+=(--with "$pkg"); done + + step "Installing $SPEC (isolated, on PATH)" + quiet "$UV" "${UV_ARGS[@]}" "$SPEC" || die "uv tool install failed. Re-run with --verbose for details." + + # uv puts tool executables in its tool bin dir; make sure future shells see it. + TOOL_BIN="$("$UV" tool dir --bin 2>/dev/null || echo "$HOME/.local/bin")" + TOOL_ENV="$("$UV" tool dir 2>/dev/null || echo "$HOME/.local/share/uv/tools")/kitaru" + case "$(uname -s)" in MINGW*|MSYS*|CYGWIN*) TOOL_BIN="$(cygpath -u "$TOOL_BIN" 2>/dev/null || echo "$TOOL_BIN")" ;; esac + ensure_path "$TOOL_BIN" + if [ "$KITARU_NO_MODIFY_PATH" = "1" ]; then + note "Not editing shell rc files (--no-modify-path). Make sure $TOOL_BIN is on your PATH." + else + quiet "$UV" tool update-shell || true + persist_path "$TOOL_BIN" + fi -KITARU_BIN="$TOOL_BIN/kitaru" -[ -x "$KITARU_BIN" ] || [ -x "$KITARU_BIN.exe" ] || die "uv reported success but $KITARU_BIN is missing. Re-run with --verbose." -ok "kitaru $("$KITARU_BIN" --version 2>/dev/null) installed" -note "$TOOL_BIN/kitaru, $TOOL_BIN/kitaru-mcp" -# Warn if a different kitaru was already reachable on the PATH the user -# started with (a pip or pipx install, say): depending on their rc order it -# may keep winning in new terminals. -hash -r -RESOLVED_KITARU="$(PATH="$ORIG_PATH" command -v kitaru 2>/dev/null || true)" -if [ -n "$RESOLVED_KITARU" ] && [ "$RESOLVED_KITARU" != "$KITARU_BIN" ] && [ "$RESOLVED_KITARU" != "$KITARU_BIN.exe" ]; then - warn "Another kitaru at $RESOLVED_KITARU ($("$RESOLVED_KITARU" --version 2>/dev/null || echo unknown)) shadows the one just installed. Remove it or put $TOOL_BIN first on PATH." + KITARU_BIN="$TOOL_BIN/kitaru" + [ -x "$KITARU_BIN" ] || [ -x "$KITARU_BIN.exe" ] || die "uv reported success but $KITARU_BIN is missing. Re-run with --verbose." + ok "kitaru $("$KITARU_BIN" --version 2>/dev/null) installed" + note "isolated environment: $TOOL_ENV (uv tool; no other Python touched)" + note "commands: $TOOL_BIN/kitaru, $TOOL_BIN/kitaru-mcp" + # Warn if a different kitaru was already reachable on the PATH the user + # started with (a pip or pipx install, say): depending on their rc order it + # may keep winning in new terminals. + hash -r + RESOLVED_KITARU="$(PATH="$ORIG_PATH" command -v kitaru 2>/dev/null || true)" + if [ -n "$RESOLVED_KITARU" ] && [ "$RESOLVED_KITARU" != "$KITARU_BIN" ] && [ "$RESOLVED_KITARU" != "$KITARU_BIN.exe" ]; then + warn "Another kitaru at $RESOLVED_KITARU ($("$RESOLVED_KITARU" --version 2>/dev/null || echo unknown)) shadows the one just installed. Remove it or put $TOOL_BIN first on PATH." + fi + MCP_CMD=("$TOOL_BIN/kitaru-mcp") + CLAUDE_SCOPE=user fi # --------------------------------------------------------------------------- @@ -321,7 +373,7 @@ fi # 4. MCP server registration # --------------------------------------------------------------------------- MCP_SERVER_URL="${KITARU_SERVER:-$KITARU_LOCAL_URL}" -MCP_BIN="$TOOL_BIN/kitaru-mcp" +MCP_ARGS=(--server "$MCP_SERVER_URL" --mode "$KITARU_MCP_MODE") if [ "$KITARU_SKIP_MCP" = "1" ]; then note "Skipping MCP registration (--no-mcp)" @@ -329,18 +381,20 @@ else step "Registering the Kitaru MCP server" registered=0 if have claude; then - # User-scope MCP servers live in ~/.claude.json. Snapshot it so a failed - # replace can put the previous entry back instead of leaving none. - CLAUDE_CFG="$HOME/.claude.json"; CLAUDE_CFG_BAK="" + # User-scope MCP servers live in ~/.claude.json, project-scope ones in + # ./.mcp.json. Snapshot the file so a failed replace can put the previous + # entry back instead of leaving none. + if [ "$CLAUDE_SCOPE" = "project" ]; then CLAUDE_CFG="$PROJECT_DIR/.mcp.json"; else CLAUDE_CFG="$HOME/.claude.json"; fi + CLAUDE_CFG_BAK="" if [ -f "$CLAUDE_CFG" ]; then CLAUDE_CFG_BAK="$(mktemp)"; cp "$CLAUDE_CFG" "$CLAUDE_CFG_BAK" fi if claude mcp get kitaru >/dev/null 2>&1; then - quiet claude mcp remove --scope user kitaru || true + quiet claude mcp remove --scope "$CLAUDE_SCOPE" kitaru || true fi - if quiet claude mcp add --scope user kitaru -- "$MCP_BIN" --server "$MCP_SERVER_URL" --mode "$KITARU_MCP_MODE" \ + if quiet claude mcp add --scope "$CLAUDE_SCOPE" kitaru -- "${MCP_CMD[@]}" "${MCP_ARGS[@]}" \ && claude mcp get kitaru >/dev/null 2>&1; then - ok "Claude Code: MCP server 'kitaru' (user scope)"; registered=1 + ok "Claude Code: MCP server 'kitaru' ($CLAUDE_SCOPE scope)"; registered=1 else if [ -n "$CLAUDE_CFG_BAK" ]; then cp "$CLAUDE_CFG_BAK" "$CLAUDE_CFG"; fi warn "Claude Code: could not register MCP server; previous config left as it was." @@ -349,7 +403,7 @@ else fi if have codex; then # `codex mcp add` overwrites an existing name, so no remove first. - if quiet codex mcp add kitaru -- "$MCP_BIN" --server "$MCP_SERVER_URL" --mode "$KITARU_MCP_MODE"; then + if quiet codex mcp add kitaru -- "${MCP_CMD[@]}" "${MCP_ARGS[@]}"; then ok "Codex: MCP server 'kitaru'"; registered=1 else warn "Codex: could not register MCP server" @@ -357,7 +411,9 @@ else fi if [ "$registered" = "0" ]; then note "No Claude Code or Codex CLI found. For Cursor or any MCP client, add:" - note " {\"mcpServers\":{\"kitaru\":{\"command\":\"$MCP_BIN\",\"args\":[\"--server\",\"$MCP_SERVER_URL\",\"--mode\",\"$KITARU_MCP_MODE\"]}}}" + MCP_JSON_ARGS="" + for a in "${MCP_CMD[@]:1}" "${MCP_ARGS[@]}"; do MCP_JSON_ARGS="$MCP_JSON_ARGS${MCP_JSON_ARGS:+,}\"$a\""; done + note " {\"mcpServers\":{\"kitaru\":{\"command\":\"${MCP_CMD[0]}\",\"args\":[$MCP_JSON_ARGS]}}}" fi fi @@ -367,7 +423,11 @@ fi say "" say "${C_GREEN}◆${C_RESET} ${C_BOLD}Kitaru is installed.${C_RESET}" say "" -if [ "$(PATH="$ORIG_PATH" command -v kitaru 2>/dev/null || true)" != "$KITARU_BIN" ]; then +if [ "$KITARU_SCOPE" = "project" ]; then + say " Installed into this project's environment. Run it as ${C_BOLD}uv run kitaru ...${C_RESET}" + say " (or activate $VENV_DIR)." + say "" +elif [ "$(PATH="$ORIG_PATH" command -v kitaru 2>/dev/null || true)" != "$KITARU_BIN" ]; then say " Open a new terminal so 'kitaru' is on your PATH." say "" fi @@ -385,6 +445,11 @@ fi say "" say " Then, in your agent's repo, tell your coding agent:" say " ${C_BOLD}Use kitaru-investigation to investigate this agent.${C_RESET}" +if [ "$KITARU_SCOPE" = "global" ]; then + say "" + say " Replays run your agent, so they need Kitaru inside the agent's own project:" + say " re-run this installer from that repo, or ${C_BOLD}uv add \"kitaru[cli,worker]\" kitaru-${C_RESET} there." +fi say "" say " No agent yet? ${C_BOLD}Use kitaru-guided-tour to show me Kitaru on the example agent.${C_RESET}" say " Check setup: kitaru doctor" From df39e0369ac3e6ce96ffb4adc80af00870db4fc7 Mon Sep 17 00:00:00 2001 From: Hamza Tahir Date: Thu, 3 Sep 2026 11:37:41 +0200 Subject: [PATCH 5/8] Installer smoke: run the global case from an empty directory The checkout root is itself a Python project named kitaru, so the project-aware installer tried to uv add kitaru into the Kitaru repo. Every step now runs from a temp dir and reads the script by absolute path. The uv add failure message no longer blames requires-python for an error uv already printed. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01UrD7eCgTu11F6qsYMS5JMP --- .github/workflows/installer.yml | 15 ++++++++++----- install.sh | 2 +- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.github/workflows/installer.yml b/.github/workflows/installer.yml index 1e349e932..6787c37bc 100644 --- a/.github/workflows/installer.yml +++ b/.github/workflows/installer.yml @@ -63,8 +63,11 @@ jobs: # lagging a publish by a few seconds. run: | get_script() { - if [ "$SOURCE" = "url" ]; then curl -fsSL https://kitaru.ai/install; else cat install.sh; fi + if [ "$SOURCE" = "url" ]; then curl -fsSL https://kitaru.ai/install; else cat "$GITHUB_WORKSPACE/install.sh"; fi } + # Run from an empty directory: the checkout is itself a Python project, + # which would put the installer into project mode. + cd "$(mktemp -d)" for attempt in 1 2 3 4 5; do get_script | bash -s -- && exit 0 echo "installer attempt $attempt failed; retrying in 30s" >&2 @@ -98,15 +101,16 @@ jobs: | grep -q ".local/bin" || { echo "PATH not persisted"; exit 1; } - name: Re-run is an upgrade, not an error shell: bash - run: cat install.sh | bash -s -- --no-skills --no-mcp --quiet + run: cd "$(mktemp -d)" && cat "$GITHUB_WORKSPACE/install.sh" | bash -s -- + --no-skills --no-mcp --quiet - name: Inside a project, install into that project's environment shell: bash run: | set -euxo pipefail export PATH="$HOME/.local/bin:$PATH" - mkdir demo-agent && cd demo-agent + cd "$(mktemp -d)" uv init --quiet -p 3.12 --name demo-agent . - cat ../install.sh | bash -s -- --no-skills --no-mcp + cat "$GITHUB_WORKSPACE/install.sh" | bash -s -- --no-skills --no-mcp grep -q 'kitaru\[cli,mcp,worker\]' pyproject.toml uv run kitaru --version uv run kitaru-mcp --help >/dev/null @@ -115,7 +119,8 @@ jobs: # Regression for the status-swallowing bug in `quiet`: an impossible # version pin makes `uv tool install` fail, and that must surface. run: |- - if cat install.sh | bash -s -- --no-skills --no-mcp --version=99.99.99; then + cd "$(mktemp -d)" + if cat "$GITHUB_WORKSPACE/install.sh" | bash -s -- --no-skills --no-mcp --version=99.99.99; then echo "installer exited 0 on a failed install" >&2; exit 1 fi echo "failed install correctly exited nonzero" diff --git a/install.sh b/install.sh index 2cff965ed..d5fa7466b 100755 --- a/install.sh +++ b/install.sh @@ -253,7 +253,7 @@ if [ "$KITARU_SCOPE" = "project" ]; then UV_ADD=(add --quiet) [ "$KITARU_PRE" = "1" ] && UV_ADD+=(--prerelease allow) quiet "$UV" "${UV_ADD[@]}" "$SPEC" "${EXTRA_PKGS[@]:+${EXTRA_PKGS[@]}}" \ - || die "uv add failed (Kitaru needs Python >= 3.11; check requires-python). Re-run with --verbose for details." + || die "uv add failed (uv's message is above; Kitaru needs Python >= 3.11). To install outside this project instead: --global" VENV_DIR="${UV_PROJECT_ENVIRONMENT:-$PROJECT_DIR/.venv}" case "$(uname -s)" in MINGW*|MSYS*|CYGWIN*) TOOL_BIN="$VENV_DIR/Scripts" ;; From df081dd34de3d1c6411e86509ba61910fbe3a365 Mon Sep 17 00:00:00 2001 From: Hamza Tahir Date: Thu, 3 Sep 2026 11:51:18 +0200 Subject: [PATCH 6/8] Docs: lead the installation page with "run it in your agent's repository" The first line now says where to run the one-liner and why (the worker lives with the agent's dependencies). Options move to a table, the by-hand equivalent is the in-project form, the not-in-a-repo case is a callout, and the paste-into-your-agent prompt says to open the repository first. README step 1 and the setup-page hint match. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01UrD7eCgTu11F6qsYMS5JMP --- README.md | 8 ++-- docs/book/agent-native/setup.md | 2 +- docs/book/getting-started/installation.md | 48 ++++++++++++++++------- 3 files changed, 39 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 2505ef836..a95b95490 100644 --- a/README.md +++ b/README.md @@ -44,19 +44,19 @@ Kitaru turns that history into something you can test: ## ⚡ Get started -**1. Install and log in.** Run this inside your agent's repository. It adds Kitaru to that project's environment with `uv` (so the worker can replay your agent next to its dependencies), installs the coding-agent skills, and registers the MCP server with Claude Code and Codex. Anywhere else it installs an isolated CLI instead. It ends by printing the two ways to get a server: `kitaru login --local` (Docker, free) or the managed cloud (14-day trial, no credit card required): +**1. Install.** Open a terminal in your agent's repository and run one line. It adds Kitaru to that project's environment with `uv` (the worker that replays your agent has to live next to its dependencies), installs the coding-agent skills, and registers the MCP server with Claude Code and Codex. It ends by printing the two ways to get a server: `kitaru login --local` (Docker, free) or the managed cloud (14-day trial, no credit card required). ```bash curl -fsSL https://kitaru.ai/install | bash ``` -Already in Claude Code, Codex, or Cursor? Paste this instead and let it run the same installer: +Already in Claude Code, Codex, or Cursor? Open the repository there and paste this instead: ``` -Set up Kitaru on this machine by following https://kitaru.ai/install.md. Use the one-line installer and tell me what it did. +Set up Kitaru in this repository by following https://kitaru.ai/install.md. Use the one-line installer and tell me what it did. ``` -Prefer to do it by hand, or want Kitaru inside your project's environment? The local server is FastAPI + Postgres, and `kitaru login --local` provisions it with Docker: +Prefer to do it by hand? The local server is FastAPI + Postgres, and `kitaru login --local` provisions it with Docker: ```bash uv add "kitaru[cli,worker,mcp]" kitaru-pydantic-ai # or: pip install diff --git a/docs/book/agent-native/setup.md b/docs/book/agent-native/setup.md index cf4f6aaac..5207bd5a0 100644 --- a/docs/book/agent-native/setup.md +++ b/docs/book/agent-native/setup.md @@ -13,7 +13,7 @@ Kitaru observes your production agents; your coding assistant is how you talk to Skills and MCP work together: the skills say how to work, and the server bounds what can be touched. {% hint style="success" %} -Used the [one-line installer](../getting-started/installation.md)? It already installed the MCP server, registered it with Claude Code and Codex (user scope, pointed at `http://localhost:8000` in `standard` mode), and installed the skills. Skip to [Capability modes and tools](#capability-modes-and-tools) unless you use another assistant or a different server URL. +Used the [one-line installer](../getting-started/installation.md)? It already installed the MCP server, registered it with Claude Code (in your repo's `.mcp.json` when run inside a repository, user scope otherwise) and Codex, pointed at `http://localhost:8000` in `standard` mode, and installed the skills. Skip to [Capability modes and tools](#capability-modes-and-tools) unless you use another assistant or a different server URL. {% endhint %} ## Install the MCP server diff --git a/docs/book/getting-started/installation.md b/docs/book/getting-started/installation.md index 4c6eb1cd2..ee157656a 100644 --- a/docs/book/getting-started/installation.md +++ b/docs/book/getting-started/installation.md @@ -5,35 +5,55 @@ icon: download # Installation -One command installs everything a first session needs, on macOS, Linux, WSL, and Git Bash on Windows: +Open a terminal **in your agent's repository** and run: ```bash curl -fsSL https://kitaru.ai/install | bash ``` -What it does, in order: +That one command: -1. Installs the `kitaru` CLI and the `kitaru-mcp` server, installing [uv](https://docs.astral.sh/uv/) first if you do not have it. **Run it inside your agent's repository** (a directory with a `pyproject.toml` or `uv.lock`) and it does `uv add "kitaru[cli,mcp,worker]"` into that project's environment, which is where the worker has to live to replay your agent alongside its dependencies. Run it anywhere else and it installs an isolated `uv tool` environment with `kitaru` on your PATH, enough for the CLI, MCP server, imports and evaluators. `--project` and `--global` force either. No system Python is required. -2. Installs the [agent skills](../agent-native/setup.md) into `~/.agents/skills`, and into `~/.claude/skills` and `~/.codex/skills` when Claude Code or Codex is installed. -3. Registers the MCP server with Claude Code and Codex. -4. Stops and prints the two ways to get a server: `kitaru login --local` for one on this machine in Docker (free, open source), or the managed cloud at https://cloud.kitaru.ai (14-day trial, no credit card required). +1. Adds `kitaru[cli,mcp,worker]` to the project's environment with `uv add`. The worker that replays your agent has to live next to your agent's dependencies, so this is the environment that matters. uv is installed first if you do not have it; no system Python and no `sudo` are needed. +2. Installs the [agent skills](../agent-native/setup.md) into `~/.agents/skills`, plus `~/.claude/skills` and `~/.codex/skills` when Claude Code or Codex is installed. +3. Registers the MCP server with Claude Code (in the repo's `.mcp.json`) and Codex, as `uv run --directory kitaru-mcp`. +4. Prints the two ways to get a server, and stops: -Nothing needs `sudo`, everything lands in the project or under your home directory, and running it again upgrades. Options: `--version 0.24.0` pins a release, `--server https://your-team.kitaru.ai` points the MCP server at a team server instead of the local one, `--with kitaru-pydantic-ai` adds an adapter to the same environment, `--no-skills` and `--no-mcp` skip steps, and `--no-modify-path` leaves your shell rc files alone. `bash -s -- --help` after the pipe lists everything. +``` +kitaru login --local local, in Docker. Free, open source. +https://cloud.kitaru.ai managed cloud. 14-day trial, no credit card required. + then: kitaru login +``` + +Works on macOS, Linux, WSL, and Git Bash on Windows. Running it again upgrades. + +{% hint style="info" %} +**Not in a repository?** Run it anywhere and it installs an isolated `kitaru` CLI on your PATH instead (a `uv tool` environment under `~/.local/share/uv/tools/kitaru`). That is enough to log in, import traces, run evaluators, and serve MCP, but replays need Kitaru inside the agent's own project, so re-run the installer there when you have one. `--project` and `--global` force either mode. +{% endhint %} + +| Option | Effect | +| --- | --- | +| `--version 0.24.0` | Pin a Kitaru release (`--pre` allows pre-releases) | +| `--with kitaru-pydantic-ai` | Also install a package into the same environment (repeatable) | +| `--server https://your-team.kitaru.ai` | Point the MCP server at a team server instead of `http://localhost:8000` | +| `--project` / `--global` | Force the in-project or the isolated install | +| `--no-skills`, `--no-mcp` | Skip those steps | +| `--no-modify-path` | Leave your shell rc files alone (global mode) | + +`curl -fsSL https://kitaru.ai/install | bash -s -- --help` lists everything, with environment-variable equivalents. -Prefer to do it by hand? The installer is three commands, which you can run yourself: +**Prefer to do it by hand?** Inside your repository, the installer is equivalent to: ```bash -uv tool install "kitaru[cli,mcp,worker]" # CLI + MCP server, isolated from your projects -npx skills add zenml-io/kitaru-skills # the coding-agent skills -kitaru login --local # local server in Docker, or: kitaru login +uv add "kitaru[cli,mcp,worker]" kitaru-pydantic-ai # into this project; pick your adapter +npx skills add zenml-io/kitaru-skills # the coding-agent skills ``` -then point your assistant at `kitaru-mcp` as described in [Set up your coding agent](../agent-native/setup.md). +plus registering `uv run kitaru-mcp --server http://localhost:8000 --mode standard` with your assistant, as described in [Set up your coding agent](../agent-native/setup.md). -Already inside Claude Code, Codex, or Cursor? Paste this instead and it runs the same installer for you: +**Already inside Claude Code, Codex, or Cursor?** Open your agent's repository there, paste this, and it runs the same installer for you: ``` -Set up Kitaru on this machine by following https://kitaru.ai/install.md. Use the one-line installer and tell me what it did. +Set up Kitaru in this repository by following https://kitaru.ai/install.md. Use the one-line installer and tell me what it did. ``` ## Verify From cd66af9c78a20884057a9a520f3b112ad7c854cb Mon Sep 17 00:00:00 2001 From: Hamza Tahir Date: Thu, 3 Sep 2026 12:10:56 +0200 Subject: [PATCH 7/8] Installer: say `kitaru login` for the managed cloud; uv run prefix in project mode Bare `kitaru login` connects to the managed cloud once #967 lands, so the closing message names it instead of the signup URL. In project mode kitaru is not on PATH, so every command in the message is prefixed with `uv run`. Docs and README match. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01UrD7eCgTu11F6qsYMS5JMP --- README.md | 2 +- docs/book/getting-started/installation.md | 9 +++++---- install.sh | 17 +++++++++-------- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index a95b95490..2d3dca50b 100644 --- a/README.md +++ b/README.md @@ -44,7 +44,7 @@ Kitaru turns that history into something you can test: ## ⚡ Get started -**1. Install.** Open a terminal in your agent's repository and run one line. It adds Kitaru to that project's environment with `uv` (the worker that replays your agent has to live next to its dependencies), installs the coding-agent skills, and registers the MCP server with Claude Code and Codex. It ends by printing the two ways to get a server: `kitaru login --local` (Docker, free) or the managed cloud (14-day trial, no credit card required). +**1. Install.** Open a terminal in your agent's repository and run one line. It adds Kitaru to that project's environment with `uv` (the worker that replays your agent has to live next to its dependencies), installs the coding-agent skills, and registers the MCP server with Claude Code and Codex. It ends by printing the two ways to get a server: `kitaru login --local` (Docker, free) or `kitaru login` for the managed cloud (14-day trial, no credit card required). ```bash curl -fsSL https://kitaru.ai/install | bash diff --git a/docs/book/getting-started/installation.md b/docs/book/getting-started/installation.md index ee157656a..357a0353d 100644 --- a/docs/book/getting-started/installation.md +++ b/docs/book/getting-started/installation.md @@ -19,11 +19,12 @@ That one command: 4. Prints the two ways to get a server, and stops: ``` -kitaru login --local local, in Docker. Free, open source. -https://cloud.kitaru.ai managed cloud. 14-day trial, no credit card required. - then: kitaru login +uv run kitaru login --local local, in Docker. Free, open source. +uv run kitaru login managed cloud. 14-day trial, no credit card required. ``` +(Inside a project Kitaru is not on your PATH, hence `uv run`. The isolated install uses plain `kitaru`.) + Works on macOS, Linux, WSL, and Git Bash on Windows. Running it again upgrades. {% hint style="info" %} @@ -62,7 +63,7 @@ Set up Kitaru in this repository by following https://kitaru.ai/install.md. Use kitaru doctor ``` -It checks the CLI, the server connection, authentication, and whether the skills are installed. Server connection and authentication fail until you have run `kitaru login --local` (needs [Docker](https://docs.docker.com/get-started/get-docker/)) or logged in to a managed or team server; the sections below cover both. +It checks the CLI, the server connection, authentication, and whether the skills are installed. Server connection and authentication fail until you have run `kitaru login --local` (needs [Docker](https://docs.docker.com/get-started/get-docker/)) or `kitaru login` for the managed cloud; the sections below cover both. Then read the [Quickstart](quickstart.md). It is written as prompts for your coding agent, and everything it needs is now in place. diff --git a/install.sh b/install.sh index d5fa7466b..bd729f16b 100755 --- a/install.sh +++ b/install.sh @@ -20,8 +20,8 @@ # 4. Registers the Kitaru MCP server with Claude Code and Codex if their # CLIs are installed; prints the JSON for everything else. # 5. Stops there and prints the two ways to get a server: local in Docker -# (`kitaru login --local`) or the managed cloud. Login is a decision, so -# the script does not make it for you. +# (`kitaru login --local`) or the managed cloud (`kitaru login`). Login +# is a decision, so the script does not make it for you. # # Nothing here needs sudo. Everything lands under $HOME. Re-running upgrades. # @@ -423,8 +423,10 @@ fi say "" say "${C_GREEN}◆${C_RESET} ${C_BOLD}Kitaru is installed.${C_RESET}" say "" +# In project mode kitaru is not on PATH; every command goes through uv run. +if [ "$KITARU_SCOPE" = "project" ]; then K="uv run kitaru"; else K="kitaru"; fi if [ "$KITARU_SCOPE" = "project" ]; then - say " Installed into this project's environment. Run it as ${C_BOLD}uv run kitaru ...${C_RESET}" + say " Installed into this project's environment, so run it as ${C_BOLD}uv run kitaru ...${C_RESET}" say " (or activate $VENV_DIR)." say "" elif [ "$(PATH="$ORIG_PATH" command -v kitaru 2>/dev/null || true)" != "$KITARU_BIN" ]; then @@ -434,13 +436,12 @@ fi if [ -n "$KITARU_SERVER" ]; then say " Next, log in to your server:" say "" - say " ${C_BOLD}kitaru login $KITARU_SERVER${C_RESET}" + say " ${C_BOLD}$K login $KITARU_SERVER${C_RESET}" else say " Next, pick where your Kitaru server lives:" say "" - say " ${C_BOLD}kitaru login --local${C_RESET} local, in Docker. Free, open source." - say " ${C_BOLD}https://cloud.kitaru.ai${C_RESET} managed cloud. 14-day trial, no credit card required." - say " then: kitaru login " + say " ${C_BOLD}$K login --local${C_RESET} local, in Docker. Free, open source." + say " ${C_BOLD}$K login${C_RESET} managed cloud. 14-day trial, no credit card required." fi say "" say " Then, in your agent's repo, tell your coding agent:" @@ -452,7 +453,7 @@ if [ "$KITARU_SCOPE" = "global" ]; then fi say "" say " No agent yet? ${C_BOLD}Use kitaru-guided-tour to show me Kitaru on the example agent.${C_RESET}" -say " Check setup: kitaru doctor" +say " Check setup: $K doctor" say " Docs: https://docs.zenml.io/kitaru" say "" } From 44ce6a7dde5126b0b75ba152a8fe753c7fd430df Mon Sep 17 00:00:00 2001 From: Alex Strick van Linschoten Date: Thu, 3 Sep 2026 13:16:23 +0200 Subject: [PATCH 8/8] Clarify device login prompt --- docs/book/deploy/authentication.md | 2 +- src/kitaru/cli/auth.py | 2 +- src/kitaru/client/control_plane.py | 7 +++---- src/kitaru/client/device_auth.py | 9 ++++----- tests/cli/test_auth.py | 28 +++++++++++++++++++++++++++- 5 files changed, 36 insertions(+), 12 deletions(-) diff --git a/docs/book/deploy/authentication.md b/docs/book/deploy/authentication.md index f7bf9f604..5c4fa0a1c 100644 --- a/docs/book/deploy/authentication.md +++ b/docs/book/deploy/authentication.md @@ -18,7 +18,7 @@ Two schemes, set by `KITARU_SERVER_AUTH_SCHEME`: kitaru login https://kitaru.internal.example.com ``` -Interactive login uses a device flow (the CLI shows a short `XXXX-XXXX` code and opens your browser) or a password prompt. Credentials are stored separately for each server. Logging in selects that server for later commands; you can override it with `--server` or `KITARU_API_URL`. Non-interactive variants: +Interactive login uses a device flow (the CLI opens your browser to complete the login, or prints a verification link when browser opening is disabled) or a password prompt. Credentials are stored separately for each server. Logging in selects that server for later commands; you can override it with `--server` or `KITARU_API_URL`. Non-interactive variants: ```bash kitaru login https://... --username you --password-stdin diff --git a/src/kitaru/cli/auth.py b/src/kitaru/cli/auth.py index 866c3f8c6..5bf93d09b 100644 --- a/src/kitaru/cli/auth.py +++ b/src/kitaru/cli/auth.py @@ -544,7 +544,7 @@ def _show_device_prompt( uri = authorization.verification_uri_complete or authorization.verification_uri else: uri = authorization.verification_uri_complete - message = f"Open {uri} and confirm code {authorization.user_code}." + message = f"Open {uri} to continue." write_interaction(message) diff --git a/src/kitaru/client/control_plane.py b/src/kitaru/client/control_plane.py index 661d6a605..f27534e01 100644 --- a/src/kitaru/client/control_plane.py +++ b/src/kitaru/client/control_plane.py @@ -198,13 +198,13 @@ async def device_login_with_metadata( ) -> ControlPlaneToken: """Authorize this machine against the control plane. - The call blocks until a signed-in account confirms the user code in a + The call blocks until a signed-in account completes authorization in a browser, or until the authorization expires. Args: open_browser: Whether to open the verification page. prompt: Called with the authorization so the caller can show the - user code. Defaults to logging it. + verification URL. Defaults to logging it. workspace_id: Workspace ID preselected on the verification page. Raises: @@ -235,9 +235,8 @@ async def device_login_with_metadata( prompt(authorization) else: logger.info( - "Open %s and confirm the code %s.", + "Open %s to continue.", verification_uri, - authorization.user_code, ) if open_browser: webbrowser.open(verification_uri) diff --git a/src/kitaru/client/device_auth.py b/src/kitaru/client/device_auth.py index b99c41f5a..4eb3fe7a0 100644 --- a/src/kitaru/client/device_auth.py +++ b/src/kitaru/client/device_auth.py @@ -38,7 +38,7 @@ async def device_login( ) -> ApiToken: """Authorize this machine against a server and store the token it gets. - The call blocks until a signed-in account confirms the user code in a + The call blocks until a signed-in account completes authorization in a browser, or until the authorization expires. Args: @@ -46,8 +46,8 @@ async def device_login( base_url: Server base URL credentials are stored under. store: Credential store the device authorization is written to. open_browser: Whether to open the verification page. - prompt: Called with the authorization so the caller can show the user - code. Defaults to logging it. + prompt: Called with the authorization so the caller can show the + verification URL. Defaults to logging it. Raises: DeviceLoginError: The authorization expired or was refused. @@ -66,9 +66,8 @@ async def device_login( prompt(authorization) else: logger.info( - "Open %s and confirm the code %s.", + "Open %s to continue.", authorization.verification_uri_complete, - authorization.user_code, ) if open_browser: webbrowser.open(authorization.verification_uri_complete) diff --git a/tests/cli/test_auth.py b/tests/cli/test_auth.py index 959db767a..4271028d4 100644 --- a/tests/cli/test_auth.py +++ b/tests/cli/test_auth.py @@ -21,7 +21,7 @@ import pytest -from kitaru.api_models.v1.auth import TokenResponse +from kitaru.api_models.v1.auth import DeviceAuthorizationResponse, TokenResponse from kitaru.api_models.v1.info import AuthScheme, ServerInfoResponse from kitaru.cli import auth from kitaru.cli.output import CLIError @@ -101,6 +101,32 @@ async def close(self) -> None: WORKSPACE_ID = uuid.UUID("11111111-1111-1111-1111-111111111111") +def test_device_prompt_uses_the_complete_verification_url( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Direct people to the verification page without a redundant code prompt.""" + messages: list[str] = [] + monkeypatch.setattr(auth, "write_interaction", messages.append) + + auth._show_device_prompt( + DeviceAuthorizationResponse( + device_id=WORKSPACE_ID, + device_code="device-code", + user_code="ABCD-EFGH", + verification_uri="https://cloud.example.com/devices/verify", + verification_uri_complete=( + "https://cloud.example.com/devices/verify?user_code=ABCD-EFGH" + ), + expires_in=300, + interval=5, + ) + ) + + assert messages == [ + "Open https://cloud.example.com/devices/verify?user_code=ABCD-EFGH to continue." + ] + + def managed_workspace( *, status: str = "available", server_url: str | None = "https://managed.example.com" ) -> ControlPlaneWorkspace: