# Quickstart: TypeScript

> Create a pack with tools, an agent, a guardrail and a flow, run it on your machine, and connect a real model.

In ten minutes: a pack with two tools, an agent that calls them, a guardrail on its answers and a flow, running on your machine.

**Before you start**, set up what the [Install page](https://docs.kindgi.com/v0.1.9/start/install.md) describes: Node 22.12, Docker, and access to the runtime image: sign in at [access.kindgi.com](https://access.kindgi.com/) with GitHub for its pull credentials, then log in once with `kindgi auth registry`.

## 1. Create the pack

```sh
npx --yes @kindgi/cli@0.1 init my-pack --template=sample
cd my-pack
pnpm install
```

- `npx --yes @kindgi/cli@0.1` runs the newest 0.1 release of the Kindgi CLI, the line these docs describe, without installing it ([Check this machine](https://docs.kindgi.com/v0.1.9/start/install.md#check-this-machine) says how). `@0.1` is optional: `npx --yes @kindgi/cli init my-pack --template=sample` runs the newest release, which may be on a later line than these docs.
- `init my-pack` makes the pack in a new folder, `my-pack`.
- `--template=sample` starts it with examples. The default, `minimal`, gives you the folders and none of the examples.

`init` prints what it made, as JSON and then as a summary. `pnpm install` ends with `Done`:

```text excerpt
{
  "template": "sample",
  "packId": "my-pack",
…
✓ Pack scaffolded at …/my-pack/
…
Done in 4.4s using pnpm v10.28.0
```

`my-pack` is also the pack's id: every tool, agent and flow in it is named `my-pack.<name>`. The `sample` template gives you:

```plaintext
my-pack/
├── kindgi.config.ts                    # the pack's id, version and folders
├── tools/echo/index.ts                 # a tool: echoes a message
├── tools/greet/index.ts                # a tool: greets a name
├── tools/fetch-httpbin/index.ts        # a tool that calls an HTTP API
├── agents/echo-agent/index.ts          # an agent that calls the tools
├── guardrails/response-not-empty/      # a check on the agent's answers
├── flows/echo-flow/index.ts            # a flow: a tool step, then the agent
├── .claude/skills/                     # skills for your coding agent
└── .agents/skills/                     # the same, where Codex and Gemini CLI look
```

> **Tip: Or ask your coding agent**
>
> The pack already has Kindgi's skills in `.claude/skills/` and `.agents/skills/`. Follow the steps below yourself, or ask your coding agent ("run the pack and try the agent", "add a tool that looks up an order"): the skills tell it which commands to run. [How it knows Kindgi](https://docs.kindgi.com/v0.1.9/start/coding-agents.md).

## 2. Run it

```sh
pnpm exec kindgi dev
```

`kindgi dev` starts the Kindgi runtime in Docker, indexes the pack, registers every tool, agent, guardrail and flow, and does it again on every save. It prints the API's URL and a token, and writes them to `.kindgirc.json` in the pack, so the next commands find the runtime by themselves. Leave it running.

The first address its banner prints is the console's, where you can see each run: what it was asked, the steps it took and what it answered:

```text
    Console    http://127.0.0.1:4000/console/   (open in your browser)
               Sign in: "Sign in as seeded user" on the sign-in page (the dev token, below)
```

To open the console, run `pnpm exec kindgi console`, or go to the banner's `Console` address. Its port is `4000`, or the next free one. On the sign-in page, click **Sign in as seeded user**.

> **Tip**
>
> `kindgi dev --open` opens the console as soon as Kindgi is up.

## 3. Run the agent

In a second terminal, in `my-pack`:

```sh
pnpm exec kindgi runs start --agent=my-pack.echo-agent --input='{"userMessage":"hi"}'
```

- `--agent=my-pack.echo-agent` names the agent to run, by its id.
- `--input` is the run's input, as JSON. An agent takes the person's message as `userMessage`.

```text excerpt
  "status": "completed",
…
⚠ dev-echo answered, and it isn't a real model: it only repeats what it's given. …
```

There's no model yet, so the answer comes from `dev-echo`, a stand-in a new pack gets: it calls the agent's first tool with `{"message": <your userMessage>}` and replies with what the tool returned, after a first line that says it isn't a real model. The run carries the `fallback-provider` and `dev-echo-not-a-model` warnings, and the command prints the second on its stderr, with how to add a key. That's enough to see the whole path: the agent's turn, the tool call into your code, the guardrail's check.

> **Caution: dev-echo checks the wiring, nothing more**
>
> It can't fill in any other tool input, and it can't produce a typed answer (an agent with an `output` schema fails with `output-schema-violation`). Connect a model ([step 6](https://docs.kindgi.com/v0.1.9/start/quickstart-typescript.md#6-connect-a-real-model)) before you write an agent of your own.

## 4. Run the flow

```sh
pnpm exec kindgi runs start --flow=my-pack.echo-flow --input='{"name":"Ada"}'
```

- `--flow=my-pack.echo-flow` names the flow to run, by its id.
- `--input` is the flow's input, as JSON.

```text excerpt
  "status": "completed",
…
    "greeting": "Hello, Ada!"
```

The flow greets the name with the `greet` tool, then hands the greeting to the agent and returns both. Add `--dry-run` to see which steps would run without running the tools that change anything.

## 5. Look at the code

A tool is a typed function. Its input and output are schemas, checked on every call:

`tools/greet/index.ts`

```ts
import { defineTool } from '@kindgi/sdk/define';
import type { ToolId } from '@kindgi/sdk/types';
import { z } from 'zod';

const GreetInput = z.object({
  name: z.string().min(1).max(100),
  greeting: z.string().min(1).max(50).default('Hello'),
});

const GreetOutput = z.object({
  message: z.string(),
});

const defined = defineTool({
  id: 'my-pack.greet' as ToolId,
  description: 'Formats a greeting for the named recipient.',
  version: '0.1.0',
  input: GreetInput,
  output: GreetOutput,
  effects: [],
  mutating: false,
  handler: async (input) => ({
    message: `${input.greeting}, ${input.name}!`,
  }),
});

if (defined.kind === 'err') {
  throw new Error(`my-pack.greet failed to compile: ${defined.error.message}`);
}

export default defined.value;
```

`mutating: false` says the tool changes nothing, so a dry run calls it and approval gates don't stop it by default. Leave it out for a tool that writes, sends or charges anything: a tool is treated as changing something unless it says otherwise. `effects` names what a tool does outside your code (writes, network calls), for policies and the audit trail.

The agent is data: its instructions, the tools it may call, the guardrails on its answers, its budget. Change a file and save; `kindgi dev` picks it up.

## 6. Connect a real model

Store your AI model provider's API key as a secret, then register the provider:

```sh
pnpm exec kindgi secrets set <KEY_NAME> --env=local --scope=tenant
pnpm exec kindgi providers register --preset=<preset>
```

`<KEY_NAME>` and `<preset>` depend on the provider:

| Provider                        | `<KEY_NAME>`         | `<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` |

- `secrets set <KEY_NAME>` asks for the key without showing it, and writes it to Kindgi's own `.kindgi/secrets.env`.
- `--env=local` is the environment the secret belongs to. Under `kindgi dev`, `local` is the pack's env files, that one among them.
- `--scope=tenant` is where the secret lives: the tenant, rather than one org (`org:<orgId>`) or project (`project:<projectId>`).
- `--preset=<preset>` registers the provider's models from Kindgi's built-in preset, which reads the `<KEY_NAME>` secret. `kindgi providers presets` lists every preset.

With Anthropic's key, for example, the two commands print:

```text excerpt
…
  Set ANTHROPIC_API_KEY at tenant in local.
…
✓ Registered anthropic: claude-opus-5-5, claude-sonnet-5-5 (default), claude-haiku-5-5, claude-haiku-4-5 — key ANTHROPIC_API_KEY (env local)
```

The provider takes over from `dev-echo` at the next turn. Run the agent again and it answers with a real model, still calling your tools. Gemini on Vertex AI has a preset too: `--preset=gemini --project=<gcp-project>`, where `--project` names your Google Cloud project. Any OpenAI-compatible endpoint (vLLM, llama.cpp, Ollama) registers from a short spec file.

The registration is in this project's dev database. To have `kindgi dev` register the model on every boot, in each worktree and after `--reset`, declare it in `kindgi.config.ts`: [Declare them in your pack's config](https://docs.kindgi.com/v0.1.9/guides/models.md#declare-them-in-your-packs-config).

## Next

- [Build a support desk](https://docs.kindgi.com/v0.1.9/tutorials/support-desk-typescript.md): tools over your own code, a typed answer, a flow that acts on it.
- [Guides](https://docs.kindgi.com/v0.1.9/guides.md): one task at a time: tools, agents, models, flows, runs, webhooks, approvals, secrets.
- [Add Kindgi to an existing app](https://docs.kindgi.com/v0.1.9/start/existing-app.md): your app's own code as tools, and your app starting runs.
- [Concepts](https://docs.kindgi.com/v0.1.9/concepts.md): packs, runs and the journal, security.
- [Set up your coding agent](https://docs.kindgi.com/v0.1.9/start/coding-agents.md): it already has Kindgi's skills.
