# Set up Kindgi with your coding agent

> The steps a coding agent follows to take a person from nothing to a first agent that answers, with the person only signing in, copying two values and answering one question.

This page is written for a coding agent (Claude Code, Cursor, Codex, or any other). A person pastes this prompt into their agent:

```text
Set up Kindgi for me: read https://docs.kindgi.com/start/agent.md and follow it step by step. Tell me whenever you need me to do something, and wait for me.
```

If you are that agent: follow this page's steps in order. Do everything you can yourself. Ask the person only for what the steps say needs them, one thing at a time, and wait until they say they've done it.

Read this page whole. If your fetch tool cut it short or summarized it, fetch <https://docs.kindgi.com/start/agent.md> again with a tool that returns the text as it is, such as `curl -s`, and read what it prints rather than saving it to a file: if your tool asks the person before it reads a file outside the folder it works in, a saved copy would need their OK. Every page of these docs has a Markdown version like this one: replace the slash at the end of a page's URL with `.md`.

## Rules for the whole setup

- **Never let a secret into the chat or into your context.** The pull token and the model key go from the person's clipboard straight into a command through a pipe. Never print, log, echo or save them anywhere else, and never ask the person to paste one into the chat.

- **If a pipe from the clipboard doesn't work,** ask the person to run the command themselves, in their own terminal, without `--password-stdin` or `--from-stdin`: it then asks for the value without showing it.

- **When something fails, run doctor** (step 0) and read what it says before you guess.

- **Leave the person's code and settings as they are** unless a page says to change them, and ask before you change anything else. That includes an app's `package.json` name (pass `--pack-id` instead), its `zod` version, and its package manager's settings, such as pnpm's `minimumReleaseAge`.

- **The commands run the CLI as `npx --yes @kindgi/cli@0.1` until step 2 makes a project,** if `node --version` works. For someone using Python without Node, use `uvx --from "kindgi-cli>=0.1,<0.2" kindgi` in its place (the CLI from PyPI; it needs no Node). From step 2 on, they write `pnpm exec kindgi`, run from the project's folder. Use the project's form in its place:

  - a TypeScript project: `pnpm exec kindgi` if `pnpm --version` works, else `npx --no -- kindgi`. `--no` keeps npx from downloading anything, so it runs the project's own CLI. The `--` passes every flag, `--help` included, to Kindgi rather than to npx;
  - a Python project: `uv run kindgi` (the project's dev dependencies bring the CLI);
  - a Java or Scala project: `./kindgiw` (the CLI version the project pins in `kindgi.config.json`; `kindgiw.cmd` on Windows).

## Step 0: check the machine

Work out this machine's OS yourself, without asking the person: `uname -s` prints `Darwin` on macOS and `Linux` on Linux, WSL included. On Windows it fails, or prints a name that starts with `MINGW` or `CYGWIN`. Then pick its clipboard command:

| OS              | Read the clipboard                | Clear it                                 |
| --------------- | --------------------------------- | ---------------------------------------- |
| macOS           | `pbpaste`                         | `pbcopy < /dev/null`                     |
| Linux (Wayland) | `wl-paste --no-newline`           | `wl-copy --clear`                        |
| Linux (X11)     | `xclip -o -selection clipboard`   | `xclip -selection clipboard < /dev/null` |
| Windows         | use the fallback (next paragraph) |                                          |

Before you pipe the clipboard, check it isn't empty, without printing it: `pbpaste | grep -q .` (with this OS's command). If the clipboard command fails (on a server, over SSH or in a container there's no clipboard) or the clipboard is empty, use the fallback: the person runs the command in their own terminal, where it asks for the value without showing it. On Windows, always use the fallback.

Then run doctor, as a command of its own:

```sh
npx --yes @kindgi/cli@0.1 doctor --json
```

Run it like that here and in every later step: nothing before or after it, such as `cd … &&`, `;`, a pipe, `sleep` or `echo $?`. To run it in another folder, `cd` there first, as a command of its own. If your tool asks the person before it runs a command, a plain command is one they can allow for good, and the next run of the same command needn't ask. It prints one JSON object, whose `ok` is `false` when a check fails; it also exits `1` then (`0` when nothing fails, `2` on a usage error). Here, in a project with `kindgi dev` running and no model key yet:

```json excerpt
{
  "ok": false,
  "cliVersion": "0.1.8",
  "project": {
    "dir": "…/my-agents",
    "language": "node"
  },
  "consoleUrl": "http://127.0.0.1:…/console/",
  "checks": [
    {
      "id": "node",
      "status": "pass",
      "message": "Node 22.21.1."
    },
    …,
    {
      "id": "model-key",
      "status": "fail",
      "message": "No model key in .env or .env.local or .kindgi/secrets.env (looked for ANTHROPIC_API_KEY, AZURE_OPENAI_API_KEY, GEMINI_API_KEY, GROQ_API_KEY, OPENAI_API_KEY, OPENROUTER_API_KEY).",
      "fix": "With kindgi dev running, set one LLM provider's key: pnpm exec kindgi secrets set ANTHROPIC_API_KEY --env=local --scope=tenant, or the same with AZURE_OPENAI_API_KEY, GEMINI_API_KEY, GROQ_API_KEY, OPENAI_API_KEY or OPENROUTER_API_KEY (it prompts without echoing; or pipe it in with --from-stdin). Never paste a key into a chat."
    },
    {
      "id": "runtime",
      "status": "pass",
      "message": "The runtime answers at http://127.0.0.1:…; its console is at http://127.0.0.1:…/console/."
    },
    {
      "id": "provider",
      "status": "fail",
      "message": "Only dev-echo is registered: agents get its canned replies, not a model's.",
      "fix": "Register the provider whose key you set: pnpm exec kindgi providers register --preset=<preset>, where <preset> is anthropic, azure-openai (with --resource-name=<resourceName> --deployments=<deployments>), gemini-api, groq, openai or openrouter (see Model key)."
    },
    {
      "id": "erasures",
      "status": "skip",
      "message": "Erasures run, but a replay after a backup restore can't find whom they erased: the runtime has no KINDGI_ERASURE_LEDGER_KEY. Fine for development; set it where you run in production."
    }
  ]
}
```

The checks come in this order: `node`, `npm`, `python`, `uv`, `java`, `maven`, `sbt`, `docker`, `registry`, `dev-sandbox`, `console-sign-in`, `license`, `mcp-headers`, `project`, `dependencies`, `model-key`, `runtime`, `provider`, `erasures`.

- **`fail`:** run its `fix`, as written: it names the CLI to use in that folder. When the fix needs the person (start Docker Desktop, sign in, copy a key), ask them, then run doctor again.
- **`warn`:** it works now, but the person should know: tell them its `message` and `fix`, and go on. `ok` stays `true` and the exit code `0`. In this release six checks warn. `dev-sandbox`: when `kindgi dev` can't run the pack's code sandboxed on this machine (Linux without bubblewrap, a container, Windows, or inside another sandbox); the code would then run with the person's own access. `console-sign-in`: when nobody can sign in to the console of the runtime the CLI points at. `license`: when the license key here expires soon, or has expired but the runtime still starts with it, or is signed with a key this CLI doesn't know yet. `mcp-headers`: when an MCP endpoint on that runtime keeps a credential in plaintext `config.headers`, from before that was refused; it still works. `model-key`: when the key is in a file the app loads too, where the app's routes can read it; the fix gives Kindgi its own copy. `provider`: when an agent that names no model would get a model the preset no longer lists, or one other than the preset's default; or when the runtime can't build a registered provider (each problem is in the check's `details`). When it can build none, `provider` fails instead.
- **`skip`:** not applicable yet. Outside a project, `project` and every check after it skip; `runtime` skips while `kindgi dev` isn't running; `dev-sandbox` skips when `KINDGI_DEV_SANDBOX=off`. `license` skips when there's no license key here: `kindgi dev` needs none. `erasures` skips with a note when the runtime has no erasure ledger key, as under `kindgi dev`: fine for development.

Fix every `fail` up to and including `docker` before you go on. `registry` is step 1.

## Step 1: access to the runtime image

If doctor's `registry` check passes, this machine can already pull the runtime: skip this step.

1. Ask the person: "Sign in at [https://access.kindgi.com](https://access.kindgi.com/) with GitHub. Under **1. Pull the runtime image**, copy the token, and tell me when you're done. Also tell me the robot name in its command, after `--username` (it isn't secret)." If the page doesn't give them a token, stop here until they have one.

2. When they say they're done, pipe the clipboard into the login, with the robot name they gave you:

   ```sh
   <read the clipboard> | npx --yes @kindgi/cli@0.1 auth registry --username <robot name> --password-stdin
   ```

   It ends with:

   ```text excerpt
   ✓ You can pull quay.io/kindgi/runtime:…@sha256:…, the image this CLI runs.
   ```

3. Clear the clipboard, if this OS lets you.

4. Run doctor again; `registry` should pass.

## Step 2: the project

**If the folder you started in is an app already** (it has a `package.json`, `pyproject.toml`, `pom.xml` or `build.sbt`), ask the person: "Shall I add Kindgi to this app, or make a separate project?" To add it, read the page for the app's language: [TypeScript or Node](https://docs.kindgi.com/start/existing-app-typescript.md), [Python](https://docs.kindgi.com/start/existing-app-python.md), [Java](https://docs.kindgi.com/start/existing-app-java.md) or [Scala](https://docs.kindgi.com/start/existing-app-scala.md). Run its `kindgi init` and install steps, then go on to step 3 here, from the app's folder. At step 5, make the first tool from the app's own code and an agent that uses it, as that page shows. Otherwise:

1. Ask the person for a name for their project, or use `my-agents`. Use TypeScript if doctor's `node` check passes and the person has no preference; Python if they ask for it, or if there's no Node (doctor's `python` and `uv` say whether it's ready); Java if they ask for it (doctor's `java` says whether there's a JDK 17 or later; Maven comes with the project); Scala if they ask for it (a JDK 17 or later too, and doctor's `sbt` says whether sbt is installed).

2. Create it and install its dependencies:

   ```sh
   # TypeScript
   npx --yes @kindgi/cli@0.1 init my-agents
   cd my-agents
   pnpm install     # if pnpm --version works; else: npm install
   ```

   ```sh excerpt
   # Python (no Node needed)
   uvx --from "kindgi-cli>=0.1,<0.2" kindgi init my-agents --template=python
   cd my-agents
   uv sync          # brings the CLI too: from now on, uv run kindgi …
   ```

   ```sh excerpt
   # Java (preview)
   npx --yes @kindgi/cli@0.1 init my-agents --template=java   # from now on: ./kindgiw …
   cd my-agents
   ./mvnw -q test
   ```

   ```sh excerpt
   # Scala (preview)
   npx --yes @kindgi/cli@0.1 init my-agents --template=scala   # from now on: ./kindgiw …
   cd my-agents
   sbt -batch test
   ```

   `--template` picks the project's language: `python`, `java` or `scala`. Without it, `init` makes a TypeScript project.

   `init` prints what it made, then the next steps. For TypeScript, with `pnpm install` after it:

   ```text excerpt
   …
   ✓ Pack scaffolded at …/my-agents/
   ✓ package.json pins pnpm@10.28.0 (packageManager)
   …
   Done in 13.3s using pnpm v10.28.0
   ```

   For Python it's `✓ Python pack scaffolded at …/my-agents/`, and `uv sync` ends with `Installed 26 packages in 457ms`. For Java, `✓ Java pack scaffolded at …/my-agents/ (preview)`, and `./mvnw -q test` prints nothing when the tests pass. For Scala, `✓ Scala pack scaffolded at …/my-agents/ (preview)`, and sbt ends with `[info] Passed: Total 3, Failed 0, Errors 0, Passed 3`.

3. Run doctor again from the project's folder; `project` and `dependencies` should pass.

`init` also gives you Kindgi's skills, in `.claude/skills/` and `.agents/skills/` (the same copy in each), for the project's language (a Java or Scala project gets its own). Read them: they are how you write tools and agents for this project.

## Step 3: start Kindgi

Start `kindgi dev` from the project's folder so that it keeps running after your turn ends. As with doctor, `cd` there first, in a command of its own; then:

- **If your tool runs commands in the background itself** (Claude Code's background tasks do), start it that way: `kindgi dev` (the project's form of the CLI) on its own, as a background task, with no `&`, `nohup` or redirect. The task is in the background already, and your tool keeps its output. Your tool can stop it.

- **Otherwise,** start it detached, with its output in `.kindgi/dev.log` (`.kindgi/` is git-ignored), and note its process id. Run `mkdir -p .kindgi` first, on its own, so the log's folder exists. Then:

  ```sh
  nohup pnpm exec kindgi dev > .kindgi/dev.log 2>&1 &
  echo $!
  ```

  `echo $!` prints the process id, a number such as `76907`.

  Run the `mkdir` before it, not as `mkdir … && nohup … &`: then `$!` is a subshell's id, and stopping it leaves `kindgi dev` running.

Use the project's form of the CLI in place of `pnpm exec kindgi`. It starts the runtime in Docker and answers with a stand-in model, `dev-echo`, until step 4 adds a real one.

Run doctor again until `runtime` passes, a few seconds apart: each run on its own, as in step 0, not in a loop or after `sleep … &&`. `model-key` and `provider` still fail: that's expected until step 4. Then tell the person it's running, and how to stop it: `kill <the process id>`, or, if your tool started it, how your tool stops it. If your tool's background tasks end with your session, tell them `kindgi dev` stops then too. Also tell them where the console is: the `Console` line of `kindgi dev`'s output (in `.kindgi/dev.log` if you started it detached), its first address (`kindgi doctor --json` has it as `consoleUrl`, and `kindgi console` opens it in their browser). They sign in there with **Sign in as seeded user**, which needs no token: don't print the token in the chat.

## Step 4: the model key

1. Ask the person: "Which model provider do you have an API key for: Anthropic, OpenAI, Gemini, Groq or OpenRouter? Copy that key, tell me the provider, and tell me when you're done." Each provider has its key's name and a preset:

   | Provider                        | Key                  | Preset       |
   | ------------------------------- | -------------------- | ------------ |
   | Anthropic                       | `ANTHROPIC_API_KEY`  | `anthropic`  |
   | OpenAI                          | `OPENAI_API_KEY`     | `openai`     |
   | Gemini (a Google AI Studio key) | `GEMINI_API_KEY`     | `gemini-api` |
   | Groq                            | `GROQ_API_KEY`       | `groq`       |
   | OpenRouter (a hosted gateway)   | `OPENROUTER_API_KEY` | `openrouter` |

   **If they have no key, or don't want to add one now,** stop here, honestly: Kindgi is running with its stand-in model, `dev-echo`, which only repeats what it gets, and says so: its answers start with `⚠ dev-echo isn't a real model: it only repeats what it's given. Add an LLM provider key (Anthropic, OpenAI, Gemini, Groq, OpenRouter…) to get real answers.` Adding a key (this step) gives a real answer. Don't write a tool that passes text through, or anything else that makes `dev-echo`'s reply look like an agent's answer.

2. When they say they're done, pipe the clipboard into the project's secrets under that provider's key, then register its preset. For OpenAI:

   ```sh
   <read the clipboard> | pnpm exec kindgi secrets set OPENAI_API_KEY --env=local --scope=tenant --from-stdin
   pnpm exec kindgi providers register --preset=openai
   ```

   - `secrets set` writes the key to Kindgi's own `.kindgi/secrets.env`, which git ignores and the app doesn't load. It needs `kindgi dev` running.
   - `--from-stdin` reads the key from the pipe, so it's never shown.
   - `--env=local` is the environment the secret belongs to. Under `kindgi dev`, `local` is the pack's env files, `.kindgi/secrets.env` among them.
   - `--scope=tenant` is where the secret lives: the tenant, rather than one org or project.
   - `--preset=openai` registers OpenAI's models from Kindgi's built-in preset, which reads the key from that provider's secret in the table, `OPENAI_API_KEY` here. `kindgi providers presets` lists the presets and their models.

   The two commands print:

   ```text excerpt
   …
     Set OPENAI_API_KEY at tenant in local.
   …
   ✓ Registered openai: gpt-6.1-sol (default), gpt-6-astra, gpt-6-luna — key OPENAI_API_KEY (env local)
   ```

3. Clear the clipboard, if this OS lets you.

4. Run doctor again; `model-key` and `provider` should pass.

Doctor's `model-key` only checks that a key is saved: a wrong key shows as a `401` on the first run. Then don't open, measure or print `.kindgi/secrets.env` or the app's env files. Ask the person to copy the key again (they can check it in their provider's console), and run the `secrets set` command again with `--write-mode=add-version`. Without it, a key that's already stored is refused (`Version conflict: OPENAI_API_KEY already exists`). The new key is used from the next run: there's no need to register the provider again or to restart `kindgi dev`.

## Step 5: the first agent

1. Ask the person: "What's one thing you'd like an agent to do?" If they don't know, suggest: "Read a customer message and say how urgent it is, and why."

2. Write that agent in the project, following the skills in `.claude/skills/` or `.agents/skills/`. `kindgi dev` picks up the file when you save it.

3. Run it with an example the person would recognise, and show them the answer:

   ```sh excerpt
   pnpm exec kindgi runs start --agent=<pack id>.<agent name> --input='{"userMessage":"…"}'
   ```

   `--agent` names the agent by its id, and `--input` is its input as JSON: the person's message goes in `userMessage`. The command waits for the run and prints it. The answer is in its `output`.

   ```text excerpt
   …
     "status": "completed",
   …
       "output": {
   …
   ```

## Step 6: wrap up

Tell the person, in your own words:

> **Your first agent answered.** It runs on your machine, with your model key in `.kindgi/secrets.env`. Kindgi keeps running in the background until you stop it (with `kill <the process id>`, or the way I told you), or, if my tool started it, until this session ends. Three things to try next:
>
> 1. **Give it a tool:** ask me "add a tool that looks up an order, and let the agent call it".
> 2. **Judge a few answers:** say yes or no to what it answered, so later versions can be checked against your judgments ([Judge a run's output](https://docs.kindgi.com/guides/evals/judge-a-runs-output/)).
> 3. **Compare two versions:** change its instructions and see whether people's judgments rate the new version higher ([Compare a version on a test set](https://docs.kindgi.com/guides/evals/compare-an-agent-version/)).

If the person then asks for an app that starts the agent's runs, read [Start runs from your app](https://docs.kindgi.com/start/call-from-your-app.md) before you write it: it says how the client finds Kindgi's URL and token, and that `runs.start` waits for the answer.
