Start: install, quickstarts, existing apps
# Start
> What Kindgi is, installing it, and your first pack.
Begin here. 1. [What is Kindgi?](introduction/): packs, the runtime, and how your app and your coding agent work with them. 2. [Install](install/): Node, Docker, and how a project gets the CLI and the SDK. 3. A first pack, running on your machine in ten minutes: [TypeScript](quickstart-typescript/), [Python](quickstart-python/), [Java](quickstart-java/) or [Scala](quickstart-scala/) (preview). 4. [Add Kindgi to an existing app](existing-app/): your app's own code as tools. From a Java app, [call Kindgi with the Java client](java-app/) (preview). 5. [Your coding agent](coding-agents/): the skills that teach it Kindgi. 6. Or let your coding agent set it all up: paste this into it, and do what it asks ([what it follows](agent/)):
```text
Set up Kindgi for me: read https://docs.kindgi.com/start/agent/ and follow it step by step. Tell me whenever you need me to do something, and wait for me.
```
# 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/ 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 the steps below 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. If your fetch tool cut this page short, read it from : the Start section as plain text, with this page under "Set up Kindgi with your coding agent". ## 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** (below) and read what it says before you guess. * **`kindgi` in the commands below** means, until step 2 makes a project: `npx --yes @kindgi/cli@0.1` if `node --version` works; else, for someone using Python without Node, `uvx --from "kindgi-cli>=0.1,<0.2" kindgi` (the CLI from PyPI; it needs no Node). From step 2 on, from the project's folder: * a TypeScript project: `pnpm exec kindgi` if `pnpm --version` works, else `npx --no kindgi`; * 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 the clipboard command for this OS: | 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 below | | Before you pipe the clipboard, check it isn't empty, without printing it: `[ -n "$(pbpaste)" ]` (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:
```sh
kindgi doctor --json
```
It prints one JSON object and exits `0` when nothing fails, `1` when a check fails (and `2` on a usage error). Here, in a project with `kindgi dev` running and no model key yet:
```json
{
"ok": false,
"cliVersion": "0.1.6",
"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, 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 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=, where is anthropic, 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`, `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 three 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. `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. `console-sign-in`: when nobody can sign in to the console of the runtime the CLI points at. * **`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`. `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 for the runtime image's pull credentials, a robot name and a pull token: "Copy your Kindgi pull token, and tell me when you're done. Also tell me your robot name (it isn't secret)." The image is in private preview: someone without credentials requests access at , and you stop here until they have them. 2. When they say they're done, pipe the clipboard into the login, with the robot name they gave you:
```sh
| kindgi auth registry --username --password-stdin
```
3. Clear the clipboard, if this OS lets you. 4. Run doctor again; `registry` should pass. ## Step 2: the project 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
# 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
# Java (preview)
npx --yes @kindgi/cli@0.1 init my-agents --template=java # from now on: ./kindgiw …
cd my-agents
./mvnw -q test
```
```sh
# Scala (preview)
npx --yes @kindgi/cli@0.1 init my-agents --template=scala # from now on: ./kindgiw …
cd my-agents
sbt -batch test
```
3. Run doctor again from the project's folder; `project` and `dependencies` should pass. `init` also gives you Kindgi's skills, in `.claude/skills/`, 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: detached, with its output in a log, and note its process id:
```sh
mkdir -p .kindgi
nohup kindgi dev > .kindgi/dev.log 2>&1 &
echo $!
```
Use `kindgi` as the project runs it (`.kindgi/` is git-ignored). Keep `mkdir` on its own line: with `mkdir … && nohup … &`, `$!` is a subshell's id, and stopping it leaves `kindgi dev` running. It starts the runtime in Docker and answers with a stand-in model, `dev-echo`, until step 4 adds a real one. Run doctor every few seconds until `runtime` passes. `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 `. Also tell them where the console is: the `Console` line of `.kindgi/dev.log`, 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. Tell them to use Chrome or Firefox: Safari can't keep the local sign-in over http yet ([Known limitations](../../deploy/operate/#known-limitations-in-015)). ## 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
| kindgi secrets set OPENAI_API_KEY --env=local --scope=tenant --from-stdin
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. `kindgi providers presets` lists the presets and their models. 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/`. `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
kindgi runs start --agent=. --input='{"userMessage":"…"}'
```
It waits for the run and prints it; the answer is in its `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 `. 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/)).
# Your coding agent
> How your coding agent knows Kindgi, the commands it runs, and what stays with you.
You don't have to learn Kindgi before you build with it. Tell your coding agent what you want, in your own words: * "Add a tool that looks up a customer by email." * "Write an agent that answers support tickets with a category, a priority and a reply." * "Connect Claude." * "Branch the flow on the priority." * "Why did the last run fail?" The agent writes the code in your project's TypeScript or Python, runs it with the Kindgi CLI, reads what happened, and fixes it. This page explains how it knows to. ## How it knows Kindgi ### Skills, loaded for the task `kindgi init` copies Kindgi's **skills** into the project, under `.claude/skills/`, for a TypeScript, Python, Java or Scala project. A skill is a page of instructions written for an agent: the steps, the commands to run, what to check, and the mistakes to avoid. Each skill opens with a short description of when it applies. Claude Code reads those descriptions and loads a skill's full text only when your request matches: asking for a tool loads the tools skill, asking for a model loads the providers skill. A request that spans several (a tool, an agent that uses it, a flow around both) loads each one in turn. | Skill | Loaded when | | -------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- | | `kindgi-getting-started`, `kindgi-python-getting-started`, `kindgi-java-getting-started`, `kindgi-scala-getting-started` | Setting up or orienting in a pack | | `kindgi-authoring-tools`, `kindgi-python-authoring-tools`, `kindgi-java-authoring-tools`, `kindgi-scala-authoring-tools` | Writing a tool: typed input and output, side effects, secrets, HTTP tools | | `kindgi-authoring-agents`, `kindgi-python-authoring-agents`, `kindgi-java-authoring-agents`, `kindgi-scala-authoring-agents` | Writing an agent: instructions, tools, typed output, budgets | | `kindgi-authoring-flows`, `kindgi-python-authoring-flows`, `kindgi-java-authoring-flows`, `kindgi-scala-authoring-flows` | Writing a flow: steps, edges, conditions, loops, fanout | | `kindgi-authoring-guardrails`, `kindgi-python-authoring-guardrails`, `kindgi-java-authoring-guardrails`, `kindgi-scala-authoring-guardrails` | Writing a guardrail check | | `kindgi-authoring-providers` | Connecting a model: Anthropic, Gemini, OpenAI-compatible endpoints | | `kindgi-authoring-mcp-servers` | Giving the coding agent an MCP server | | `kindgi-framework-feedback` | Reporting a problem in Kindgi itself | A pack gets the skills for its language, plus the shared ones. ### Matched to your version The skills ship with the CLI, so `kindgi init` copies the ones written for the Kindgi version it installs. The agent writes against the API you have, not one it half-remembers. After an upgrade, `kindgi dev` tells you the skills are behind, and `kindgi skills sync` refreshes them (see [Keeping them current](#keeping-them-current)). ### What else it reads * **The SDK documents itself.** Every TypeScript export has JSDoc and every Python one a docstring, so the agent finds field-level docs right where it writes the code. * **The CLI explains itself.** `kindgi --help`, and `--help` on each command, list the commands and their flags. * **Errors name what's wrong.** A flow step the runtime has no tool for, an answer that doesn't match the agent's output schema, a package that's only in `devDependencies`: the message says which, and the agent fixes it and runs again. * **These docs, for agents.** [`docs.kindgi.com/llms.txt`](https://docs.kindgi.com/llms.txt) indexes this site in plain text, one file per section; [`llms-full.txt`](https://docs.kindgi.com/llms-full.txt) is all of it. Point an agent there when it needs more than the skills. ## What it runs The skills tell the agent which `kindgi` commands to run and when, so it works the way you would: write, run, look, fix. | Command | What the agent does with it | | --------------------------------- | ------------------------------------------------------------ | | `kindgi init` | Creates a pack, or adds Kindgi to your app | | `kindgi dev` | Starts Kindgi on your machine, which picks up every save | | `kindgi runs start` | Tries an agent or a flow with an input, and reads the answer | | `kindgi runs get`, `runs journal` | Finds out what a run did, step by step, and why it failed | | `kindgi providers register` | Connects a model, once its key is in your env file | | `kindgi mcp add` | Gives itself access to a database or another service | | `kindgi skills sync` | Refreshes its skills after an upgrade | | `kindgi feedback write` | Records a bug it found in Kindgi itself | | `kindgi build` | Builds the pack's image for deployment | It runs the CLI the project pins: `pnpm exec kindgi` in a TypeScript project, `uv run kindgi` in a Python one. ## What stays with you * **The names.** You name your pack and its agents. The pack's id prefixes every tool, agent and flow (`.`), so pick it once; the skills tell the agent to ask you for it rather than guess. * **The credentials.** You provide them, such as your model provider's API key: type it into `kindgi secrets set`, which asks for it without showing it and keeps it in Kindgi's own `.kindgi/secrets.env`. The agent never invents one or writes one into code. Then it registers the provider. * **What the code can reach.** `kindgi dev` runs the code the agent writes sandboxed: it can't read your keys, your other projects or the Docker socket, though it keeps the network and the app's own files ([Keep your tools' code away from your keys](../../guides/secrets/dev-sandbox/)). Logging in to the runtime image's registry is yours too: `kindgi auth registry` asks for your token the same way ([Install](../install/)). ## Keep it out of your keys `kindgi init` adds deny rules to your project's `.claude/settings.json`, so Claude Code's file tools never read the files that hold keys and tokens:
```json
{
"permissions": {
"deny": [
"Read(./.env*)",
"Read(./.kindgi/secrets.env)",
"Read(./.kindgi/dev/runtime.env)",
"Read(./kindgi.env)",
"Read(./pack.env)"
]
}
}
```
It merges them into a settings file you already have, and leaves one it can't read as JSON alone, saying what to add. A project with a `.cursorignore`, `.geminiignore` or `.aiderignore` gets the same files added to it. **In a monorepo,** mind where the agent starts. Claude Code reads `.claude/settings.json` from the folder a session starts in, and a `./` rule is relative to that folder. So when your pack sits below its repository's root, `kindgi init` also adds the rules, under the pack's path, to the root's `.claude/settings.json` (creating it if there's none), and says so:
```text
✓ /…/acme/.claude/settings.json: created with 5 deny rules for apps/agent/, so an agent started at the repo root can't read this pack's keys
```
The root's file then holds `"Read(./apps/agent/.env*)"`, `"Read(./apps/agent/.kindgi/secrets.env)"` and so on, merged the same way, and a `.cursorignore`, `.geminiignore` or `.aiderignore` at the root gets the same paths. A repository rooted at your home folder (your dotfiles, say) is left alone: its `.claude/settings.json` is Claude Code's settings for every project, so `init` only says what you could add there. An agent started in the pack's folder uses the pack's own file. One started in another folder of the repository loads neither. The pack's files are outside its folder, so Claude Code asks you before it reads them, unless reads are already allowed (in your settings, or with `--allowedTools`): then it reads them. Start the agent in the pack's folder or at the repository's root. A bare `./.env*` matches at any depth below the session's folder; a rule with a folder in it, such as `./.kindgi/secrets.env`, matches only there. The rules stop the agent's file tools, and a plain `cat .env.local` too. A program its shell runs (`node -e …`, a script) can still read the files. To close that, turn on Claude Code's [sandbox](https://code.claude.com/docs/en/sandboxing) with the same files. Tested with Claude Code 2.1.288:
```json
{
"permissions": {
"deny": [
"Read(./.env*)",
"Read(./.kindgi/secrets.env)",
"Read(./.kindgi/dev/runtime.env)",
"Read(./kindgi.env)",
"Read(./pack.env)"
]
},
"sandbox": {
"enabled": true,
"failIfUnavailable": true,
"allowUnsandboxedCommands": false,
"filesystem": {
"denyRead": [
"./.env*",
"./.kindgi/secrets.env",
"./.kindgi/dev/runtime.env",
"./kindgi.env",
"./pack.env"
],
"allowWrite": ["~/.npm"]
},
"network": { "allowedDomains": ["registry.npmjs.org"], "allowLocalBinding": true }
}
}
```
* **`allowLocalBinding`** lets the agent's `kindgi` commands reach `kindgi dev`, and its `curl` reach your app, on `localhost`. Without it, every `kindgi runs start` fails. * **The npm registry and `~/.npm`** let it install packages. * **`.kindgirc.json` isn't on either list:** it holds the dev token the agent's own `kindgi` commands read to find `kindgi dev`. With the sandbox on, a `Read` deny rule binds the agent's shell commands too, so denying it would break them. * **In a monorepo,** put the sandbox in the root's settings, with the same paths under the pack's folder in both lists (`"./apps/agent/.env*"`, …). * **Some things then run in a terminal of your own:** `kindgi dev`, your app's dev server, and anything that needs Docker, such as `kindgi build`. `kindgi doctor` works, and says which checks it skipped because it couldn't read a file. These settings keep the agent's own tools and commands out of your keys. The code it writes still runs in your app and in `kindgi dev`, with your access. Under `kindgi dev`, your pack's tools never get a secret stored with `kindgi secrets set`, nor a model provider's key, in their environment. Your app's routes do see what its own env files hold. Read what the agent writes. ## Other coding agents Claude Code loads the skills by itself. They're plain Markdown, so any coding agent can follow them once it knows where they are: * A new pack has an `AGENTS.md` that points to them, for agents that read `AGENTS.md`. * In an existing app, `kindgi init` leaves your `AGENTS.md` (or rules file) alone. Add a line to it, for example: "Kindgi's instructions are in `.claude/skills/`. Before writing Kindgi code, read the skill for the task." ## Keeping them current The skills are copies, made when you ran `kindgi init`. After upgrading Kindgi, refresh them:
```sh
kindgi skills sync # keeps skills you edited; --force replaces them
```
In a Python project, run it as `uv run kindgi skills sync`. `kindgi dev` tells you when the installed skills are behind the CLI's. `.claude/skills/.kindgi-manifest.json` records what was installed. ## MCP servers for your coding agent Give the coding agent access to a database or another service through an MCP server, without writing its secret into the project:
```sh
kindgi mcp presets
kindgi mcp add postgres --secret=MY_DB_URL
```
`add` writes an entry to `.mcp.json` that starts the server with the secret from your env files. These servers are for your coding agent (Claude Code, Cursor, VS Code), not for Kindgi's runtime. ## When Kindgi is the problem If your agent diagnoses a bug in Kindgi rather than in your code, the `kindgi-framework-feedback` skill has it write a structured note to `FEEDBACK.md` with `kindgi feedback write`, ready to send to us.
# Add Kindgi to an existing app
> Kindgi inside the app you already have (a Next.js or Node app, or a Python app), so your tools are your app's own code.
Kindgi doesn't need a project of its own. Run `kindgi init` in your app and the pack lives beside your code: its tools import your app's modules, and your app starts runs through the SDK. ## A TypeScript or Node app In the app's root (where its `package.json` is), with no pack name:
```sh
npx @kindgi/cli init
pnpm install # or the app's own package manager
```
`init` adds: * the pack's config: its id (from the app's name), version, and where its primitives live. It's `kindgi.config.ts` in an app whose `package.json` says `"type": "module"`, and `kindgi.config.mts` in any other; * a `kindgi/` folder for the primitives: `kindgi/tools/`, `kindgi/agents/`, `kindgi/guardrails/`, `kindgi/flows/`. In an app that isn't `"type": "module"`, it also gets a one-line `package.json` that makes them ES modules; * `@kindgi/sdk` (a dependency) and `@kindgi/cli` (a devDependency) in your `package.json`, at the CLI's own version, and, from 0.1.1, `zod`; * from 0.1.1, in a pnpm app, `allowBuilds: { esbuild: false }` in `pnpm-workspace.yaml`: pnpm 11+ won't install until every dependency's install script has a decision, and esbuild (the CLI's bundler) doesn't need its script. A decision the app already has, `true` or `false`, is kept; * from 0.1.4, in an npm app, the same decision in `package.json`'s `allowScripts` (`"esbuild": false`): without it, npm 11 warns on the first install that esbuild's script is "not yet covered by allowScripts". npm 10 ignores the field. A decision the app already has, for `esbuild` or a pinned `esbuild@`, is kept; * the skills for your coding agent under `.claude/skills/`, and `.gitignore` entries (`.kindgi/`, `.kindgirc.json`, `.env.local`). It creates no env files: `kindgi dev` reads your app's own `.env` and `.env.local`, and keeps the secrets you store in its own `.kindgi/secrets.env`, which your app doesn't load. If your app lints with ESLint, leave out what Kindgi builds: add `".kindgi/**"` to the `globalIgnores` in `eslint.config.mjs`. To keep the pack separate from the app instead, pass `--new-repo`. ### Your code as a tool A tool imports your app's modules like any other file in it, path aliases (`@/…`) included: kindgi/tools/get-request/index.ts
```ts
import { defineTool } from '@kindgi/sdk/define';
import type { ToolId } from '@kindgi/sdk/types';
import { z } from 'zod';
import { findRequest } from '@/lib/requests'; // your app's own code
const defined = defineTool({
id: 'acme-support.get-request' as ToolId,
description: 'Looks up a support request by its id (REQ-1234).',
version: '0.1.0',
input: z.object({ id: z.string() }),
output: z.object({
found: z.boolean(),
subject: z.string().optional(),
body: z.string().optional(),
}),
effects: [],
mutating: false,
handler: async ({ id }) => {
const request = findRequest(id);
return request
? { found: true, subject: request.subject, body: request.body }
: { found: false };
},
});
if (defined.kind === 'err') throw new Error(defined.error.message);
export default defined.value;
```
Ids start with the pack's id (`acme-support`, from the app's name). `mutating: false` says the tool only reads, so a dry run may call it. A package a tool imports (an ORM client such as `@prisma/client`, an API SDK) must be in your app's `dependencies`, not `devDependencies`: the deployed pack installs production dependencies only, so a dev-only import works under `kindgi dev` and fails once deployed. `kindgi dev` warns when a tool imports one, and `kindgi build` refuses the pack until it moves. Build-time tools (the `prisma` CLI, `typescript`) stay in `devDependencies`. ### An agent that uses it kindgi/agents/triage/index.ts
```ts
import { defineAgent } from '@kindgi/sdk/define';
import type { AgentId, Semver } from '@kindgi/sdk/types';
import { z } from 'zod';
const defined = defineAgent({
id: 'acme-support.triage' as AgentId,
version: '0.1.0' as Semver,
name: 'Triage',
description: 'Reads a support request and sets its priority.',
instructions:
'The user names a support request id. Look it up with the get-request tool, ' +
'then answer with its priority and a one-sentence summary.',
capabilities: [{ needs: [{ feature: 'tool-use' as const }] }],
tools: [{ id: 'acme-support.get-request', version: '^0.1.0' }],
retrieval: [],
guardrails: [],
output: {
schema: z.object({
priority: z.enum(['low', 'normal', 'urgent']),
summary: z.string(),
}),
},
budget: { maxSteps: 4, maxCostUsd: 0.05, maxWallMs: 60_000 },
});
if (defined.kind === 'err') throw new Error(defined.error.message);
export default defined.value;
```
`output` makes the answer typed: the model has to answer with JSON that fits the schema, and your app gets it as an object. ### Run it
```sh
pnpm exec kindgi dev
```
`kindgi dev` builds the pack from `kindgi/`, runs its tools in a process started from your app's root, and reloads on every save. Until you register a model, agents answer with `dev-echo`, which only checks the wiring: it calls the agent's first tool with `{"message": …}` and can't produce a typed answer, so this agent fails with `output-schema-violation`. Register a model first:
```sh
pnpm exec kindgi secrets set ANTHROPIC_API_KEY --env=local --scope=tenant # prompts for the key; it isn't echoed
pnpm exec kindgi providers register --preset=anthropic
pnpm exec kindgi runs start --agent=acme-support.triage --input='{"userMessage":"Triage REQ-1002"}'
```
```text
"status": "completed",
…
"output": {
"summary": "User unable to log in due to expired password with non-functional password reset email delivery.",
"priority": "urgent"
},
```
To have `kindgi dev` register the model on every boot, in each worktree and after `--reset`, declare it in the pack's config: [Declare them in your pack's config](../../guides/models/#declare-them-in-your-packs-config). ### What the pack's image needs `kindgi build` turns the pack into an image. It installs your app's production dependencies with install scripts off, so your app's own `postinstall` and `prepare` scripts don't run there, and the build says so:
```text
✓ The app's own install scripts don't run in the image: postinstall (`prisma generate`)
```
When the image needs what such a script does, say so under `image` in `kindgi.config.ts`. **Prisma.** A tool that uses Prisma's client needs `prisma generate` in the image. Without it the build fails when it loads the pack:
```text
kindgi-index: file-import-failed: Failed to import kindgi/tools/find-order/index.ts: Error: @prisma/client did not initialize yet. Please run "prisma generate" and try to import it again.
```
`prisma()` copies the schema into the image and runs `prisma generate` after the install:
```ts
// in kindgi.config.ts
import { prisma } from '@kindgi/sdk/build';
const config = {
// pack, discovery and environments, as kindgi init wrote them
image: {
extensions: [prisma({ schema: 'prisma/schema.prisma' })],
},
};
```
With a `prisma.config.ts`, name it too: `prisma({ schema: 'prisma/schema.prisma', config: 'prisma.config.ts' })`. **Also under `image`:** * `systemPackages`: Debian packages the image needs, such as `['tesseract-ocr']`. * `buildEnv`: placeholder variables for the build steps and for loading your modules while building, such as `{ DATABASE_URL: 'postgresql://build-placeholder' }`. Never secrets: they're set while building only, and the final image doesn't have them. * **patch-package:** when a skipped script runs it, `kindgi build` prints the build step to add (`defineBuildExtension`, also from `@kindgi/sdk/build`). With pnpm, `pnpm patch` applies your patches in the install itself. The build ends with the image and a check of what's in it:
```text
✓ Built kindgi-pack/acme-orders:20261008.193804 (sha256:9e102b60f643…)
✓ /app/index.json in the image matches the local index byte for byte
```
[Self-host with Docker](../../deploy/self-host/) pushes, signs and deploys it. ## A Python app In the app's directory (where its `pyproject.toml` is):
```sh
uv add --dev "kindgi-cli>=0.1,<0.2" # the CLI, pinned to Kindgi's minor version
uv run kindgi init # --pack-id= if the app's name doesn't make one
uv sync # or what it prints for Poetry or pip
uv run kindgi dev
```
With Poetry, add the CLI with `poetry add --group dev "kindgi-cli>=0.1,<0.2"` and run it as `poetry run kindgi`. `init` edits your `pyproject.toml` in place, keeping its layout and comments: * it adds the `[tool.kindgi]` tables: the pack id from `[project].name`, discovery under `kindgi/`, and, in a Poetry app, `dev.python` set to run `poetry run python`; * it adds `kindgi` to `[project].dependencies`. Where it can't edit them (Poetry 1, or `dynamic` dependencies), it prints the command to run instead. Then the `kindgi/tools`, `guardrails`, `agents` and `flows` folders, the Python skills and `.gitignore` entries. An app with both a `package.json` and a `pyproject.toml` gets a TypeScript pack unless you pass `--template=python`. The app's root is on `sys.path`, so a tool imports your packages by name: kindgi/tools/orders.py
```python
from typing import Any
from pydantic import BaseModel
from acme.orders import find_orders # your app's own code
from kindgi import tool
class Customer(BaseModel):
customer_id: str
class Orders(BaseModel):
orders: list[dict[str, Any]]
@tool(id="acme.customer-orders", mutating=False)
def customer_orders(input: Customer) -> Orders:
"""The customer's orders, newest first."""
return Orders(orders=find_orders(input.customer_id))
```
`mutating=False` says the tool only reads, so a dry run may call it. A package a tool imports must be in your app's main dependencies (`[project].dependencies`), not a dev group: the deployed pack installs without dev dependencies, so a dev-only import works under `kindgi dev` and fails once deployed. Inside `kindgi/`, import the pack's own modules relatively. Don't add an `__init__.py` to `kindgi/`: the folder would shadow the `kindgi` package. ## A Java app (Maven) Preview **Preview.** Java and Scala support is tested and supported, but its API may still change in 0.1.6 without the usual deprecation period. In the app's directory (where its `pom.xml` is), with a JDK 17 or later and `JAVA_HOME` set:
```sh
npx --yes @kindgi/cli@0.1 init # --pack-id= if the app's artifactId doesn't make one
./kindgiw dev
```
`init` writes `kindgi.config.json`: the pack id from the app's `artifactId`, its version, the Kindgi CLI version it pins (`"cli"`, which `./kindgiw` runs, also written), and discovery under `kindgi` packages (`src/main/java/**/kindgi/tools/**/*.java`, and so on), so your app's own `tools` packages are never taken for Kindgi's. It leaves `pom.xml` alone and prints the dependency to add to it:
```xml
com.kindgikindgi-pack…
```
kindgi-pack comes from Maven Central, at the CLI's version. It needs Jackson 2.18 or later in your app (it declares 2.18.11): in a Spring Boot app, Spring Boot 3.4 or later. An app with both a `package.json` and a `pom.xml` gets a TypeScript pack unless you pass `--template=java`. `kindgi dev` runs the app's own Maven (its `mvnw`, else `mvn`; `dev.maven` in `kindgi.config.json` names another, such as `["mvn", "-s", "settings.xml"]`) and its JDK (`JAVA_HOME`, or `dev.javaHome`). `MAVEN_ARGS` and `MAVEN_OPTS` reach Maven, never the pack. A tool is a class in a `kindgi.tools` package under your own, and calls your app's code directly: src/main/java/com/acme/kindgi/tools/CustomerOrders.java
```java
package com.acme.kindgi.tools;
import com.acme.orders.OrderService;
import com.kindgi.pack.Tool;
import java.util.List;
import java.util.Map;
public final class CustomerOrders {
public record Input(String customerId) {}
public record Output(List