# Kimaki

> Iron Man's Jarvis for coding agents, inside Discord

This file contains the full content of all documentation pages. For a compact index, see [llms.txt](https://kimaki.dev/llms.txt). To download all pages as a zip, use [docs.zip](https://kimaki.dev/docs.zip).

---
title: "Kimaki: AI coding agents from Discord"
url: "https://kimaki.dev/index.md"
description: "Kimaki is Iron Man's Jarvis for coding agents inside Discord. Each channel is a project, each thread is a coding session."
---

import { HeroSection } from '../components/hero-section.tsx'

<Above>
  <HeroSection />
</Above>

import Readme from '../../../README.md'

<div align="center" class="hidden">
  <br />

  <br />

  <h3 id="kimaki">kimaki</h3>
  <p>A collaborative agent orchestrator, inside Discord</p>

  <br />

  <br />
</div>

Kimaki is a **collaborative agent orchestrator** that lets you drive every feature of [OpenCode](https://opencode.ai) from Discord. Each Discord **channel is a project**, each **thread is a coding session**. Send a message, an AI agent edits code on your machine.

You can try the bot in the [Kimaki Discord Server](https://discord.gg/qz3hapKcMM) to see what it can do.

## Quick Start

```bash
npx -y kimaki@latest
```

The CLI walks you through everything. Setup takes about 1 minute: you install the Kimaki bot to your Discord server with one click, pick your projects, and you're done.

## What is Kimaki?

Kimaki turns Discord into the control surface for your coding agents. It connects to [OpenCode](https://opencode.ai), a coding agent similar to Claude Code, and maps your work onto Discord's natural structure:

* **Channels are projects.** Each channel is linked to a project directory on your machine.
* **Threads are sessions.** Every message you send starts a thread that maps to one OpenCode session.

This separation is the whole point. Other Discord/iMessage agent tools cram **everything into a single channel**, so sessions pile on top of each other with no clean way to partition them. Kimaki splits **projects into channels** and **sessions into threads**, so each piece of work has its own place. Switch projects by switching channels. Switch tasks by switching threads. Search, resume, and fork any of them later.

```diagram
                            ┌──────────────────────────────────────────────────┐
   Discord server           │  Your machine                                    │
  ┌──────────────────┐      │                                                  │
  │ #web-app ────────┼──────┼──▶ /code/web-app   ──▶ OpenCode session (thread) │
  │ #api ────────────┼──────┼──▶ /code/api       ──▶ OpenCode session (thread) │
  │ #docs ───────────┼──────┼──▶ /code/docs      ──▶ OpenCode session (thread) │
  └──────────────────┘      │        ▲                                         │
        │ thread = session  │        │  reads, edits, runs commands            │
        ▼                   │        ▼  in the project directory               │
     agent replies  ◀───────┼──── AI agent (any model, your subscriptions)     │
  └──────────────────┘      └──────────────────────────────────────────────────┘
```

Think of it as texting your codebase: you describe what you want, the agent does it, and the conversation lives in a thread you can return to.

## Battle tested every day

I'm Tommy, the creator of Kimaki. I do **all of my development** through it: every project, every session, straight from Discord. I built Kimaki because I wanted one place to start agents, watch them work, jump between projects, and pick things back up from my phone. It is the tool I actually use every day.

## All your models, including subscriptions

Kimaki gives you access to **every model OpenCode supports**: Anthropic, OpenAI, Google, and more. The best part: you can use your existing **Claude Pro/Max** and **ChatGPT/Codex** subscriptions instead of paying per token.

Run `/login`, pick a provider, choose OAuth, and authenticate with your subscription. Kimaki authenticates against the provider the same way the native CLIs do, so subscription inference works and per-token costs show as zero. You can even add multiple accounts and Kimaki rotates between them on rate limits.

See [Models & Subscriptions](https://kimaki.dev/docs/getting-started/subscriptions) and [Model & Agent Switching](https://kimaki.dev/docs/getting-started/model-switching).

## Core Features

Kimaki adds a layer of orchestration features on top of OpenCode. The ones worth knowing first:

* **[Scheduled tasks](https://kimaki.dev/docs/features/scheduled-tasks)** — run the bot on a schedule (cron or a future time). For example, every morning read your inbox with a CLI like [Zele](https://github.com/remorses/zele) and post an email digest thread; then reply to mark some read or unsubscribe.
* **[The queue](https://kimaki.dev/docs/features/queue)** — queue a message to send when the current run finishes (impossible in plain OpenCode). Great for "review this when you're done" or "commit at the end". End any message with `. queue` and even edit it later to update the queued text.
* **[btw](https://kimaki.dev/docs/features/btw)** — fork the current context into a new thread to ask a clarifying question in parallel while the agent keeps working. End a message with `. btw` or run `/btw`.
* **[Worktrees](https://kimaki.dev/docs/features/worktrees)** — `/new-worktree` moves a session into an isolated folder mid-plan so it never touches your main checkout; `/merge-worktree` rebases the commits back and lets you preserve or squash them (and asks the agent to resolve conflicts).
* **[Diff viewer](https://kimaki.dev/docs/features/diff-viewer)** — `/diff` generates a shareable URL to review changes in a real diff viewer from your phone or browser.
* **[Voice messages](https://kimaki.dev/docs/features/voice)** — record a voice note; Kimaki transcribes it using your project's file tree for accuracy.
* **[Images](https://kimaki.dev/docs/features/images)** — attach images to your message and see images the agent produces, displayed inline in Discord.
* **[OpenCode commands](https://kimaki.dev/docs/features/opencode-commands)** — your OpenCode commands, skills, and MCP prompts become Discord slash commands.
* **[Shell commands](https://kimaki.dev/docs/features/shell-commands)** — prefix any message with `!` to run a shell command in the project directory.
* **[Tunnels](https://kimaki.dev/docs/remote-access/tunnels)** — expose a local dev server to a public URL so you can view it on your phone or another machine.
* **[Quick agent switching](https://kimaki.dev/docs/getting-started/model-switching)** — instantly change model or system prompt with a `/<name>-agent` command.

## How messages reach a session

When you send a message during an active run, OpenCode normally queues it to run **after the current tool call**. Kimaki adds an interrupt: if the current step is still going after \~3 seconds, Kimaki **aborts it and force-sends your message**, then resumes. So a message acts as an interrupt instead of waiting forever behind a long-running command. See [Message Handling](https://kimaki.dev/docs/core-concepts/message-handling).

## Setup

Run the CLI and follow the interactive prompts:

```bash
npx -y kimaki@latest
```

The setup wizard gives you two options:

* **Gateway mode (default)**: uses Kimaki's pre-built Discord bot. No Discord Developer Portal setup needed. Click one install link, authorize the bot in your server, and you're running. Recommended.
* **Self-hosted mode**: create your own Discord bot at [discord.com/developers](https://discord.com/developers/applications). Takes 5-10 minutes. Useful if you want full control over the bot identity.

Both modes work identically after setup. Keep the CLI running; it's the bridge between Discord and your machine.

## Commands

Kimaki ships a full set of slash commands and a CLI. The most common slash commands:

| Command                | Description                                                 |
| ---------------------- | ----------------------------------------------------------- |
| `/resume <session>`    | Resume a previous session (with autocomplete)               |
| `/abort`               | Stop the current running session                            |
| `/model`               | Change the AI model for this channel or session             |
| `/agent`               | Change the agent for this channel or session                |
| `/login`               | Authenticate a provider (OAuth subscription or API key)     |
| `/queue <message>`     | Queue a message to send after the current response finishes |
| `/btw <prompt>`        | Fork context into a new thread to ask a side question       |
| `/new-worktree <name>` | Move the session into an isolated git worktree              |
| `/merge-worktree`      | Merge the worktree branch back into the default branch      |
| `/diff`                | Generate a shareable diff URL                               |
| `/share`               | Generate a public URL to share the current session          |

See the full [Commands reference](https://kimaki.dev/docs/reference/commands) for every slash command and CLI subcommand.

## Access Control

Kimaki checks Discord permissions before processing any message. Users need **one** of:

* **Server Owner**
* **Manage Server** permission
* **Administrator** permission
* **"Kimaki" role** — create a role with this name (case-insensitive) and assign it to trusted users

The "Kimaki" role is the recommended approach for team access. Messages from users without any of these are ignored.

* **Blocking access**: create a role named **"no-kimaki"** (case-insensitive) to block specific users, even server owners.
* **Multi-agent orchestration**: other Discord bots are ignored by default. Assign the "Kimaki" role to another bot to let it trigger Kimaki sessions.

## Best Practices

* **Create a dedicated Discord server** for your agents. This keeps coding sessions separate and gives you full control over permissions.
* **Use the "Kimaki" role** for team access.
* **Send long prompts as file attachments.** Tap the plus icon and use "Send message as file" for longer prompts. Kimaki reads file attachments as your message.

## Troubleshooting

If sessions stop responding, fail to start, or the bot behaves unexpectedly, run `/restart-opencode-server` in any channel. This restarts the backend OpenCode server while keeping the bot connected to Discord. It fixes most transient issues.

If the problem persists, or if the issue is with the bot itself (crashes, messages not picked up, threads not created), run `/upgrade-and-restart` to update Kimaki to the latest version and do a full restart.

See the full [Troubleshooting guide](https://kimaki.dev/docs/guides/troubleshooting).

## Advanced Topics

* [**Channels & Threads**](https://kimaki.dev/docs/core-concepts/channels-threads): the orchestration model in depth
* [**Models & Subscriptions**](https://kimaki.dev/docs/getting-started/subscriptions): use your Claude and Codex subscriptions
* [**CI & Automation**](https://kimaki.dev/docs/guides/ci-automation): programmatic sessions, GitHub Actions, per-session permissions
* [**Scheduled Tasks**](https://kimaki.dev/docs/features/scheduled-tasks): cron and one-time tasks, email digests
* [**Advanced Setup**](https://kimaki.dev/docs/guides/advanced-setup): multiple instances, multiple Discord servers
* [**Docker**](https://kimaki.dev/docs/guides/docker): run Kimaki on a VPS
* [**Internals**](https://kimaki.dev/docs/reference/internals): how Kimaki works under the hood


---
title: "Models & Subscriptions"
url: "https://kimaki.dev/docs/getting-started/subscriptions.md"
description: "Use every OpenCode model from Discord, including your Claude and ChatGPT/Codex subscriptions via OAuth."
---

Kimaki gives you access to **every model OpenCode supports**: Anthropic, OpenAI, Google, and more. You can authenticate with an API key, or use your existing **Claude Pro/Max** and **ChatGPT/Codex** subscriptions via OAuth so you pay nothing extra per token.

## The `/login` command

Run `/login` in any project channel. Kimaki walks you through a short flow:

```diagram
  /login
    │
    ▼
  ┌──────────┐      ┌──────────┐      ┌──────────┐
  │ provider │ ───▶ │  method  │ ───▶ │   auth   │
  │ (anth/   │      │ (OAuth/  │      │ (modal)  │
  │  openai) │      │  key)    │      │          │
  └──────────┘      └──────────┘      └──────────┘
```

1. **Pick a provider.** Connected providers show a checkmark.
2. **Pick a method.** Choose **OAuth** to use a subscription, or **API key** to paste a key.
3. **Authenticate.** For OAuth, open the link and authorize; some providers ask you to paste a short code back. For API keys, enter the key in a modal.

## Using subscriptions instead of API credits

The reason to choose OAuth is cost. When you log in with **OAuth**, Kimaki authenticates the same way the native CLIs do, so your **subscription covers inference** and per-token costs show as zero.

* **Anthropic** — log in with your **Claude Pro or Claude Max** subscription. Kimaki requests the inference scope and treats requests as Claude Code requests, so subscription usage applies.
* **OpenAI / Codex** — log in with your **ChatGPT** subscription to use Codex models without per-token API billing.

<Aside>
  <Tip>
    If you already authenticated OpenCode on this machine (for example with the OpenCode CLI), those credentials are reused. You may not need to `/login` at all.
  </Tip>
</Aside>

## Pooling every subscription with Subrouter

**Subrouter** is the first entry in the `/login` provider list. Pick it and Kimaki asks *which* subscription to add, then adds it to a shared pool. Log in as many times as you like, to as many providers as you like.

When a run hits a rate limit, Subrouter tries the **next account**. When every account of that provider is exhausted, it tries the **next provider**. Quota and authentication failures rotate through the pool; normal request errors return immediately so a bad prompt is not repeated across subscriptions.

```diagram
  model: subrouter/default
       │
       ▼
  anthropic/claude-opus-4-6 ──429──▶ second Claude account ──429──▶ openai/gpt-5.5
                                                                        │
                                                        exhausted ──────┴──▶ xai/grok-4.6
```

Rate-limited accounts go into a **cooldown** shared by every session on the machine, so nothing retries a subscription that is known to be out. A `429` honors `retry-after` or `retry-after-ms` when the provider sends one, including zero. Without a usable retry delay, the fallback is five minutes. A `402` (balance exhausted) waits six hours.

After logging in, pick the model with `/model` and choose **`subrouter/default`**.

### Managing the pool

Everything except login is the [`subrouter` CLI](https://github.com/remorses/subrouter). Kimaki does not wrap it; run it directly.

```bash
# everything at a glance: accounts, presets, cooldowns
npx -y @subrouter/cli status

# accounts and their cooldown state
npx -y @subrouter/cli account list
npx -y @subrouter/cli account remove anthropic <n|email>

# stop waiting on cooldowns and retry everything now
npx -y @subrouter/cli cooldown clear
```

Install it globally with `npm i -g @subrouter/cli` if you use it often.

### Presets

A **preset** is an ordered list of `provider/model` entries, and every preset shows up as a model named `subrouter/<preset>`.

```bash
# try Opus first, fall back to Grok
npx -y @subrouter/cli preset create fast --models 'anthropic/claude-opus-4-6,xai/grok-4.6'
npx -y @subrouter/cli preset list
npx -y @subrouter/cli preset remove fast
```

The builtin `default` preset ranks the newest model of every provider you are logged in to. Create a preset named `default` to override it. New presets appear in `/model` after the OpenCode server restarts.

## Legacy per-provider rotation

Before Subrouter, Kimaki rotated accounts **within** a single provider: pick `anthropic/claude-opus-4-6`, hit a limit, swap to your second Claude account, keep the same model. That still works and is still the path used by plain `anthropic/*`, `openai/*`, and `xai/*` model ids.

It cannot fall back to a different provider, so prefer Subrouter. To move an existing account across, run `/login`, pick **Subrouter**, and authorize it again.

<Expandable title={<Markdown inline children="Legacy <code>kimaki multioauth</code> commands" />}>
  ```bash
  # List all stored Anthropic + OpenAI accounts (active one marked)
  kimaki multioauth list

  # Per-provider management
  kimaki multioauth anthropic list
  kimaki multioauth anthropic remove <index-or-email>

  kimaki multioauth openai list
  kimaki multioauth openai check     # test all accounts for usage limits
  kimaki multioauth openai remove <index-or-email>
  ```
</Expandable>

## Choosing the model after login

Once a provider is connected, pick the model:

* **`/model`** — choose provider, model, and (if supported) a thinking-level variant. Store it per session, per channel, or globally.
* **Agent files** — define a model + variant + permissions once and switch with a single `/<name>-agent` command. This is faster than the `/model` menus when you switch often.

See [Model & Agent Switching](/docs/getting-started/model-switching) for the full agent-file workflow.

## API keys instead of subscriptions

Prefer a plain API key? Choose **API key** in the `/login` method step and paste it. You can also set the model directly in your project's `opencode.json`:

```json
{
  "model": "anthropic/claude-sonnet-4-20250514"
}
```

Format is `provider/model-name`, for example `openai/gpt-5.3-codex` or `google/gemini-3-pro-preview`.


---
title: "Model & Agent Switching"
url: "https://kimaki.dev/docs/getting-started/model-switching.md"
description: How to quickly switch models and thinking levels using agent markdown files and Discord commands.
---

Kimaki gives you two main ways to switch models: **agent files** (`.opencode/agent/*.md`) for one-command switching, and the **`/model` slash command** for interactive selection. From `/model` you can also change the thinking level or clear an override via buttons on the same reply. Agent files are the fastest option for day-to-day use; `/model` is useful for one-off changes.

## Why agents are faster than /model

The `/model` command requires clicking through 3-4 dropdown menus every time you want to switch. With agent files you define a model + variant + permissions once, restart kimaki, and from then on you can switch with a single `/sonnet-high-agent` command. No menus, instant.

This is especially useful when you regularly switch between models during a session, for example using a cheap model for boilerplate and a powerful one for architecture decisions.

## Creating agent files

Create `.md` files in your project's `.opencode/agent/` directory. Each file becomes a selectable agent in the `/agent` dropdown and gets its own `/<name>-agent` slash command.

The file only needs YAML frontmatter. The body is an optional system prompt override.

### Examples

**.opencode/agent/sonnet-high.md** - Sonnet with high thinking, auto-allows questions and plan entry so it doesn't stall on permission prompts:

```yaml
---
description: Claude Sonnet with high thinking
mode: primary
model: anthropic/claude-sonnet-4-20250514
variant: high
permission:
  question: allow
  plan_enter: allow
---
```

**.opencode/agent/opus-max.md** - Opus with maximum thinking budget. Use for complex architecture and debugging tasks:

```yaml
---
description: Claude Opus with max thinking
mode: primary
model: anthropic/claude-opus-4-20250514
variant: max
permission:
  question: allow
  plan_enter: allow
---
```

**.opencode/agent/gpt5.md** - GPT 5 Codex for fast iteration:

```yaml
---
description: GPT 5 Codex
mode: primary
model: openai/gpt-5.3-codex
permission:
  question: allow
  plan_enter: allow
---
```

**.opencode/agent/gpt5-medium.md** - GPT 5 Codex pinned to medium reasoning effort:

```yaml
---
description: GPT 5 Codex, medium effort
mode: primary
model: openai/gpt-5.3-codex
variant: medium
permission:
  question: allow
  plan_enter: allow
---
```

**.opencode/agent/gemini-low\.md** - Gemini with low thinking for quick cheap tasks. Lower temperature for more deterministic output:

```yaml
---
description: Gemini 3 pro, low thinking
mode: primary
model: google/gemini-3-pro-preview
variant: low
temperature: 0.3
permission:
  question: allow
  plan_enter: allow
---
```

**.opencode/agent/safe-build.md** - Locked down agent that can only read and edit, no bash or external tools. Useful for untrusted refactoring tasks:

```yaml
---
description: Build agent, no bash or external tools
mode: primary
model: anthropic/claude-sonnet-4-20250514
variant: high
color: '#E67E22'
steps: 50
permission:
  bash: deny
  question: allow
  plan_enter: allow
---
```

### Frontmatter reference

| Field         | Type    | Description                                                                                            |
| ------------- | ------- | ------------------------------------------------------------------------------------------------------ |
| `model`       | string  | Model ID as `provider/model` (e.g. `anthropic/claude-sonnet-4-20250514`)                               |
| `variant`     | string  | Reasoning level (provider/model dependent): `none`, `minimal`, `low`, `medium`, `high`, `max`, `xhigh` |
| `mode`        | string  | `primary` (top-level agent) or `all` (usable as both top-level and subagent)                           |
| `description` | string  | Shown in the `/agent` selector and agent autocomplete                                                  |
| `permission`  | object  | Auto-allow/deny permissions: `question`, `plan_enter`, `bash`, `edit`, etc.                            |
| `temperature` | number  | Sampling temperature (e.g. `0.3` for deterministic, `1.0` for creative)                                |
| `top_p`       | number  | Top-p nucleus sampling                                                                                 |
| `steps`       | number  | Max agentic iterations before forcing a text-only response                                             |
| `hidden`      | boolean | Hide from the agent selector UI                                                                        |
| `color`       | string  | Hex color (e.g. `"#FF5733"`) or theme color (`primary`, `warning`, etc.)                               |
| `options`     | object  | Extra provider-specific parameters passed through to the model API                                     |

Any unknown frontmatter keys are automatically merged into `options` and forwarded to the provider, so you can pass provider-specific parameters directly in frontmatter without nesting them under `options`.

### Effort customization notes

`variant` is the primary way to customize agent effort from markdown frontmatter.

* For GPT-5 models, use variants like `minimal`, `low`, `medium`, `high`, or `xhigh` (if the selected GPT-5 model supports it).
* For Anthropic models, common values are `high` and `max` (mapped to model thinking settings).
* Available values depend on the exact provider + model pair.

## Override the default `build` agent variant

The best way to override the default build agent effort is to create a file named exactly `build.md` in `.opencode/agent/`. The filename maps to the agent name, so this merges into the built-in `build` agent config.

Use both `model` and `variant` to make behavior predictable:

```yaml
---
description: Build agent pinned to GPT-5 medium effort
mode: primary
model: openai/gpt-5.3-codex
variant: medium
permission:
  question: allow
  plan_enter: allow
---
```

Why this is the most reliable approach:

* `build.md` targets the native `build` agent directly.
* `variant` gives you default effort for that agent.
* pairing with `model` avoids ambiguity when the active model changes.
* explicit per-request variant still wins when you choose one manually.

> Using `.md` agent files does **not** override built-in prompts unless the file has markdown body content.
> Frontmatter-only files keep default prompt behavior and only change config fields like `model`, `variant`, `permission`, and `steps`.
> This applies to built-in agents like `build`, `plan`, `explore`, and other native agents.

### Activating

After creating or editing agent files, restart kimaki so it picks up the new slash commands. Then switch with:

* `/agent` dropdown to pick from all available agents
* `/<name>-agent` slash command for instant one-step switching (e.g. `/sonnet-high-agent`, `/opus-max-agent`, `/gpt5-agent`)

## Setting variant via /model

The `/model` command includes a variant picker. After selecting a provider and model, if the model supports thinking levels a dropdown appears. The variant is stored alongside the model at whatever scope you choose (session, channel, or global).


---
title: Background Sync
url: "https://kimaki.dev/docs/getting-started/background-sync.md"
description: Automatically mirror OpenCode sessions started outside Kimaki into Discord threads.
---

Kimaki runs a background sync loop that mirrors **OpenCode sessions started outside of Discord** into Discord threads. If you start a session from the OpenCode CLI, TUI, or any other client, it automatically appears as a thread in the matching project channel.

```diagram
  Your terminal                              Discord
 ┌────────────────────────────┐             ┌────────────────────────────────────────────┐
 │                            │             │  #my-project                               │
 │  $ opencode                │             │                                            │
 │  > fix the auth bug        │────────────►│  └─ Sync: fix the auth bug                 │
  │    ...model working...     │             │       The auth token was expired...       │
  │  > now add tests           │────────────►│       » user: now add tests                │
  │                            │             │       I added tests for the auth...       │
 └────────────────────────────┘             └────────────────────────────────────────────┘
```

Sync is **enabled by default**, polls every 5 seconds, is lightweight when idle, and only syncs sessions created after the bot started.

## Use cases

* **Resume sessions from your phone.** Start a coding session on your desktop with the OpenCode CLI. Leave your desk. The session appears in Discord, so you can follow progress, send follow-up messages, or queue commands from your phone.
* **Search past sessions.** Discord's search makes it easy to find sessions across all your projects. Every synced session is a thread with full message history, searchable by keyword, date, or channel.
* **Team collaboration.** If your team shares a machine running Kimaki (a dev server, shared workstation, or remote box), sessions started by anyone on that machine show up in the shared Discord channel. Others can see what's happening, jump into the thread, or continue the work.

## How it works

The sync loop checks all registered project directories every 5 seconds. For each directory, it:

1. Lists recent sessions from the OpenCode server
2. Skips sessions already managed by Discord (Kimaki-originated sessions have a `<discord-user />` tag in their messages)
3. Creates a new thread (prefixed with "Sync:") or reuses an existing one for each external session
4. Sends any new messages that haven't been synced yet, respecting your channel's verbosity setting
5. Shows a typing indicator for sessions that are currently busy

**Seamless handoff.** If you start a session in the CLI and then continue it from Discord, the sync loop detects the switch and stops mirroring. If you go back to the CLI, sync picks it up again. No manual toggling; the latest user message determines ownership.

**One server for everything.** Kimaki runs a single shared OpenCode server process for all projects. The sync loop reuses this same server, so there's no extra overhead.

## Disabling sync

If you don't need background sync, disable it with:

```bash
kimaki --disable-sync
```

Kimaki still works normally for Discord-originated sessions; only the external session mirroring is turned off.


---
title: "Channels & Threads"
url: "https://kimaki.dev/docs/core-concepts/channels-threads.md"
description: "How Kimaki maps Discord channels to projects and threads to coding sessions, the core orchestration model."
---

Kimaki's whole design rests on one mapping: a Discord **channel is a project**, and a **thread is a coding session**. Understanding this makes everything else obvious.

```diagram
  Discord                       Your machine
  ┌─────────────────┐           ┌─────────────┐
  │ server          │           │             │
  │  #web-app       │  ───────▶ │ /code/web   │
  │   ├─ login      │  ───────▶ │ session A   │
  │   └─ dark       │  ───────▶ │ session B   │
  │  #api           │  ───────▶ │ /code/api   │
  │   └─ limit      │  ───────▶ │ session C   │
  └─────────────────┘           └─────────────┘
```

## Why this matters

Most chat-based agent tools put **everything in one channel**, like an iMessage thread. Sessions stack on top of each other, context bleeds between unrelated tasks, and there is no clean way to find or resume a specific piece of work.

Kimaki partitions work along the two axes that actually matter:

* **Projects → channels.** Switch the project you're working on by switching channels.
* **Sessions → threads.** Switch the task you're working on by switching threads.

Each thread keeps its own conversation, its own session state, and its own history. You can leave a thread, come back days later, and resume exactly where you left off.

<Aside>
  <Tip>
    Treat threads like git branches for conversations. One thread per task keeps each session focused and easy to search later.
  </Tip>
</Aside>

## How a channel becomes a project

When you add a project, Kimaki creates a Discord channel and stores the link between that channel and a directory on your machine. The mapping lives in the channel topic as metadata:

```xml
<kimaki><directory>/code/web-app</directory><app>bot_id</app></kimaki>
```

Add projects during the setup wizard, with the `/add-project` slash command, or from the CLI:

```bash
# Add the current directory as a project channel
npx -y kimaki project add

# Add a specific directory
npx -y kimaki project add /code/web-app
```

## How a message becomes a session

The first message you send in a project channel **creates a thread** and starts an OpenCode session bound to that channel's directory. The agent replies inside the thread. Text has no prefix. A separator appears when the reply switches between text and tool output.

From then on, messages **in that thread** continue the same session. Messages **in the channel** (outside any thread) start new threads and new sessions. This is what keeps tasks isolated.

```diagram
  #web-app channel
       │
       │ first message ──▶ creates "fix login" ──▶ session starts
       │
       │ another msg ────▶ creates "dark mode" ──▶ separate session
       │
       └─ thread "fix login" ──▶ continues the same session
```

## Resuming, forking, and searching

Because every session is a thread, you get Discord's organization for free:

* **Resume** any past session with `/resume` (autocomplete lists recent sessions).
* **Fork** from any earlier message with `/fork`, or fork the whole context into a new thread with [`/btw`](/docs/features/btw).
* **Search** across all your projects using Discord's built-in search; every thread is full message history.

<Aside>
  <Note>
    Sessions you start outside Discord (OpenCode CLI or TUI) can also appear as threads automatically. See [Background Sync](/docs/getting-started/background-sync).
  </Note>
</Aside>

## One server per machine, many channels

A single Kimaki instance runs on one machine and can serve **many channels across many Discord servers**. Each channel points at a directory on the machine where the bot runs. To control more machines, run Kimaki on each and add their channels to the same server. See [Advanced Setup](/docs/guides/advanced-setup) for multi-machine and multi-server layouts.


---
title: Message Handling
url: "https://kimaki.dev/docs/core-concepts/message-handling.md"
description: "How Discord messages are delivered into an OpenCode session, and how Kimaki turns a message into an interrupt."
---

When you send a message to an active session, Kimaki has to decide **when** the agent sees it. This page explains the delivery model and the interrupt behavior that makes a mid-run message actually stop the agent.

## The default: queue after the current step

OpenCode runs in steps: it calls a tool, reads the result, calls another tool, and so on. When you send a message while a run is in progress, OpenCode **queues it to be delivered after the current step finishes**. The agent keeps doing what it's doing, then picks up your new message on the next step.

```diagram
  You send message
        │
        ▼
  ┌──────────┐      step finishes      ┌──────────┐
  │ tool call│ ──────────────────────▶ │agent     │
  │(running) │                        │reads msg │
  └──────────┘                        └──────────┘
```

This is fine for quick steps. But if the current step is a **long-running command** (a build, a test suite, a `sleep`), your message could wait a long time before the agent ever sees it.

## The interrupt: abort after \~3 seconds

Kimaki adds an interrupt on top. If your message has not started being processed within about **3 seconds**, Kimaki **aborts the current step and force-delivers your message**, then resumes the session with it.

```diagram
  You send message during long run
         │
         ▼ timer: 3s
  ┌──────────┐  fires  ┌──────────┐ idle  ┌──────────┐
  │long step │ ──────▶ │abort     │ ────▶ │resend    │
  │(build)   │         │          │       │msg       │
  └──────────┘         └──────────┘       └──────────┘
```

The effect: **a message acts as an interrupt**. Instead of waiting behind a slow command, your new instruction takes over. This is exactly what you want when the agent is heading the wrong direction and you need to redirect it now.

<Aside>
  <Note>
    The abort preserves your session's agent and model overrides. Kimaki resends the original message parts after the abort, so nothing about your session config is lost.
  </Note>
</Aside>

## Tuning the timeout

The interrupt timeout defaults to **3000 ms**. Override it with an environment variable when starting Kimaki:

```bash
KIMAKI_INTERRUPT_STEP_TIMEOUT_MS=8000 npx -y kimaki@latest
```

Increase it if you want short tool calls to finish before your message interrupts. Decrease it for a snappier "stop now" feel.

## When you want to wait instead of interrupt

Sometimes you do **not** want to interrupt: you want to line up a follow-up for after the run completes. That's what [the queue](/docs/features/queue) is for. End your message with `. queue` (or use `/queue`) and it waits for the current run to finish instead of aborting it.

| You want to...                                 | Use                                          |
| ---------------------------------------------- | -------------------------------------------- |
| Redirect the agent right now                   | a normal message (interrupts after \~3s)     |
| Add a follow-up for after the run finishes     | [`. queue` / `/queue`](/docs/features/queue) |
| Ask a side question without disturbing the run | [`. btw` / `/btw`](/docs/features/btw)       |


---
title: Managing Sessions
url: "https://kimaki.dev/docs/core-concepts/sessions.md"
description: "Resume, fork, share, undo, compact, and inspect your coding sessions from Discord."
---

Every thread is a session, and Kimaki gives you a full set of commands to manage their lifecycle: pick up old ones, branch off, share, and clean up context.

## Resuming and starting

```diagram
/resume <session> ──▶ continue a past session (autocomplete lists recent)
/new-session      ──▶ start a fresh session with a prompt, files, and agent
/last-sessions    ──▶ list the 20 most recently active sessions
```

* **`/resume <session>`** — reopen any earlier session; autocomplete suggests recent ones.
* **`/new-session`** — start a new session, optionally with mentioned files and a chosen agent.
* **`/last-sessions`** — a quick cross-project list of what you were last working on. Titles prefixed **`btw:`** are [side-question forks](/docs/features/btw). They answer a related question in parallel and are not duplicate sessions.

The CLI equivalent is **`kimaki session list --active`**.

## Forking

Forking branches a session so you can explore without disturbing the original.

* **`/fork`** — fork from a chosen earlier message and continue from that point.
* **`/fork-subagent`** — pull a subagent's task out into its own thread to continue it directly.
* **[`/btw`](/docs/features/btw)** — fork the whole context into a new thread to answer a side question in parallel.

<Aside>
  <Tip>
    Use `/fork` to try an alternative approach from a known-good point. The original thread stays intact, so you can compare both.
  </Tip>
</Aside>

## Sharing and identifying

* **`/share`** — generate a **public URL** for the session so anyone with the link can read it.
* **`/session-id`** — show the session ID and an `opencode` command to attach to it from your terminal.
* **[`/diff`](/docs/features/diff-viewer)** — share the current changes as a diff URL.

To export the session event stream and file a Kimaki bug, see [Debug sessions and report bugs](/docs/guides/report-bugs).

## Context management

Long sessions fill the model's context window. Keep them healthy:

* **`/context-usage`** — show token usage and the percentage of the context window used.
* **`/compact`** — summarize the conversation so far to free up context while keeping the gist.

Kimaki also shows context usage automatically in the thread at roughly 10% increments (prefixed `⬦`), and at the end of each assistant turn in a footer like `kimakivoice ⋅ main ⋅ 2m 30s ⋅ 71% ⋅ claude-opus`.

## Undo and redo

* **`/undo`** — revert the last assistant message, including the file changes it made.
* **`/redo`** — reapply the last undone message.

These let you back out a change the agent made without manually reverting files.


---
title: The Queue
url: "https://kimaki.dev/docs/features/queue.md"
description: "Queue a message to send when the current run finishes, something plain OpenCode cannot do."
---

The **queue** lets you line up a message to be delivered **when the current run finishes**, instead of interrupting it. Plain OpenCode has no way to do this; Kimaki tracks the queue itself.

## Why queue instead of just sending

A normal message during a run [acts as an interrupt](/docs/core-concepts/message-handling): after \~3 seconds Kimaki aborts the current step to deliver it. The queue is the opposite: it **waits politely** for the run to complete, then sends.

This is perfect for follow-ups that only make sense after the work is done:

* **"Review what you just did"** — queue a review request for a second pass.
* **"Commit at the end"** — queue a commit instruction so it runs once the feature is finished.
* **Ask another agent to check the work** — queue a handoff message for the end of the run.

## Queueing a message

Two ways to queue, both identical in effect:

```diagram
  /queue <message>     ──▶ explicit command
  message... . queue   ──▶ suffix shortcut
```

* **`/queue <message>`** — queues the given text.
* **`. queue` suffix** — end any message with a punctuation mark plus `queue` and it gets queued automatically. Supported forms include `. queue`, `! queue`, `? queue`, a trailing `queue.`, or `queue` on its own final line.

If the session is **busy**, the message waits and Kimaki replies with its queue position. If the session is already **idle**, it dispatches immediately.

<Aside>
  <Tip>
    The suffix is stripped before the prompt reaches the agent, so `fix the test. queue` is sent as `fix the test`.
  </Tip>
</Aside>

## Editing a queued message

This is the part people love: **edit the Discord message you queued and the queue updates automatically**.

* Change the text → the queued prompt is updated to the new content.
* Remove the `queue` suffix → the item is **dropped from the queue** entirely.

No need to clear and re-add. Just edit in place while it's still waiting.

## When a queued message is sent

Once the run finishes, Kimaki dispatches the queued message and shows it in the thread as:

```
» Tommy: commit everything with a descriptive message
```

The `»` marker only appears when the message actually had to wait behind a running request. If it dispatched immediately (idle session), it's sent without the marker.

## Queueing a command

You can queue a registered slash command, not just text:

```
/queue-command <command> [arguments]
```

This validates that the command exists and queues it. When sent, it shows as `» Tommy: /command-name`.

## Removing a queued message

Each `/queue` and `/queue-command` confirmation includes a **Remove from queue** button. Click it to drop that one entry without affecting other queued messages.

You can also delete the original Discord message when the queue item came from a normal thread message (not a slash command).


---
title: btw (side questions)
url: "https://kimaki.dev/docs/features/btw.md"
description: Fork the current context into a new thread to ask a clarifying question while the agent keeps working.
---

**btw** forks the current session's full context into a **new thread** and immediately answers a side question there, without disturbing the running task. Use it when you need a clarification in parallel.

## The problem it solves

The agent is in the middle of a long task and you suddenly want to ask "wait, why did you choose X?" If you send that in the thread, it [interrupts](/docs/core-concepts/message-handling) the work. If you wait, you lose the moment.

**btw** splits the question off into its own thread that shares the same context but runs independently.

```diagram
  thread: build ──────────────────────▶ keeps working
       │
       │ ". btw why pick?"
       ▼
  thread: btw ──▶ answers in parallel
```

## Using btw

Two equivalent triggers:

* **`/btw <prompt>`** — fork and ask the prompt.
* **`. btw` suffix** — end a message with punctuation plus `btw`, for example `why this approach? btw`. Supported forms include `. btw`, `! btw`, a trailing `btw.`, or `btw` on its own final line.

Unlike the [queue suffix](/docs/features/queue), the `btw` suffix **requires punctuation or a newline** before it, so `btw fix this` at the start of a message is not treated as a btw.

## What happens

1. Kimaki **forks the entire session context** (no single message ID), so the new thread knows everything the original does.
2. It creates a thread named `btw: <your prompt>`.
3. It **does not replay** past Discord messages into the new thread; it just carries the context.
4. It **dispatches your question immediately**, so the forked session starts answering right away.

The forked session is told its only job is to answer your question: it will not continue, resume, or reference the original task.

Session lists such as **`/last-sessions`** and **`kimaki session list --active`** show these threads with a **`btw:`** title prefix. They are side sessions, not duplicates of the original task.

<Aside>
  <Note>
    The original thread is never paused or interrupted. btw runs as a separate session in parallel.
  </Note>
</Aside>

## btw vs fork vs queue

| Want to...                                          | Use                                 |
| --------------------------------------------------- | ----------------------------------- |
| Ask a side question in parallel, keep the run going | **btw**                             |
| Continue from a specific earlier message            | [`/fork`](/docs/reference/commands) |
| Add a follow-up for after the run finishes          | [the queue](/docs/features/queue)   |
| Redirect the agent right now                        | a normal message                    |


---
title: Worktrees
url: "https://kimaki.dev/docs/features/worktrees.md"
description: "Move a session into an isolated git worktree mid-plan, then merge the commits back into your default branch."
---

**Worktrees** let a session work in an **isolated folder** so it never touches your main checkout. Start with an idea in a thread, then move it into a worktree once you realize it should not change your main files. Merge the commits back later with one command.

## The typical flow

```diagram
  thread: redesign sidebar
       │ /new-worktree
       ▼
  ┌──────────────────┐     work in worktree
  │worktree: opencode│──────▶ commits accumulate
  │kimaki-redesign   │
  └────┬─────────────┘
       │ /merge-worktree
       ▼
  rebase onto target
       ├──▶ keep commits ──▶ fast-forward ──▶ done
       └──▶ squash ──▶ one commit ──▶ fast-forward ──▶ done
```

## Creating a worktree

Run **`/new-worktree`**:

* **From a project channel**, pass a `name`. Kimaki creates a thread immediately (so you can start typing) and builds the worktree in the background. The branch is named `opencode/kimaki-<name>` and the thread is prefixed with `⬦ worktree:`.
* **From an existing thread**, the `name` is optional; Kimaki derives it from the thread name.
* **`base-branch`** is optional and defaults to `HEAD` (your current local HEAD), so you can branch from unpushed commits.

<Aside>
  <Tip>
    Long auto-derived names get shortened by stripping vowels (e.g. `configurable-sidebar-width` becomes `cnfgrbl-sdbr-wdth`). Names you pass explicitly are kept as-is.
  </Tip>
</Aside>

## Why isolate work in a worktree

* **Protect your main checkout.** Experimental or risky changes stay in a separate folder.
* **Run things in parallel.** Different threads can each have their own worktree and branch.
* **Keep a clean default branch** until you decide the work is good.

## Merging back

When the worktree work is ready, run **`/merge-worktree`**. The `strategy` select has two choices, and `target-branch` optionally overrides the project's default branch:

* **Keep commits (rebase)** is the default. It rebases the worktree commits onto the target and preserves every commit.
* **Squash into one commit** rebases the same changes, then creates one commit on the target named `Merge worktree <branch>`.
* The worktree and target must have **no uncommitted changes**.
* On success the `⬦ worktree:` prefix is removed from the thread title and Kimaki reports the source commit count.

## Conflict resolution by the agent

If the rebase hits **conflicts**, Kimaki does not give up. It asks the AI model **in the thread** to resolve them: understand both sides, edit the conflicted files, `git add`, and `git rebase --continue`, repeating until the rebase finishes. The agent then runs `kimaki merge-worktree` itself with the same strategy and target branch. If the target changed and the automatic retry fails, run `/merge-worktree` again later.

```diagram
  /merge-worktree ──▶ rebase ──▶ conflict?
                                 │ yes
                                 ▼
                      agent resolves ──▶ CLI retries merge ──▶ done
                                              └──▶ failure ──▶ retry later
```

## Listing worktrees and auto-worktrees

Run **`/worktrees`** to see every worktree for the channel's project (Kimaki, OpenCode, or manual). The same view has a **Turn on / Turn off** button for automatic worktree creation on new sessions in that channel.

<Aside>
  <Note>
    Worktrees created by Kimaki, by OpenCode, or manually all show up in `/worktrees` for the channel's project.
  </Note>
</Aside>


---
title: Diff Viewer
url: "https://kimaki.dev/docs/features/diff-viewer.md"
description: Generate a shareable URL to review your changes in a real diff viewer from your phone or browser.
---

The **`/diff`** command uploads your current git diff and gives you a **shareable URL** to a real, syntax-highlighted diff viewer. Review changes from your phone, your desktop browser, or send the link to a teammate.

## Using it

Run **`/diff`** in a thread or project channel. Kimaki:

1. Runs the [critique](https://github.com/remorses/critique) CLI on the working directory.
2. Uploads the diff and gets back a URL like `https://critique.work/v/<id>`.
3. Replies in Discord with the link and an inline preview image.

```diagram
  /diff ──▶ critique --web ──▶ upload ──▶ https://critique.work/v/<id>
                                              │
                                              ▼
                                 open on phone or desktop
```

Click the embed to open the full diff viewer in your browser. It works on mobile, which is the whole point: you can review what the agent changed without being at your machine.

<Aside>
  <Note>
    Diffs are ephemeral and the URL is unguessable; only people you share the link with can see it. critique is open source.
  </Note>
</Aside>

## Common cases

* **No changes** — if the working tree is clean, Kimaki replies "No changes to show".
* **critique not installed** — Kimaki tells you it's unavailable. It ships with Kimaki in normal installs.

## Why this beats raw diff output

A code block in Discord is hard to read on a phone and has no syntax highlighting or file navigation. A diff URL gives you a proper viewer with per-file navigation and highlighting, so reviewing a large change is actually pleasant on a small screen.


---
title: Scheduled Tasks
url: "https://kimaki.dev/docs/features/scheduled-tasks.md"
description: "Run the bot on a schedule, cron or a one-time future time, for digests, reminders, and recurring maintenance."
---

Kimaki can run a prompt **on a schedule**: once at a future time, or repeatedly on a cron expression. This turns the bot into an autonomous worker that wakes up, does a job, and posts the result as a Discord thread you can reply to.

## Scheduling with `--send-at`

Add **`--send-at`** to any `kimaki send` command. It accepts a one-time UTC ISO timestamp (ending in `Z`) or a cron expression:

```bash
# One-time: run at a specific UTC time
kimaki send --channel <channel-id> --prompt 'Review open PRs' \
  --send-at '2026-03-01T09:00:00Z'

# Recurring: every Monday at 9am UTC
kimaki send --channel <channel-id> \
  --prompt 'Run the test suite and summarize failures' \
  --send-at '0 9 * * 1'
```

<Aside>
  <Warning>
    All scheduling is in **UTC**. One-time dates must end with `Z`, and cron expressions fire in UTC too. Convert from your local time before scheduling.
  </Warning>
</Aside>

## Flagship example: a morning email digest

The pattern that makes scheduled tasks shine: a **morning email digest** you can act on from Discord.

```diagram
  cron 0 7 * * *
       │
       ▼
  ┌─────────┐      ┌──────────┐      ┌───────────┐
  │ read    │ ───▶ │ post     │ ───▶ │ you reply │
  │ inbox   │      │ digest   │      │ (mark)    │
  └─────────┘      └──────────┘      └───────────┘
```

Schedule a task that reads your inbox each morning with a CLI like **[Zele](https://github.com/remorses/zele)** (a multi-account email and calendar CLI) and posts a summary:

```bash
kimaki send --channel <channel-id> \
  --prompt 'Use the zele CLI to read my unread emails. Post a concise digest grouped by sender and importance. List the message IDs so I can act on them.' \
  --send-at '0 7 * * *'
```

When the digest lands as a thread, **reply to it** to resume the work: "mark the GitHub ones as read and unsubscribe from the newsletters." The agent runs the corresponding `zele` commands. The whole loop, read then act, happens from one Discord thread.

## Notify-only reminders

For a reminder that should **not** start an AI session, add `--notify-only`. It posts a thread you can reply to later:

```bash
kimaki send --session <session-id> \
  --prompt 'Reminder: <@user-id> the staging API key created today expires 2026-06-01. Renew it before it breaks prod.' \
  --send-at '2026-05-28T09:00:00Z' --notify-only
```

Make reminder prompts **detailed**: the future reader has no context, so include what, when, why, and the exact action needed.

## Other recurring patterns

* **Weekly QA** — run the full test suite, summarize failures, mention a user only if review is needed.
* **Maintenance cron** — rotate secrets, check dependency updates, clean stale branches on a `0 9 1 * *` monthly schedule.
* **Project memory** — have the task read and update a markdown file in the repo each run.

All other `send` flags (`--notify-only`, `--worktree`, `--agent`, `--model`, `--user`) work with `--send-at`. The exception is `--wait`, which is incompatible since the task runs in the future.

Pass **`--user`** when someone should see the thread in Discord. Omit it for quiet autonomous work so the thread stays out of the sidebar. Clear a stored user later with `kimaki task edit <id> --user ''`.

## Run only when needed

Use **`--pre-run`** to run a shell command in the project directory before a scheduled occurrence starts. This example uses [crisp-cli](https://github.com/remorses/crisp-cli) to start a support session only when Crisp has unread customer messages.

Create `scripts/should-run-support.ts`:

```ts
#!/usr/bin/env bun

import { $ } from 'bun'

type Conversation = {
  session_id: string
  unread?: { operator?: number }
  preview_message?: {
    from?: string
  }
}

const output = await $`crisp-cli list_website_conversations`.text()
const response = Bun.YAML.parse(output) as { items?: Conversation[] }
const requests = (response.items ?? []).filter((conversation) => {
  return (
    (conversation.unread?.operator ?? 0) > 0 &&
    conversation.preview_message?.from === 'user'
  )
})

const sessionIds = requests.map((conversation) => conversation.session_id)
if (sessionIds.length === 0) process.exit(1)

console.log(Bun.YAML.stringify({ sessionIds }))
```

The **unread operator count** means the support team has not read the message. The `from: user` check excludes operator replies. Install and configure the CLI once before scheduling:

```bash
pnpm add -g crisp-cli
crisp-cli config --token '<crisp-mcp-token>'
```

Then schedule the check every five minutes:

```bash
kimaki send --channel <channel-id> \
  --send-at '*/5 * * * *' \
  --pre-run 'bun scripts/should-run-support.ts' \
  --prompt 'Read the Crisp session IDs in the pre-run output. Use crisp-cli to inspect each conversation and draft a support reply.'
```

An exit code of **0 starts the session**. Kimaki appends the command's stdout to the task prompt. Any other exit code skips that occurrence. Stdout and stderr are also written to the Kimaki log.

Scheduled tasks **do not overlap by default**. If one occurrence still has an active OpenCode session, Kimaki skips the next occurrence and checks again at the following scheduled time. Add `--allow-concurrency` only when parallel runs are safe.

## Managing tasks

```bash
kimaki task list
kimaki task edit <id> --prompt "new prompt" [--send-at "new schedule"] \
  [--pre-run "command"] [--allow-concurrency true|false] [--user "<discord-user-id>"]
kimaki task edit <id> --user ''
kimaki task delete <id>
```

The `/tasks` slash command lists scheduled tasks in Discord with cancel buttons. For the full `kimaki send` reference and CI usage, see [CI & Automation](/docs/guides/ci-automation).


---
title: Sleeping Sessions
url: "https://kimaki.dev/docs/features/sleep.md"
description: "Let an agent pause itself for hours or days with the kimaki_sleep tool, then wake up in the same thread and keep working."
---

An agent can **put itself to sleep** and wake up later in the same thread. Use it to wait for a deploy, a nightly build, a support reply, or a specific date, without burning tokens and without you remembering to poke it.

The agent calls the **`kimaki_sleep`** tool, the session goes idle, and Kimaki wakes it when the time arrives.

```diagram
  agent calls kimaki_sleep        Kimaki checks every 5s         same session resumes
            │                               │                               │
            ▼                               ▼                               ▼
  ┌────────────────────┐  writes  ┌────────────────────┐   due    ┌────────────────────┐
  │ session goes idle  │ ───────► │ wake row in SQLite │ ───────► │ wake message in    │
  │ no tokens burned   │          │ survives restarts  │          │ the same thread    │
  └────────────────────┘          └────────────────────┘          └────────────────────┘
```

## Asking for a sleep

Just say it in the thread. The agent picks the tool itself:

> deploy is running, check back in 2 hours and confirm it went green

> wait until tomorrow 9am UTC, then summarize what changed overnight

The tool takes either **`duration`** or **`until`**, never both:

| Argument   | Meaning                          | Examples                 |
| ---------- | -------------------------------- | ------------------------ |
| `duration` | Relative wait                    | `30s`, `10m`, `2h`, `1d` |
| `until`    | Absolute UTC instant             | `2030-01-01T09:00:00Z`   |
| `reason`   | Why it is waiting, shown on wake | `waiting for the deploy` |

In Discord the sleep appears as a normal tool line:

```
┣ kimaki_sleep for 2h _waiting for the deploy_
```

<Aside>
  <Warning>
    `until` must be **UTC** and end with `Z`. Convert from your local time before asking for a specific instant.
  </Warning>
</Aside>

## Waking up

When the time arrives, Kimaki posts a wake message into the same thread and the session continues with its full history:

```
⬦ Woke after sleeping until 2030-01-01 09:00 UTC
Reason: waiting for the deploy
Continue the work you were waiting for.
```

The wake is a **new turn** on the **same session**, so the agent still remembers everything from before the sleep.

## It survives restarts

The wake time is stored in SQLite, not in memory. Restarting Kimaki, upgrading it, or rebooting the machine does not lose a pending sleep. The next time the bot runs, the due sleep fires.

Delivery is **retried until it lands**. A sleep is only marked delivered once the wake has actually started a turn, so a crash midway through waking just retries on the next check. Each wake carries an idempotency key, so a retry can never wake the same session twice.

## Cancelling a sleep

Send **any message** in the thread and the pending sleep is cancelled. The agent picks up your new message instead, and no wake arrives later. If it still needs the later wake after answering, it should call **`kimaki_sleep` again** with the original `until` time.

This is not limited to chat messages. **`/queue`**, **`/abort`**, slash commands, and prompts sent with `kimaki send` all supersede a pending sleep, because cancelling happens wherever a new turn enters the session.

<Aside>
  <Tip>
    **`/btw` is the exception.** It forks your question into a side thread on purpose, so the sleeping thread keeps waiting. Use it to ask something without ending the wait.
  </Tip>
</Aside>

Asking for another sleep simply replaces the pending one. Each session has at most **one** pending sleep.

## Sleep vs scheduled tasks

Both wait for the future, but they are not the same thing:

|               | `kimaki_sleep`                      | [`--send-at`](/docs/features/scheduled-tasks) |
| ------------- | ----------------------------------- | --------------------------------------------- |
| Who starts it | The agent, mid-conversation         | You, from the CLI                             |
| What resumes  | **This** session, with full context | A **new** prompt                              |
| Repeats       | No, one-shot                        | Cron supported                                |
| Best for      | "check back after the deploy"       | digests, reminders, recurring jobs            |

Use **sleep** when the current conversation must continue later. Use **scheduled tasks** when you want fresh work to start on a timer.

## Limits

* Only the **main thread session** can sleep. Subagents cannot.
* Wake granularity is about **5 seconds**, so very short sleeps can overshoot slightly.
* The agent should call `kimaki_sleep` **last**, after its text, and stop there. The tool result is not a wake. Work continues only after Kimaki posts the **wake message**.
* If the thread is **deleted** before the wake, the sleep is dropped instead of retried.


---
title: Voice Messages
url: "https://kimaki.dev/docs/features/voice.md"
description: "Record a voice message in Discord and Kimaki transcribes it using your project's file tree for accuracy."
---

Record a **voice message** in Discord and Kimaki transcribes it, then processes it as a normal prompt. Speaking is often faster than typing a long instruction, especially from your phone.

## How it works

```diagram
  voice ──▶ transcription ──▶ "📝 Transcribed" ──▶ session
              ▲
              │ project file tree for accuracy
```

1. You record a voice note in the thread.
2. Kimaki sends the audio to a transcription model.
3. The transcribed text appears in the thread prefixed with **📝 Transcribed message:** and is sent to the agent.

## Accuracy from your project file tree

Transcription of code is hard: file names and function names are not normal words. Kimaki improves accuracy by feeding the **project's file tree** into the transcription prompt, so when you say a file path or a function name, the model recognizes it from your actual codebase.

<Aside>
  <Tip>
    Say file and function names naturally. Because the transcriber sees your file list, "open thread session runtime" is far more likely to come back as `thread-session-runtime.ts`.
  </Tip>
</Aside>

## Models and setup

Kimaki auto-detects the provider from the API key you configure for audio:

* **OpenAI** key (starts with `sk-`) → uses `gpt-audio`.
* **Gemini** key → uses `gemini-2.5-flash`.

You'll be prompted for an audio API key during setup, or you can add one later from Discord when you first send a voice message.

## Sending audio back

Kimaki can also speak: when you ask for audio of some text, the agent can generate speech with `kimaki tts` and post it to the thread. See the [Commands reference](/docs/reference/commands) for the `tts` and `upload-to-discord` CLI subcommands.


---
title: Images
url: "https://kimaki.dev/docs/features/images.md"
description: "Attach images to your messages and see images the agent produces, displayed inline in Discord."
---

Kimaki handles images in **both directions**: attach images to your message for the agent to look at, and see images the agent produces rendered inline in the thread.

## Sending images to the agent

Attach one or more images (or PDFs) to a Discord message. Kimaki downloads each one, resizes it if needed, and sends it to the session as inline content the model can actually see.

```diagram
  Discord attachment (png/jpg/pdf)
         │ download + resize
         ▼
  inline image ──▶ OpenCode session ──▶ model reads it
```

This is great for **screenshots of bugs**, **design mockups**, **error dialogs**, or **diagrams** you want the agent to implement or debug.

<Aside>
  <Note>
    Images are sent as inline content, so the agent does not need to use a file-reading tool to view them; it sees them as part of your message.
  </Note>
</Aside>

## Seeing images from the agent

When the agent produces image files (screenshots, generated diagrams, rendered output), it can upload them back into the thread. Sending multiple files in one message makes Discord display them in a **grid**, so a set of screenshots shows up neatly together.

The agent uploads files with the bundled CLI:

```bash
kimaki upload-to-discord --session <session-id> screenshot-1.png screenshot-2.png
```

## Common workflows

* **Bug from a screenshot** — paste a screenshot of the broken UI and say "fix this".
* **Implement a design** — attach a mockup and ask the agent to build it.
* **Visual verification** — ask the agent to screenshot a running app (via a browser tool) and post the result back so you can confirm it on your phone.

For exposing a live dev server to view in a real browser instead of static screenshots, see [Tunnels](/docs/remote-access/tunnels).


---
title: OpenCode Commands
url: "https://kimaki.dev/docs/features/opencode-commands.md"
description: "Your OpenCode commands, skills, and MCP prompts become Discord slash commands automatically."
---

Kimaki registers your project's **OpenCode commands, skills, and MCP prompts** as Discord slash commands. Anything you can invoke in OpenCode, you can invoke from Discord.

## How names map

Kimaki adds a suffix so each kind of command is distinct and avoids colliding with Kimaki's own commands:

```diagram
  OpenCode command "deploy"     ──▶ /deploy-cmd
  skill "analysis"              ──▶ /analysis-skill
  MCP prompt "git-commit"       ──▶ /git-commit-mcp-prompt
```

| Source     | Suffix        | Example slash command    |
| ---------- | ------------- | ------------------------ |
| Command    | `-cmd`        | `/deploy-cmd`            |
| Skill      | `-skill`      | `/analysis-skill`        |
| MCP prompt | `-mcp-prompt` | `/git-commit-mcp-prompt` |

Names are lowercased and sanitized to fit Discord's 32-character limit, with the suffix preserved. The reserved `init` command is never registered.

## Passing arguments

Each generated slash command takes an optional **`arguments`** field, passed through to the underlying OpenCode command:

```
/deploy-cmd arguments: --staging
```

Run in a project channel, the command starts a new thread; run in a thread, it continues that session.

## Triggering commands programmatically

You can also invoke a registered command through `kimaki send` by starting the prompt with `/commandname`:

```bash
kimaki send --thread <thread-id> --prompt '/review fix the auth module'
```

If the name matches a registered command it runs as that command; otherwise the prompt is sent as plain text. See [CI & Automation](/docs/guides/ci-automation) for more on `kimaki send`.

## Refreshing the command list

After adding or changing OpenCode commands, skills, or MCP servers, **restart Kimaki** so it re-registers the Discord slash commands. The `/restart-opencode-server` command also re-registers commands without a full restart.

<Aside>
  <Tip>
    Skills you add to a project show up as `/<name>-skill`, so you can trigger a whole skill workflow from a single Discord command.
  </Tip>
</Aside>


---
title: Shell Commands
url: "https://kimaki.dev/docs/features/shell-commands.md"
description: "Prefix a Discord message with ! to run a shell command directly in the project directory."
---

Prefix any Discord message with **`!`** to run it as a shell command in the project directory, no AI session involved. It's the fastest way to peek at state, run a quick script, or check git status.

## Using the `!` prefix

```diagram
  "!git status" ──▶ strips "!" ──▶ runs in dir ──▶ output in code block
```

Type a message starting with `!`. Kimaki strips the `!`, runs the rest as a shell command, posts a `Running ...` placeholder, then edits it with the result:

```
!ls -la
```

The reply shows the command, its exit code, and the output in a code block (truncated if very long).

## Where it runs

* **In a thread** with a [worktree](/docs/features/worktrees) that is ready, the command runs in the **worktree directory**.
* **Otherwise** it runs in the channel's **project directory**.

This means `!` commands always run in the same place the agent is working.

<Aside>
  <Note>
    While a worktree is still being created, `!` commands are skipped so they don't accidentally run in the base directory.
  </Note>
</Aside>

## The slash command equivalent

`/run-shell-command <command>` does exactly the same thing as the `!` prefix. The `!` prefix is just a shortcut.

## What it's good for

* **`!git status`**, **`!git log --oneline -5`** — quick git checks.
* **`!ls`**, **`!cat package.json`** — inspect files without spending an agent turn.
* **`!pnpm test`** — kick off a one-off command directly.

For long-running servers you want to view remotely, use [Tunnels](/docs/remote-access/tunnels) instead.


---
title: Browser VS Code
url: "https://kimaki.dev/docs/remote-access/vscode.md"
description: "Open the current project or worktree in a full VS Code editor in your browser, tunneled from your machine."
---

The **`/vscode`** command opens the current project (or its [worktree](/docs/features/worktrees)) in a real **VS Code editor in your browser**, served from your machine through a private tunnel. Make a quick edit from your phone or any browser without leaving Discord.

## Using it

Run **`/vscode`** in a project channel or a thread. Kimaki:

1. Launches a browser VS Code server (Coderaft) pointed at the working directory.
2. Opens a **private tunnel** on `kimaki.dev` to that server.
3. Replies with the editor URL (ephemeral, only you see it).

```diagram
   /vscode ──▶ browser VS Code on 127.0.0.1 ──▶ private tunnel ──▶ https://<id>.kimaki.dev
                                                                          │
                                                                          ▼
                                                          edit files from any browser
```

The URL points at the **same directory the agent works in**: the worktree directory when the thread has a ready worktree, otherwise the channel's project directory.

## Auto-stop after 30 minutes

Each VS Code session **auto-stops after 30 minutes** to avoid leaving an editor exposed. The start reply includes a **Stop VS Code** button if you want to end it sooner. Running `/vscode` again while a session is active returns the existing URL (with the same Stop button) instead of starting a second one.

<Aside>
  <Warning>
    The tunnel URL is unguessable but **publicly reachable** for its lifetime. Treat it like a temporary secret; do not share it, and let it expire when you're done.
  </Warning>
</Aside>

## When to use it vs alternatives

| Want to...                             | Use                                                  |
| -------------------------------------- | ---------------------------------------------------- |
| Make a manual edit in a real editor    | **`/vscode`**                                        |
| View a running dev server in a browser | [Tunnels](/docs/remote-access/tunnels)               |
| See your desktop screen and control it | [Screen Sharing](/docs/remote-access/screen-sharing) |
| Have the agent make the change for you | just send a message                                  |


---
title: Tunnels
url: "https://kimaki.dev/docs/remote-access/tunnels.md"
description: Expose a local dev server to a public URL so you can view it on your phone or another machine.
---

The **`kimaki tunnel`** command exposes a local port as a public HTTPS URL. The user driving the bot from Discord is not at the terminal, so `localhost:3000` is useless to them; a tunnel gives them a real URL to open.

## Basic usage

Wrap your dev server command so the local port is detected automatically:

```bash
kimaki tunnel -- pnpm dev
```

Or tunnel an already-running service on a known port:

```bash
kimaki tunnel --port 3000
```

```diagram
  pnpm dev ──▶ tunnel ──▶ https://<id>.kimaki.dev ──▶ open anywhere
```

## Passing the public URL to your app

`kimaki tunnel` injects **`TRAFORO_URL`** into the child process. Wire your app to that variable so OAuth callbacks, webhook URLs, and absolute links use the public tunnel instead of localhost:

```bash
# Next.js
kimaki tunnel -- sh -c 'APP_URL=$TRAFORO_URL exec pnpm dev'

# better-auth
kimaki tunnel -- sh -c 'BETTER_AUTH_URL=$TRAFORO_URL exec pnpm dev'

# Vite
kimaki tunnel -- sh -c 'VITE_BASE_URL=$TRAFORO_URL exec pnpm dev'
```

<Aside>
  <Tip>
    When launching a dev server through `kimaki tunnel -- ...`, the local port is auto-detected from the process output. Only pass `--port` if detection fails.
  </Tip>
</Aside>

## Options

| Option                 | Description                                                                         |
| ---------------------- | ----------------------------------------------------------------------------------- |
| `-p, --port <port>`    | Local port to expose (optional when the command output reveals it)                  |
| `-t, --tunnel-id [id]` | Custom tunnel ID (use only for services safe to expose publicly; default is random) |
| `-h, --host [host]`    | Local host (default: localhost)                                                     |
| `-k, --kill`           | Kill any existing process on the port before starting                               |

<Aside>
  <Note>
    Use a **random** tunnel ID by default. Only pass `-t` to pick a stable, guessable ID when the service is safe to be publicly discoverable.
  </Note>
</Aside>

## Running it in the background

Keep the tunnel + dev server alive across commands using a background session manager so you can read logs and grab the URL later:

```bash
tuistory launch "kimaki tunnel -- pnpm dev" -s myapp-dev
tuistory read -s myapp-dev   # find the printed tunnel URL
```


---
title: Screen Sharing
url: "https://kimaki.dev/docs/remote-access/screen-sharing.md"
description: "Share your machine's screen to anyone with a browser link via Kimaki."
---

Share your machine's screen to anyone with a browser link. Uses VNC under the hood, bridged through a WebSocket proxy and exposed via a kimaki tunnel.

```bash
# Start sharing (runs in foreground, Ctrl+C to stop)
kimaki screenshare

# Run in background with tuistory
tuistory launch "kimaki screenshare" -s screenshare
```

Or use the `/screenshare` slash command in Discord — it posts a private URL with a **Stop screen share** button.

Sessions auto-stop after **30 minutes**. Use the Stop button on the start reply, or Ctrl+C for the CLI, to stop earlier.

## macOS Setup

macOS requires **Remote Management** enabled (not just Screen Sharing) for full mouse and keyboard control:

1. Go to **System Settings > General > Sharing > Remote Management**
2. Enable **"VNC viewers may control screen with password"**
3. Set a VNC password

Or via terminal:

```bash
sudo /System/Library/CoreServices/RemoteManagement/ARDAgent.app/Contents/Resources/kickstart \
  -activate -configure -allowAccessFor -allUsers -privs -all \
  -clientopts -setvnclegacy -vnclegacy yes \
  -restart -agent -console
```

## Linux Setup

Requires `x11vnc` and a running X11 display (`$DISPLAY`):

```bash
sudo apt install x11vnc
```

Kimaki spawns `x11vnc` automatically when you start screen sharing.


---
title: Expose the OpenCode server on a VPS
url: "https://kimaki.dev/docs/remote-access/opencode-server.md"
description: "Bind Kimaki's OpenCode server to 0.0.0.0 on a VPS, lock it with a password, and attach the OpenCode TUI from your laptop."
---

Kimaki starts one shared **OpenCode server**. By default that server listens on **`127.0.0.1`**, so only processes on the same machine can reach it.

On a **VPS** you often want the laptop TUI, `opencode web`, or `opencode attach` to talk to that same server. Bind it to **`0.0.0.0`**, pick a stable **port**, and set a **password**.

```bash
OPENCODE_SERVER_PASSWORD=replace-me \
  kimaki --opencode-hostname 0.0.0.0 --opencode-port 4096
```

Then from your laptop:

```bash
opencode attach http://YOUR_VPS_IP:4096 --password replace-me
```

```diagram
  VPS                                              Laptop
 ┌────────────────────────────────────┐           ┌─────────────────────┐
 │  kimaki                            │           │                     │
 │  --opencode-hostname 0.0.0.0       │           │  opencode attach    │
 │  --opencode-port 4096              │           │  http://VPS:4096    │
 │  OpenCode serve                    │◄──────────│  --password secret  │
 │  0.0.0.0:4096 + basic auth         │           │                     │
 └────────────────────────────────────┘           └─────────────────────┘
```

These flags bind **only the OpenCode child**. Kimaki's own lock/hrana server stays on **`127.0.0.1`** unless you set `KIMAKI_INTERNET_REACHABLE_URL`.

<Aside>
  <Warning>
    **Never bind `0.0.0.0` without a password.** The OpenCode HTTP API can start sessions, run shell commands, and edit files. Kimaki refuses to start if `--opencode-hostname` is not loopback and `OPENCODE_SERVER_PASSWORD` is missing.
  </Warning>
</Aside>

## Password

OpenCode uses **HTTP basic auth**. Kimaki already forwards these env vars to the server it starts, and the Kimaki SDK client sends the matching `Authorization` header.

| Variable                   | Role                                                                         | Default    |
| -------------------------- | ---------------------------------------------------------------------------- | ---------- |
| `OPENCODE_SERVER_PASSWORD` | Enables basic auth. **Required** when `--opencode-hostname` is not loopback. | unset      |
| `OPENCODE_SERVER_USERNAME` | Basic-auth username                                                          | `opencode` |

```bash
OPENCODE_SERVER_PASSWORD=replace-me \
OPENCODE_SERVER_USERNAME=opencode \
  kimaki --opencode-hostname 0.0.0.0 --opencode-port 4096
```

Attach with the same values:

```bash
opencode attach http://YOUR_VPS_IP:4096 \
  --username opencode \
  --password replace-me
```

`--password` and `--username` on `opencode attach` also read `OPENCODE_SERVER_PASSWORD` and `OPENCODE_SERVER_USERNAME` if you omit the flags.

## Flags

| Flag                         | What it does                                                                                                            |
| ---------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `--opencode-hostname <host>` | Address OpenCode listens on. Default is OpenCode's `127.0.0.1`.                                                         |
| `--opencode-port <port>`     | Port OpenCode listens on. Default is a **random free port**. Use a fixed port on a VPS so you can attach after restart. |

Kimaki itself still talks to the server at **`127.0.0.1`**, even when the process binds `0.0.0.0`. That is correct: `0.0.0.0` means "listen on every interface", including localhost.

## Docker on a VPS

Publish the port and pass the password into the container. Add the flags to `CMD`.

```yaml
services:
  kimaki:
    build: .
    restart: unless-stopped
    ports:
      - "4096:4096"
    environment:
      NODE_ENV: production
      KIMAKI_DATA_DIR: /data
      OPENCODE_SERVER_PASSWORD: ${OPENCODE_SERVER_PASSWORD}
    command:
      [
        "kimaki",
        "--gateway",
        "--data-dir",
        "/data",
        "--projects-dir",
        "/data/projects",
        "--auto-restart",
        "--opencode-hostname",
        "0.0.0.0",
        "--opencode-port",
        "4096",
      ]
    volumes:
      - kimaki-data:/data

volumes:
  kimaki-data:
```

Set the secret on the host before `docker compose up`:

```bash
export OPENCODE_SERVER_PASSWORD=replace-me
docker compose up -d
```

Do not commit the password. Put it in a `.env` file next to `docker-compose.yml`, or in your process supervisor.

The full container setup is in [Deploy Kimaki on a VPS with Docker](/docs/guides/docker).

## Firewall

Open **only** the OpenCode port you chose. Do not expose Kimaki's lock port (`KIMAKI_LOCK_PORT`, default `29988`).

```bash
# example: allow 4096 from your laptop only
ufw allow from YOUR_LAPTOP_IP to any port 4096 proto tcp
```

## Related

* [OpenCode server docs](https://opencode.ai/docs/server/)
* [OpenCode CLI attach](https://opencode.ai/docs/cli/)
* [Docker on a VPS](/docs/guides/docker)


---
title: Access Control
url: "https://kimaki.dev/docs/configuration/access-control.md"
description: "Decide who can trigger Kimaki, block specific users, and require mentions in busy channels."
---

Kimaki checks Discord permissions before processing **any** message, so only trusted people can run agents on your machine. You control access with roles and a per-channel mention mode.

## Who can trigger Kimaki

A user needs **one** of these to use the bot:

* **Server Owner**
* **Manage Server** permission
* **Administrator** permission
* the **"Kimaki" role** (case-insensitive)

Messages from anyone without one of these are silently ignored.

```diagram
   message ──▶ has Owner / Manage Server / Admin / "Kimaki" role?
                       │ yes                         │ no
                       ▼                             ▼
                 process message                 ignore silently
```

The **"Kimaki" role** is the recommended way to grant team access: create a role named `Kimaki` and assign it to trusted members. No elevated Discord permissions required.

## Blocking users

Create a role named **"no-kimaki"** (case-insensitive) to block specific users, **even server owners**. This is useful in shared servers to prevent accidental triggers from people who otherwise have admin rights.

<Aside>
  <Tip>
    Use a dedicated Discord server for your agents. It keeps coding sessions isolated and gives you full control over who has the Kimaki role.
  </Tip>
</Aside>

## Letting other bots trigger Kimaki

Other Discord bots are **ignored by default**. To build multi-agent orchestration where another bot drives Kimaki, assign the **"Kimaki" role to that bot**. Then its messages are treated like a trusted user's.

## Mention mode for busy channels

By default the bot responds to every message in a configured channel. In a busy or shared channel that can be noisy. Enable **mention mode** so the bot only starts a new session when it is **@mentioned**:

```
/toggle-mention-mode
```

* When **enabled**, only `@Kimaki ...` messages start new sessions in that channel.
* Messages **inside existing threads are unaffected**; they always continue their session without a mention.

Run the command again to toggle it back off.


---
title: Tool Permissions
url: "https://kimaki.dev/docs/configuration/permissions.md"
description: "Approve or deny risky tool calls from Discord, protect specific directories, and lock down sessions."
---

Kimaki surfaces OpenCode's **permission system** in Discord. When the agent tries something that needs approval, you get buttons in the thread. You can also pre-configure defaults and restrict individual sessions.

## Approval buttons

When the agent attempts a tool call that requires permission (a shell command, accessing files outside the project, etc.), Kimaki posts three buttons in the thread:

```diagram
   agent wants to run a command
            │
            ▼
   ┌──────────┐  ┌────────────────┐  ┌────────┐
   │  Accept  │  │ Accept Always  │  │  Deny  │
   └──────────┘  └────────────────┘  └────────┘
        │               │                │
     run once      remember rule      reject + tell agent
```

* **Accept** — allow this one call.
* **Accept Always** — allow and remember the rule for the session so similar calls don't ask again.
* **Deny** — reject the call; the agent is told and continues.

<Aside>
  <Note>
    OpenCode fixes a permission's scope when it asks. A reply can allow or deny, but it cannot widen the requested pattern.
  </Note>
</Aside>

## Directory access

**Every directory is allowed by default.** The agent can read and edit files outside the project without asking. OpenCode's own default is to ask for each external folder, which turned ordinary reads into a stream of approval prompts.

To protect specific folders, add `deny` or `ask` rules to your `opencode.json`. Your project config is merged **after** Kimaki's defaults and the last matching rule wins, so your rules take priority:

```json
{
  "$schema": "https://opencode.ai/config.json",
  "permission": {
    "external_directory": {
      "~/.ssh": "deny",
      "~/.ssh/*": "deny",
      "~/Documents/*": "ask"
    }
  }
}
```

Kimaki writes `"external_directory": { "*": "allow" }`, so the keys above merge on top of that wildcard. Paths you don't list stay allowed.

To flip the default back to a strict allow-list, start the bot with **`--restrict-directories`**:

```bash
kimaki --restrict-directories
```

The agent is then limited to the session working directory plus a few known-safe paths (`/tmp`, `~/.config/opencode`, `~/.opensrc`, `~/.kimaki`, and common toolchain caches). Anything else raises an approval prompt in the thread.

<Aside>
  <Note>
    Worktree threads deny the original checkout, with or without the flag. This keeps the agent from editing the main repo after the thread moved to a worktree. It is a session-level rule, so it also beats your `opencode.json`. The only way to override it is an explicit `--permission 'external_directory:allow'` on that session.
  </Note>
</Aside>

## Default permissions in opencode.json

Set project-wide defaults in your `opencode.json`. See the [OpenCode Permissions docs](https://opencode.ai/docs/permissions/) for the full schema, pattern matching, and per-agent overrides.

Agent files can also carry permissions, which is handy for an auto-allow agent that never stalls on prompts:

```yaml
---
description: Build agent that never stalls on prompts
mode: primary
permission:
  question: allow
  plan_enter: allow
---
```

## Locking down a single session

When starting a session with `kimaki send`, restrict tools for **that session only** with repeatable `--permission` rules:

```bash
# Read-only review session
kimaki send -c <channel-id> -p 'Review this code' \
  --permission 'bash:deny' \
  --permission 'edit:deny'
```

Rules are evaluated last-match-wins. This is ideal for CI, scheduled tasks, or sandboxed runs. See [CI & Automation](/docs/guides/ci-automation) for the full `--permission` reference.


---
title: Verbosity
url: "https://kimaki.dev/docs/configuration/verbosity.md"
description: "Control how much tool activity Kimaki shows in a thread, from full tool output down to text only."
---

**Verbosity** controls how much of the agent's tool activity appears in a thread. Lower verbosity keeps threads readable; higher verbosity shows everything for debugging.

## The three levels

```diagram
   tools_and_text      ──▶  shows everything: edits, reads, searches, bash, status
   text_and_essential  ──▶  text + essential tools (edits, MCP); hides reads  (default)
   text_only          ──▶  only the agent's text replies; hides all tools
```

| Level                                                     | What it shows                                                                                    |
| --------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
| **Tools and text** (`tools_and_text`)                     | All output, including every tool execution and status message                                    |
| **Text and essential tools** (`text_and_essential_tools`) | Text plus essential tools (file edits, custom MCP tools). Hides reads and searches. **Default.** |
| **Text only** (`text_only`)                               | Only text responses. Hides all tools and status messages.                                        |

The default, **text and essential tools**, is tuned so you see the edits and meaningful actions without the noise of every file read and grep.

## Setting verbosity

Run **`/verbosity`** in a channel and pick a level from the dropdown. The setting is a **per-channel override** of the global default and **applies immediately**, including to sessions that are already running.

<Aside>
  <Note>
    Verbosity is stored per channel, so different projects can have different levels. The reply shows whether the current value is a channel override or the global default.
  </Note>
</Aside>

## What "essential tools" means

At the default level, Kimaki shows tool parts that matter for following the work:

* **File edits and writes** (prefixed `┣` or `◼︎` in the thread).
* **Custom MCP tools** you've configured.

It hides low-signal parts like file reads, searches, thinking, and non-side-effect bash. Bump to **tools and text** when you want to see exactly what the agent is doing step by step.


---
title: Memory
url: "https://kimaki.dev/docs/configuration/memory.md"
description: Persist learnings and context across sessions with a MEMORY.md file the agent reads and updates.
---

Kimaki gives the agent a **persistent memory** through a `MEMORY.md` file in your project root. The agent reads it at session start and can update it, so hard-won context survives across sessions.

## How MEMORY.md works

```diagram
   session starts ──▶ agent reads MEMORY.md ──▶ works with that context
                                                      │
                                  learns something worth keeping
                                                      ▼
                                       agent updates MEMORY.md ──▶ next session benefits
```

Put anything the agent should remember between sessions: architecture decisions, gotchas, naming conventions, where things live, commands that work. The next session picks up where the last one's knowledge left off, without you re-explaining.

## What to keep in it

* **Non-obvious learnings** that prevent repeated mistakes.
* **Project conventions** (naming, file layout, preferred libraries).
* **Commands and workflows** specific to the repo (test command, build steps).
* **Decisions and their rationale**, so future sessions don't undo them.

<Aside>
  <Tip>
    Keep entries concise: short titles and 2-3 sentences each. A long MEMORY.md is condensed into a line-numbered table of contents so the agent can jump to the relevant section instead of loading everything.
  </Tip>
</Aside>

## Context awareness reminders

Kimaki also injects small **awareness reminders** into a session when relevant, so the agent stays oriented as things change mid-session:

* **Branch / detached HEAD changes** (for example after switching branches).
* **Working directory changes** (for example after [`/new-worktree`](/docs/features/worktrees) moves the session into a worktree).
* A **MEMORY.md reminder** after a large reply, nudging the agent to record anything worth keeping.

These keep the agent's understanding of its environment current without you having to spell it out.

## Tips

* Commit `MEMORY.md` to the repo so memory is shared across machines and teammates.
* Ask the agent directly to "update MEMORY.md with what you just learned" at the end of a tricky session.
* Pair it with [the queue](/docs/features/queue): queue "update MEMORY.md with the key takeaways" to run when a session finishes.


---
title: MCP Servers
url: "https://kimaki.dev/docs/configuration/mcp.md"
description: "List and toggle Model Context Protocol servers for a project, straight from Discord."
---

The **`/mcp`** command lets you list and toggle **MCP (Model Context Protocol) servers** for the current project without touching config files. MCP servers give the agent extra tools and data sources.

## Using it

Run **`/mcp`** in a project channel. Kimaki shows the configured servers and their status, with a menu to connect or disconnect each one.

```diagram
   /mcp ──▶ list configured MCP servers ──▶ pick one ──▶ connect / disconnect
```

* **Connect** — start the server so its tools become available to the agent.
* **Disconnect** — stop it; its tools are removed.

## Project-scoped, no extra storage

MCP state is **project-scoped** (per channel), not per thread or session. The state lives in **OpenCode's own config**, so toggling from Discord changes the same configuration OpenCode uses everywhere. There's no separate Kimaki database for it.

<Aside>
  <Note>
    MCP servers are configured in your OpenCode config. The `/mcp` command manages the ones already defined there; add new servers in your `opencode.json`.
  </Note>
</Aside>

## MCP prompts as slash commands

If an MCP server exposes **prompts**, Kimaki registers them as Discord slash commands with the `-mcp-prompt` suffix (for example `/git-commit-mcp-prompt`). See [OpenCode Commands](/docs/features/opencode-commands) for how commands, skills, and MCP prompts map to slash commands.


---
title: Managing Projects
url: "https://kimaki.dev/docs/configuration/projects.md"
description: "Add, create, and remove the Discord channels that map to project directories on your machine."
---

A **project** is a directory on your machine linked to a Discord channel. These commands manage that mapping. For the underlying model, see [Channels & Threads](/docs/core-concepts/channels-threads).

## Adding an existing project

Link a directory that already exists on your machine.

```diagram
   /add-project <project>   ──▶ pick a recent OpenCode project ──▶ creates its channel
   npx kimaki project add   ──▶ add the current (or any) directory from the CLI
```

* **`/add-project <project>`** — autocompletes recent OpenCode projects and creates Discord channels for the one you pick. Run it in the server where you want the channels.
* **CLI** for directories not in the recent list:

```bash
# Add the current directory
npx -y kimaki project add

# Add a specific directory
npx -y kimaki project add /code/web-app

# Specify the server when the bot is in multiple
npx -y kimaki project add ./web-app --guild 123456789
```

## Creating a brand new project

**`/create-new-project <name>`** creates a new project folder, initializes git, sets up the channel, and starts a session, all in one step. Great for spinning up a fresh repo entirely from Discord.

<Aside>
  <Tip>
    New folders are created under Kimaki's projects directory. Run multiple isolated bots with separate projects directories using `--data-dir`; see [Advanced Setup](/docs/guides/advanced-setup).
  </Tip>
</Aside>

## Removing a project

**`/remove-project <project>`** removes the Discord channels for a project. The directory on your machine is left untouched; only the channel mapping is deleted.

From the CLI you can remove individual channel mappings by channel ID (useful for cleaning duplicates without deleting the Discord channel):

```bash
npx -y kimaki project remove <channel-id>
```

## Listing projects

From the CLI:

```bash
npx -y kimaki project list
```

This shows every directory currently registered with the bot, including the guild (server) name each channel belongs to. The `--json` flag outputs machine-readable data with `guild_id` and `guild_name` fields for disambiguation when the same project is registered on multiple servers.


---
title: Troubleshooting
url: "https://kimaki.dev/docs/guides/troubleshooting.md"
description: How to fix common transient issues with Kimaki.
---

Most Kimaki issues are transient and caused by the underlying OpenCode server getting into a bad state. The single most effective fix is **restarting the OpenCode server**.

## `/restart-opencode-server`

This is the first thing to try when something goes wrong. Run it in any channel or thread.

**What it fixes:**

* Sessions not responding or stuck
* New sessions failing to start
* Auth state not refreshing after `/login`
* Slash commands not appearing or missing new agents
* Plugins not loading after config changes
* OpenCode server crashes or hangs

**What it does:**

1. Aborts any in-progress sessions in the current channel
2. Restarts the shared OpenCode server process
3. Re-registers all Discord slash commands (built-in, user commands, agents)

Other channels reconnect automatically through the listener backoff loop once the server comes back up.

<Note title="Note">
  This restarts the **OpenCode server**, not the Kimaki bot itself. Your bot stays connected to Discord the entire time. Only the backend that runs AI sessions is restarted.
</Note>

## `/upgrade-and-restart`

If `/restart-opencode-server` does not fix the issue, the problem is likely in Kimaki itself rather than the OpenCode server. Run `/upgrade-and-restart` to update Kimaki to the latest npm release and restart the bot process entirely. This replaces the running code with the newest version, fixing bugs in message handling, Discord event routing, session lifecycle, command registration, and anything else in the bot layer.

**What it fixes (on top of `/restart-opencode-server`):**

* Bot crashes or unhandled exceptions
* Discord event handling bugs (messages not being picked up, interactions failing)
* Session lifecycle issues (threads not created, footers stuck, typing indicator stuck)
* Command registration failures
* Memory leaks or degraded performance over long runs
* Any bug that was fixed in a newer Kimaki release

You can also upgrade from the terminal:

```bash
kimaki upgrade
```

## Common symptoms and fixes

| Symptom                                         | Fix                                                         |
| ----------------------------------------------- | ----------------------------------------------------------- |
| Session stuck, bot not responding               | `/restart-opencode-server`                                  |
| "Failed to create session" errors               | `/restart-opencode-server`                                  |
| Model change not taking effect                  | `/restart-opencode-server`                                  |
| Missing slash commands after config change      | `/restart-opencode-server`                                  |
| Auth expired or provider errors after `/login`  | `/restart-opencode-server`                                  |
| Bot crashes or throws unhandled errors          | `/upgrade-and-restart`                                      |
| Threads not being created or messages ignored   | `/upgrade-and-restart`                                      |
| Typing indicator stuck, footers missing         | `/upgrade-and-restart`                                      |
| Issue persists after `/restart-opencode-server` | `/upgrade-and-restart`                                      |
| Bot completely offline                          | Check the terminal where `kimaki` is running                |
| Messages stop arriving when lid is closed       | [Disable sleep globally](#macbook-sleeping-with-lid-closed) |

## MacBook sleeping with lid closed

If you run Kimaki on a MacBook and close the lid, macOS puts the machine to sleep by default. This means Discord messages stop arriving and sessions go unresponsive.

`caffeinate` only prevents **idle sleep**; it does not block the hardware sleep triggered by closing the lid.

### Fix: disable sleep globally

```bash
sudo pmset -a disablesleep 1
```

This prevents sleep entirely, including on lid close. To re-enable later:

```bash
sudo pmset -a disablesleep 0
```

### Fix: System Settings (power adapter only)

Go to **System Settings > Battery > Options** and enable **"Prevent automatic sleeping on power adapter when the display is off"**. This only works when plugged in and may not be enough on its own for lid-close scenarios.

### Fix: Amphetamine (GUI alternative)

[Amphetamine](https://apps.apple.com/app/amphetamine/id937984704) is a free Mac App Store app with an explicit **"Allow system sleep when display is closed"** toggle. Disable that toggle to keep the Mac awake with the lid shut.

<Warning title="Warning">
  Running a MacBook with the lid closed traps heat. Keep it on a flat surface with good airflow; never put it in a bag while awake.
</Warning>

## Debugging with logs

If the issue persists after a restart, check the log file:

```bash
# default location
cat ~/.kimaki/kimaki.log
```

The log file is reset on every bot startup, so it only contains logs from the current run. Look for error lines near the bottom for the most recent failures.

To file a GitHub issue with session jsonl, logs, and the model id, follow [Debug sessions and report bugs](/docs/guides/report-bugs).

## Live CPU profiling

To see where the running bot spends CPU time, type this in the **same terminal** as Kimaki and press Enter:

```
cpuprof
```

Type `cpuprof` again to stop, or wait **20 seconds**. Kimaki writes a Chrome DevTools `.cpuprofile` file to `~/.kimaki/cpu-profiles/` (or `<data-dir>/cpu-profiles/` if you passed `--data-dir`).

```bash
# top functions by self-time
bunx profano ~/.kimaki/cpu-profiles/cpu-*.cpuprofile
```

This does not restart the bot. Heap snapshots still use `kill -SIGUSR1 <PID>`.

## Restarting the bot process

In rare cases where the bot itself is unresponsive (not just OpenCode), you can restart the entire process:

**From the terminal:**

```bash
# find the process
ps aux | grep kimaki

# send restart signal
kill -SIGUSR2 <PID>
```

The bot waits 1 second and restarts with the same arguments. Your Discord connection is briefly interrupted but reconnects automatically.


---
title: Debug sessions and report bugs
url: "https://kimaki.dev/docs/guides/report-bugs.md"
description: "Export session events, share evidence in a secret gist, and open a Kimaki GitHub issue with ground truth. Never open a pull request unless remorses agrees."
---

Export the session, put the evidence in a **secret gist**, then open a GitHub issue. Do not open a pull request.

Agents must **read this page first** before filing anything on [remorses/kimaki](https://github.com/remorses/kimaki).

```diagram
  /session-id
       │
       v
  export jsonl ──> jq ──> ground truth (events, not guesses)
       │
       v
  kimaki.log + prompts + model id
       │
       v
  secret gist ──> jsonl + log + session markdown
       │
       v
  gh issue create (body links gist)
       │
       v
  wait for remorses ──> PR only if he agrees in a comment
```

## Collect ground truth

Do not describe what you think happened. Quote what the files show.

1. Get the **session id** with `/session-id` in the Discord thread, or `kimaki session list`.
2. Export the **event stream**. This is the source of truth for session-state bugs (footer after abort, stuck typing, missed idle).
3. Copy **`kimaki.log`**. It resets on every bot restart, so grab it before you restart.
4. Record the **model id** and the **verbatim user prompts**. Do not paraphrase.

```bash
kimaki session export-events-jsonl --session ses_xxx --out ./tmp/ses_xxx.jsonl
kimaki session read ses_xxx > ./tmp/ses_xxx.md
kimaki --version
```

Kimaki keeps only the **last 1000 events per session** in SQLite, so export while the bug is fresh.

The log file is `~/.kimaki/kimaki.log` by default, or `<dataDir>/kimaki.log` if you passed `--data-dir`.

<Aside>
  <Note>
    The jsonl dump is only for Discord-mapped Kimaki sessions. `opencode run` sessions are not in that SQLite buffer. Use `kimaki session read` for those.
  </Note>
</Aside>

### Model id and prompts

`kimaki session read` includes the exact **`providerID/modelID`** on each assistant message and the verbatim user prompts. Copy both from `./tmp/ses_xxx.md`; do not rely on memory.

Before upload, **remove secrets** such as tokens, API keys, cookies, and unrelated private prompts. Keep event order, timestamps, model ids, and relevant error lines unchanged.

### Inspect the jsonl

Each line is `{ timestamp, threadId, projectDirectory, event }`. Read it before you write the issue.

```bash
# event type counts
jq -r '.event.type' ./tmp/ses_xxx.jsonl | sort | uniq -c

# lifecycle only
jq -r 'select(.event.type=="session.status" or .event.type=="session.idle" or .event.type=="session.error") | [.timestamp, .event.type, (.event.properties.status.type // ""), (.event.properties.error.name // "")] | @tsv' ./tmp/ses_xxx.jsonl

# timestamps + types
jq -r '[.timestamp, .event.type] | @tsv' ./tmp/ses_xxx.jsonl
```

If the stream does not contain the event you expected, say that. Do not invent a cause.

## Open an issue, never a PR

File issues on [github.com/remorses/kimaki/issues](https://github.com/remorses/kimaki/issues).

**Never open a pull request.** If you have a fix or a reproduction, put it on the issue. A PR is allowed only after **remorses** agrees in a comment on that issue.

### Share evidence in a gist

Put the **jsonl, log, session markdown, and reproduction code** in one secret gist. Paste the URL printed by `gh gist create` into the issue body.

A secret gist is **unlisted, not private**. Anyone with its URL can read it. Remove secrets and unrelated private data before upload. If the evidence cannot be public, do not upload it; state that private evidence is available to the maintainer.

```bash
# gists are secret by default; omit the reproduction file if you do not have one
gh gist create --desc "kimaki ses_xxx debug" \
  ./tmp/ses_xxx.jsonl \
  ./tmp/ses_xxx.md \
  ~/.kimaki/kimaki.log \
  ./tmp/reproduction.ts

# paste the returned gist URL into ./tmp/kimaki-issue.md, then create the issue
gh issue create -R remorses/kimaki \
  --title "Footer appears after abort in ses_xxx" \
  --body-file ./tmp/kimaki-issue.md
```

### Issue body

Keep it short. Use facts from the jsonl and log.

```markdown
**Session:** ses_xxx
**Model:** anthropic/claude-opus-4-6
**Kimaki version:** output of `kimaki --version`
**OS:** macOS 15.2
**Gist:** https://gist.github.com/...

**Steps to reproduce:**
1. ...
2. ...

**User prompts** (verbatim, in order):
1. ...
2. ...

**What the files show:**
- jsonl line N: `session.status` busy at <timestamp>
- jsonl line M: `session.idle` then a footer message was posted
- log: <exact error line>

**What I expected:**
No footer after abort.
```

Do not write "probably a race" or "might be Discord". If you do not have an event or a log line, say **unknown**.

## Reproduce with a test

Only if you have the Kimaki repo. Copy the exported jsonl into `cli/src/session-handler/event-stream-fixtures/` and extend `event-stream-state.test.ts`. That is how session-state bugs are locked down: pure derivation over a recorded stream.

```bash
kimaki session export-events-jsonl --session ses_xxx --out ./tmp/ses_xxx.jsonl
cp ./tmp/ses_xxx.jsonl cli/src/session-handler/event-stream-fixtures/ses_xxx.jsonl
```

Live event capture (optional, needs a bot restart or a test run):

```bash
KIMAKI_LOG_OPENCODE_SESSION_EVENTS=1 kimaki
# writes <dataDir>/opencode-session-events/ses_xxx.jsonl
```

Put the failing test on the **issue** (gist or snippet). Do not open a PR unless remorses asked for one.

## Related

* [Troubleshooting](/docs/guides/troubleshooting) for restarts and stuck sessions
* [Managing Sessions](/docs/core-concepts/sessions) for `/session-id` and `/share`
* [Commands reference](/docs/reference/commands) for `kimaki session` subcommands


---
title: "CI & Automation"
url: "https://kimaki.dev/docs/guides/ci-automation.md"
description: "Start Kimaki sessions from CI pipelines, cron jobs, or any automation. Includes GitHub Actions examples, scheduled tasks, and per-session permissions."
---

## Getting Your Bot Token

The `send` command authenticates with Discord using the `KIMAKI_BOT_TOKEN` environment variable. The value depends on which mode you're using.

Print your token with:

```bash
kimaki bot token
```

This prints the token to stdout so you can copy it or pipe it into a secret manager.

### Self-hosted mode

If you created your own Discord bot, the token is your **Discord bot token** (starts with a base64 app ID, like `MTIz...`). You can also find it in the [Discord Developer Portal](https://discord.com/developers/applications) under Bot > Token.

### Gateway mode

If you're using the shared Kimaki gateway bot (`kimaki --gateway`), the token is a **`clientId:clientSecret`** pair generated during onboarding. It looks like `a1b2c3d4-...:7f8e9d...`. This credential authenticates your kimaki instance against the gateway proxy; it is not a Discord bot token.

`kimaki bot token` prints the correct value in both modes. Store it as a repository secret and pass it as `KIMAKI_BOT_TOKEN` in CI.

## Programmatically Start Sessions

The `send` command creates a Discord thread with your prompt, and the running bot on your machine picks it up automatically.

### Environment Variables

| Variable           | Required    | Description                                              |
| ------------------ | ----------- | -------------------------------------------------------- |
| `KIMAKI_BOT_TOKEN` | Yes (in CI) | Bot token or gateway `clientId:clientSecret` (see above) |

### CLI Options

```bash
npx -y kimaki send \
  --channel <channel-id>    # Required: Discord channel ID
  --prompt <prompt>         # Required: Message content
  --name <name>             # Optional: Thread name (defaults to prompt preview)
  --app-id <app-id>         # Optional: Bot application ID for validation
  --notify-only             # Optional: Create notification thread without starting AI session
  --worktree <name>         # Optional: Create git worktree for isolated session
  --thread <thread-id>      # Optional: Send prompt to existing thread (no new thread)
  --session <session-id>    # Optional: Resolve thread from session and send prompt
  --permission <rule>       # Optional: Repeatable. Per-session permission rule (see below)
```

Use either `--channel/--project` (create new thread) or `--thread/--session` (send to existing thread), not both.

### Example: GitHub Actions on New Issues

This workflow starts a Kimaki session whenever a new issue is opened:

```yaml
# .github/workflows/investigate-issues.yml
name: Investigate New Issues

on:
  issues:
    types: [opened]

jobs:
  investigate:
    runs-on: ubuntu-latest
    steps:
      - name: Start Kimaki Session
        env:
          KIMAKI_BOT_TOKEN: ${{ secrets.KIMAKI_BOT_TOKEN }}
        run: |
          npx -y kimaki send \
            --channel '1234567890123456789' \
            --prompt 'Investigate issue ${{ github.event.issue.html_url }} using gh cli. Try fixing it in a new worktree ./${{ github.event.issue.number }}' \
            --name 'Issue #${{ github.event.issue.number }}'
```

**Setup:**

1. Add `KIMAKI_BOT_TOKEN` to your repository secrets (Settings > Secrets > Actions)
2. Replace `1234567890123456789` with your Discord channel ID (right-click channel > Copy Channel ID)
3. Make sure the Kimaki bot is running on your machine

### How It Works

1. **CI runs `send`** — Creates a Discord thread with your prompt
2. **Running bot detects thread** — Automatically starts a session
3. **Bot starts OpenCode session** — Uses the prompt from the thread
4. **AI investigates** — Runs on your machine with full codebase access

Use `--notify-only` for notifications that don't need immediate AI response (e.g., subscription events). Reply to the thread later to start a session with the notification as context.

## Add Project Channels

Create Discord channels for a project directory without starting a session:

```bash
# Add current directory as a project
npx -y kimaki project add

# Add a specific directory
npx -y kimaki project add /path/to/project

# Specify guild when bot is in multiple servers
npx -y kimaki project add ./myproject --guild 123456789

# In CI with env var for bot token
KIMAKI_BOT_TOKEN=xxx npx -y kimaki project add --app-id 987654321
```

| Option                  | Description                                                         |
| ----------------------- | ------------------------------------------------------------------- |
| `[directory]`           | Project directory path (defaults to current directory)              |
| `-g, --guild <guildId>` | Discord guild/server ID (auto-detects if bot is in only one server) |
| `-a, --app-id <appId>`  | Bot application ID (reads from database if available)               |

## Scheduled Tasks

Add `--send-at` to any `kimaki send` command to schedule it for later, using a one-time UTC ISO date or a recurring cron expression:

```bash
# Recurring: every Monday at 9am UTC
kimaki send --channel <channel-id> \
  --prompt 'Run weekly test suite and summarize failures' \
  --send-at '0 9 * * 1'
```

For the full scheduling workflow, including morning email digests, reminders, and recurring maintenance, see [Scheduled Tasks](/docs/features/scheduled-tasks).

## Per-Session Permissions

When starting sessions with `kimaki send`, you can restrict tools for that specific session using `--permission`. Useful for CI pipelines, scheduled tasks, or spawning sandboxed sessions.

Format: `tool:action` or `tool:pattern:action`. Actions: `allow`, `deny`, `ask`.

```bash
# Read-only session (no edits, no bash)
kimaki send -c 123 -p 'Review this code' \
  --permission 'bash:deny' \
  --permission 'edit:deny'

# Only allow git commands
kimaki send -c 123 -p 'Check git history' \
  --permission 'bash:git *:allow' \
  --permission 'bash:*:deny'

# Deny everything except reading
kimaki send -c 123 -p 'Analyze the codebase' \
  --permission '*:deny' \
  --permission 'read:allow' \
  --permission 'glob:allow' \
  --permission 'grep:allow'
```

Rules are evaluated with `findLast()` — later rules override earlier ones. The `--permission` flag works with `--send-at` (scheduled tasks) and `--thread`/`--session` (existing threads) too.

See the full [OpenCode Permissions documentation](https://opencode.ai/docs/permissions/) for all available permissions, granular pattern matching, and per-agent overrides.


---
title: Advanced Setup
url: "https://kimaki.dev/docs/guides/advanced-setup.md"
description: "Running multiple Kimaki instances, multiple Discord servers, and architecture details."
---

## Architecture: One Bot Per Machine

**Each bot instance is tied to one machine.** This is by design.

When you run `kimaki` on a computer, it spawns OpenCode servers for projects on that machine. The bot can only access directories on the machine where it's running.

To control multiple machines:

1. Create a separate Discord bot for each machine (or use gateway mode on each)
2. Run `kimaki` on each machine
3. Add all bots to the same Discord server

Each channel shows which bot (machine) it's connected to. You can have channels from different machines in the same server, controlled by different bots.

## Running Multiple Instances

By default, Kimaki stores its data in `~/.kimaki`. To run multiple bot instances on the same machine (e.g., for different teams or projects), use a separate `--data-dir` and optionally set `KIMAKI_LOCK_PORT` explicitly:

```bash
# Instance 1 - uses default ~/.kimaki
npx -y kimaki@latest

# Instance 2 - separate data directory + explicit lock port
KIMAKI_LOCK_PORT=31001 npx -y kimaki@latest --data-dir ~/work-bot

# Instance 3 - another separate instance
KIMAKI_LOCK_PORT=31002 npx -y kimaki@latest --data-dir ~/personal-bot
```

Each instance has its own:

* **Database** — Bot credentials, channel mappings, session history
* **Projects directory** — Where `/create-new-project` creates new folders
* **Lock port** — Derived from the data directory path by default; override with `KIMAKI_LOCK_PORT` when you need a specific port

This lets you run completely isolated bots on the same machine, each with their own Discord app and configuration.

## Multiple Discord Servers

A single Kimaki instance can serve multiple Discord servers. Install the bot in each server using the install URL shown during setup, then add project channels to each server.

### Method 1: Use `/add-project` command

1. Run `npx kimaki` once to set up the bot
2. Install the bot in both servers using the install URL
3. In **Server A**: run `/add-project` and select your project
4. In **Server B**: run `/add-project` and select your project

The `/add-project` command creates channels in whichever server you run it from.

### Method 2: Re-run CLI with `--add-channels`

1. Run `npx kimaki` — set up bot, install in both servers, create channels in first server
2. Run `npx kimaki --add-channels` — select projects for the second server

The setup wizard lets you pick one server at a time.

You can link the same project to channels in multiple servers — both will point to the same directory on your machine.

## Docker on a VPS

To run Kimaki on a remote machine instead of your laptop, put it in a container with a persistent volume. The first boot prints an install URL in the logs. See [Deploy Kimaki on a VPS with Docker](/docs/guides/docker).

To reach the OpenCode server from your laptop (`opencode attach`, `opencode web`), start Kimaki with `--opencode-hostname 0.0.0.0`, a fixed `--opencode-port`, and `OPENCODE_SERVER_PASSWORD`. See [Expose the OpenCode server](/docs/remote-access/opencode-server).


---
title: Deploy Kimaki on a VPS with Docker
url: "https://kimaki.dev/docs/guides/docker.md"
description: "Run Kimaki in a Docker container on any VPS. Persist credentials on a volume, copy the install URL from the logs, and authorize the shared bot."
---

Run Kimaki on a **VPS** the same way you run it on your laptop: one container, one persistent volume, **gateway mode**. You do not create a Discord bot. The shared Kimaki bot is installed into your server from a URL printed in the container logs.

```bash
docker compose up -d
docker compose logs -f
```

Look for a line that starts with `data: {"type":"install_url"`. Open that URL, pick your Discord server, click **Authorize**. Credentials stay on the volume, so later restarts skip this step.

## How it works

The container runs `kimaki --gateway`. On first boot Kimaki creates local credentials, prints an **install URL**, and waits for you to authorize the shared bot. After that it connects through the [gateway proxy](/docs/reference/gateway-architecture) and listens for Discord messages.

```diagram
  Your VPS                         Kimaki cloud                    Discord
 ┌──────────────────────────┐     ┌─────────────────────┐     ┌──────────────┐
 │  Docker                  │     │                     │     │              │
 │  kimaki --gateway        │────>│  gateway proxy      │────>│  your server │
 │                          │     │  kimaki.dev         │     │              │
 │  /data  (volume)         │     │                     │     │  channels    │
 │    discord-sessions.db   │     └─────────────────────┘     │  + threads   │
 │    projects/             │                                 └──────────────┘
 └──────────────────────────┘
```

<Aside>
  <Tip>
    A working image used by the public Kimaki demo lives in [`kimaki-demo/Dockerfile`](https://github.com/remorses/kimaki/blob/main/kimaki-demo/Dockerfile). The files below are the same idea, written for a normal VPS instead of Fly.io.
  </Tip>
</Aside>

## Dockerfile

Create a directory on the VPS, then add this **Dockerfile**. Pin a Kimaki version if you want reproducible deploys.

```dockerfile
FROM node:24-slim

RUN apt-get update && apt-get install -y git curl unzip \
  && rm -rf /var/lib/apt/lists/*

# bun is required by OpenCode
RUN curl -fsSL https://bun.sh/install | bash
ENV PATH="/root/.bun/bin:$PATH"

RUN npm install -g kimaki@latest

# Pre-install OpenCode so the first session does not hit GitHub rate limits
RUN curl -fsSL https://opencode.ai/install | bash
ENV PATH="/root/.opencode/bin:$PATH"

RUN mkdir -p /data/projects
WORKDIR /data

# --gateway: shared Kimaki bot, no Discord bot token
# --data-dir /data: sqlite DB and credentials on the volume
# --projects-dir /data/projects: folders created by /create-new-project
# --auto-restart: respawn the bot process after a crash
CMD ["kimaki", "--gateway", "--data-dir", "/data", \
     "--projects-dir", "/data/projects", "--auto-restart"]
```

<Aside>
  <Note>
    **git** is required. OpenCode uses it for worktrees, diffs, and project detection. Without it, sessions fail in confusing ways.
  </Note>
</Aside>

## docker-compose.yml

Pair the image with a **named volume**. That volume is the only state that must survive rebuilds.

```yaml
services:
  kimaki:
    build: .
    restart: unless-stopped
    volumes:
      - kimaki-data:/data
    environment:
      NODE_ENV: production
      KIMAKI_DATA_DIR: /data

volumes:
  kimaki-data:
```

Start it:

```bash
docker compose up -d --build
```

Give the machine about **2 GB RAM**. OpenCode plus the bot is tight on 1 GB once a session is running.

## Get the install URL from the logs

Docker has no TTY, so Kimaki prints structured **SSE** events on stdout. The first event is the install URL.

```bash
docker compose logs -f
```

Look for:

```
data: {"type":"install_url","url":"https://kimaki.dev/discord-install?clientId=...&clientSecret=..."}
```

Copy the `url` value, open it in a browser, select your Discord server, click **Authorize**.

```diagram
  docker compose up
          │
          v
  install_url in logs ──> open URL ──> Authorize ──> authorized ──> ready
```

| Event         | What it means                                            |
| ------------- | -------------------------------------------------------- |
| `install_url` | Open this URL once. It contains your credentials.        |
| `authorized`  | Discord install succeeded. A `guild_id` is now stored.   |
| `ready`       | The bot is connected and listening.                      |
| `error`       | Something failed. Read `message`. Restart and try again. |

<Aside>
  <Warning>
    The install URL contains your **client secret**. Do not paste it into a public channel. Authorization waits about **5 minutes**. If you miss that window, the credentials are already on the volume. Reprint the same URL with the command below; do not wipe `/data`.
  </Warning>
</Aside>

If the log scrolled away, reprint the URL from the same data directory:

```bash
docker compose exec kimaki kimaki discord-install-url --gateway --data-dir /data
```

For the full event protocol, including how a host process can parse these lines, see [Programmatic Gateway](/docs/guides/programmatic-gateway).

## After authorization

The bot creates a default **#kimaki** channel and a welcome thread. From Discord:

1. Run **`/login`** and connect Claude, Codex, or an API key. See [Models & Subscriptions](/docs/getting-started/subscriptions).
2. Run **`/create-new-project`** or **`/add-project`**. New folders land in `/data/projects`. See [Managing Projects](/docs/configuration/projects).

Credentials, channel mappings, and project files stay on the volume. Rebuilding the image does not log you out.

## Useful flags

Add these to `CMD` when they match how you want the VPS to behave.

| Flag                          | When to use it                                                                                                                                                                                  |
| ----------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--allow-all-users`           | Anyone in the Discord server can start sessions. The `no-kimaki` role still blocks. Default access is [role-based](/docs/configuration/access-control).                                         |
| `--restrict-directories`      | The agent must ask before leaving the project folder.                                                                                                                                           |
| `--no-auto-upgrade`           | Stop Kimaki from upgrading itself on boot. You control versions by rebuilding the image.                                                                                                        |
| `--verbosity <level>`         | Default thread verbosity. See [Verbosity](/docs/configuration/verbosity).                                                                                                                       |
| `--opencode-hostname 0.0.0.0` | Bind the OpenCode server on every interface so you can `opencode attach` from another machine. Requires `OPENCODE_SERVER_PASSWORD`. See [OpenCode server](/docs/remote-access/opencode-server). |
| `--opencode-port <port>`      | Stable OpenCode listen port. Default is a random free port. Use this with `--opencode-hostname` on a VPS.                                                                                       |

A public demo or shared box usually wants `--allow-all-users` and an OpenCode config that auto-allows tools, because nobody is sitting on permission buttons. A personal VPS can keep the defaults and approve tools from Discord.

## Updating

Rebuild to pull a newer `kimaki@latest` (or bump a pinned version in the Dockerfile):

```bash
docker compose up -d --build
```

The **volume** keeps `/data`, so gateway credentials and projects survive. From Discord you can also run `/upgrade-and-restart`, which upgrades the npm package inside the running container. A later image rebuild overwrites that, so prefer rebuilding when you manage the box with Docker.

## Logs and shell

```bash
# follow bot output
docker compose logs -f

# shell inside the container
docker compose exec kimaki bash
```

The Kimaki log file is `/data/kimaki.log`. It resets every time the bot process starts. See [Troubleshooting](/docs/guides/troubleshooting) if sessions stall after the bot is online.

## One bot per machine

Each Kimaki process can only see directories **inside this container**. That is the same rule as a laptop install. To drive another machine, run another container (or a native `kimaki` process) there and install that instance into the same Discord server. See [Advanced Setup](/docs/guides/advanced-setup).


---
title: Programmatic Gateway
url: "https://kimaki.dev/docs/guides/programmatic-gateway.md"
description: "How to spawn kimaki as a child process in gateway mode, parse SSE events, and integrate it into a cloud platform."
---

When kimaki runs in a non-TTY environment (piped stdout, no terminal), it emits
structured events on stdout using the SSE (Server-Sent Events) wire format.
This lets a host process parse lifecycle events reliably even when other log
lines, warnings, and debug output are interleaved on the same stream.

## Use case

You are building a cloud platform that provisions kimaki instances for users.
Each user gets their own kimaki process running on a VPS. Your platform needs to:

1. Start kimaki for a new user
2. Get the Discord install URL to show in your web UI
3. Know when the user has authorized the bot
4. Know when the bot is fully ready and listening for messages
5. Redirect the user to your own page after OAuth (custom callback URL)

## Event lifecycle

When kimaki starts in gateway mode with piped stdout, it emits these events
in order:

```diagram
install_url  ──▶  authorized  ──▶  ready
                       ▲
                       │ (user clicks URL and authorizes)
```

| Event         | Payload                           | Description                           |
| ------------- | --------------------------------- | ------------------------------------- |
| `install_url` | `{ type, url }`                   | Discord OAuth URL to send to the user |
| `authorized`  | `{ type, guild_id }`              | User authorized the bot in a guild    |
| `ready`       | `{ type, app_id, guild_ids }`     | Bot is connected and listening        |
| `error`       | `{ type, message, install_url? }` | Something went wrong                  |

## SSE wire format

Each event is a single line prefixed with `data: ` and terminated with `\n\n`:

```
data: {"type":"install_url","url":"https://kimaki.dev/discord-install?clientId=...&callbackUrl=..."}\n\n
```

This is standard SSE format. The `data:` prefix is what makes it robust — log
lines, warnings, spinner output, and other noise do not start with `data:` at
column 0, so the parser ignores them completely.

## Parsing events with eventsource-parser

Install the parser:

```bash
npm install eventsource-parser
```

Parse events from the child process stdout:

```typescript
import { spawn } from 'node:child_process'
import { createParser } from 'eventsource-parser'

const child = spawn('kimaki', [
  '--gateway',
  '--restart-onboarding',
  '--data-dir', '/data/user-abc',
  '--gateway-callback-url', 'https://your-platform.com/oauth-done',
], {
  env: {
    ...process.env,
    // Unique port per instance to avoid conflicts between concurrent kimaki processes
    KIMAKI_LOCK_PORT: '31200',
  },
  stdio: ['ignore', 'pipe', 'pipe'],
})

const parser = createParser({
  onEvent(sseEvent) {
    const event = JSON.parse(sseEvent.data)

    switch (event.type) {
      case 'install_url': {
        // Send this URL to your user (email, web UI, etc.)
        console.log('Install URL:', event.url)
        break
      }
      case 'authorized': {
        // User authorized the bot — you now have their guild_id
        console.log('Guild ID:', event.guild_id)
        break
      }
      case 'ready': {
        // Bot is fully connected and listening for Discord messages
        console.log('App ID:', event.app_id)
        console.log('Guild IDs:', event.guild_ids)
        break
      }
      case 'error': {
        console.error('Error:', event.message)
        break
      }
    }
  },
})

// Feed raw stdout into the parser — it extracts data: lines, ignores everything else
child.stdout.on('data', (chunk) => {
  parser.feed(chunk.toString())
})
```

## CLI flags

| Flag                           | Required        | Description                                                   |
| ------------------------------ | --------------- | ------------------------------------------------------------- |
| `--gateway`                    | yes             | Use the shared Kimaki gateway bot                             |
| `--restart-onboarding`         | for fresh setup | Force the onboarding flow even if saved credentials exist     |
| `--data-dir <path>`            | recommended     | Isolated data directory per user instance                     |
| `--gateway-callback-url <url>` | optional        | Redirect user here after OAuth instead of default kimaki page |

## Custom callback URL

Pass `--gateway-callback-url` to redirect the user to your own page after they
authorize the bot. The callback URL receives a `?guild_id=<id>` query parameter
so your platform knows which guild was authorized.

```bash
kimaki --gateway --gateway-callback-url https://your-platform.com/setup-done
```

The install URL emitted in the `install_url` event will include the callback:

```
https://kimaki.dev/discord-install?clientId=...&callbackUrl=https%3A%2F%2Fyour-platform.com%2Fsetup-done
```

After the user authorizes, Discord redirects to kimaki's OAuth handler, which
then redirects to your callback URL with `?guild_id=<id>` appended.

## Running multiple instances

Each kimaki process needs a unique `KIMAKI_LOCK_PORT` to avoid conflicts.
Without it, a new process will kill the existing one.

```typescript
const lockPort = 31100 + userIndex

const child = spawn('kimaki', ['--gateway', '--data-dir', userDataDir], {
  env: { ...process.env, KIMAKI_LOCK_PORT: String(lockPort) },
  stdio: ['ignore', 'pipe', 'pipe'],
})
```

Also use a unique `--data-dir` per user so their SQLite databases, logs, and
credentials are isolated.

## Why SSE format instead of plain JSON lines

Process stdout is noisy. Kimaki logs, clack prompts, OpenCode server output,
and debug messages all go to stdout. Parsing JSON by checking if a line starts
with `{` is fragile — a log line could start with `{` by coincidence.

SSE format solves this because:

* Only lines starting with exactly `data:` at column 0 are parsed as events
* Other SSE fields (`id:`, `event:`, `retry:`) are ignored by the parser if
  you only read `.data`
* Log lines, warnings, and spinners are silently discarded
* The parser handles chunks split across multiple `data` events correctly


---
title: "Abort Session with \"x\""
url: "https://kimaki.dev/docs/plugins/abort-on-x.md"
description: "A simple OpenCode plugin that lets you interrupt a running session by typing \"x\" in Discord."
---

## Why you need this

When an AI is generating a long response or going down the wrong path, you want to stop it **fast**. Typing `/abort` or clicking buttons takes too long. Sometimes the model is deleting files or running commands you didn't want - every second counts.

With this plugin, just type `x` and hit enter. The session stops immediately. The message isn't sent to the model, so you don't waste tokens or pollute your conversation history.

## How it works

The plugin hooks into `chat.message`, which fires when you send a message. If the message is exactly "x" (case-insensitive), it:

1. Clears the message so it's never sent to the LLM
2. Calls `session.abort()` to stop the current generation

## Installation

Kimaki supports any OpenCode plugin. Create this file at `~/.config/opencode/plugins/abort-on-x.ts`:

```ts
import type { Plugin } from '@opencode-ai/plugin'

export const AbortOnXPlugin: Plugin = async ({ client }) => ({
  'chat.message': async (input, output) => {
    const text = output.parts
      .filter((p) => p.type === 'text')
      .map((p) => (p as { text: string }).text.trim())
      .join('')

    if (text.toLowerCase() === 'x') {
      output.parts.length = 0
      await client.session.abort({ path: { id: input.sessionID } })
    }
  },
})
```

Plugins in `~/.config/opencode/plugins/` load automatically on startup. No config changes needed.

## Using other OpenCode plugins

Kimaki runs on OpenCode, so any OpenCode plugin works out of the box. You can:

* **Load local plugins** by placing `.ts` or `.js` files in `~/.config/opencode/plugins/` (global) or `.opencode/plugins/` (per-project)
* **Load npm plugins** by adding them to your `opencode.json`:

```json
{
  "plugin": ["opencode-wakatime", "opencode-helicone-session"]
}
```

Browse community plugins at [opencode.ai/docs/ecosystem](https://opencode.ai/docs/ecosystem#plugins) or read the [plugin docs](https://opencode.ai/docs/plugins) to write your own.


---
title: Commands Reference
url: "https://kimaki.dev/docs/reference/commands.md"
description: Every Kimaki slash command and CLI subcommand in one place.
---

A complete reference of Kimaki's **Discord slash commands** and **CLI subcommands**, ordered by how often you'll reach for them. For deep dives, follow the links.

## Session slash commands

| Command                 | Description                                                                                               |
| ----------------------- | --------------------------------------------------------------------------------------------------------- |
| `/resume <session>`     | Resume a previous session ([docs](/docs/core-concepts/sessions))                                          |
| `/abort`                | Stop the current running session                                                                          |
| `/new-session <prompt>` | Start a new session with a prompt, files, and agent                                                       |
| `/btw <prompt>`         | Fork context into a new thread for a side question ([docs](/docs/features/btw))                           |
| `/fork`                 | Fork the session from a previous message ([docs](/docs/core-concepts/sessions))                           |
| `/fork-subagent`        | Fork a subagent task into its own thread                                                                  |
| `/last-sessions`        | List the 20 most recently active sessions. Titles prefixed `btw:` are side-question forks, not duplicates |
| `/share`                | Generate a public URL for the session                                                                     |
| `/session-id`           | Show the session ID and an attach command                                                                 |
| `/compact`              | Summarize and compact the session context                                                                 |
| `/context-usage`        | Show token usage and context window percentage                                                            |
| `/undo`                 | Undo the last assistant message (revert file changes)                                                     |
| `/redo`                 | Redo the last undone message                                                                              |

## Model & agent slash commands

| Command         | Description                                                                                      |
| --------------- | ------------------------------------------------------------------------------------------------ |
| `/model`        | Change model, thinking level, or clear overrides ([docs](/docs/getting-started/model-switching)) |
| `/agent`        | Change the agent for this channel or session                                                     |
| `/<name>-agent` | Instantly switch to a named agent file                                                           |
| `/login`        | Authenticate a provider via OAuth or API key ([docs](/docs/getting-started/subscriptions))       |
| `/verbosity`    | Set how much tool output is shown ([docs](/docs/configuration/verbosity))                        |

## Queue slash commands

| Command                    | Description                                                                                             |
| -------------------------- | ------------------------------------------------------------------------------------------------------- |
| `/queue <message>`         | Queue a message for after the current run; reply has a **Remove** button ([docs](/docs/features/queue)) |
| `/queue-command <command>` | Queue a registered command; reply has a **Remove** button                                               |

## Project & worktree slash commands

| Command                      | Description                                                                              |
| ---------------------------- | ---------------------------------------------------------------------------------------- |
| `/add-project <project>`     | Create channels for an existing project ([docs](/docs/configuration/projects))           |
| `/create-new-project <name>` | Create a new project folder and start a session                                          |
| `/remove-project <project>`  | Remove channels for a project                                                            |
| `/new-worktree <name>`       | Move the session into an isolated git worktree ([docs](/docs/features/worktrees))        |
| `/merge-worktree`            | Merge the worktree branch into the default branch                                        |
| `/worktrees`                 | List worktrees and toggle automatic worktree creation ([docs](/docs/features/worktrees)) |

## Access & permissions slash commands

| Command              | Description                                                                   |
| -------------------- | ----------------------------------------------------------------------------- |
| `/mcp`               | List and toggle MCP servers for the project ([docs](/docs/configuration/mcp)) |
| `/transcription-key` | Set the API key used for voice transcription ([docs](/docs/features/voice))   |

## Remote access slash commands

| Command                        | Description                                                                                    |
| ------------------------------ | ---------------------------------------------------------------------------------------------- |
| `/vscode`                      | Open browser VS Code; reply has a **Stop** button ([docs](/docs/remote-access/vscode))         |
| `/screenshare`                 | Share screen via VNC; reply has a **Stop** button ([docs](/docs/remote-access/screen-sharing)) |
| `/diff`                        | Generate a shareable diff URL ([docs](/docs/features/diff-viewer))                             |
| `/run-shell-command <command>` | Run a shell command in the project dir ([docs](/docs/features/shell-commands))                 |

## Maintenance slash commands

| Command                    | Description                                                                                 |
| -------------------------- | ------------------------------------------------------------------------------------------- |
| `/tasks`                   | List scheduled tasks with cancel buttons ([docs](/docs/features/scheduled-tasks))           |
| `/restart-opencode-server` | Restart the OpenCode server and re-register commands ([docs](/docs/guides/troubleshooting)) |
| `/upgrade-and-restart`     | Upgrade Kimaki to the latest version and restart                                            |

Your OpenCode commands, skills, and MCP prompts are also registered automatically as `/<name>-cmd`, `/<name>-skill`, and `/<name>-mcp-prompt`. See [OpenCode Commands](/docs/features/opencode-commands).

## Message shortcuts

| Shortcut               | Effect                                                                                      |
| ---------------------- | ------------------------------------------------------------------------------------------- |
| `!<command>`           | Run a shell command in the project dir ([docs](/docs/features/shell-commands))              |
| `... . queue`          | Queue the message for after the current run ([docs](/docs/features/queue))                  |
| `... . btw`            | Fork context to answer a side question ([docs](/docs/features/btw))                         |
| voice note             | Transcribed and sent as a prompt ([docs](/docs/features/voice))                             |
| image / PDF attachment | Sent to the agent as inline content ([docs](/docs/features/images))                         |
| small text attachment  | Inlined into the prompt, plus a local path and Discord URL                                  |
| large text attachment  | Saved locally. The prompt gets the path, Discord URL, size, and type, not the file contents |

## CLI subcommands

```bash
# Start the bot (interactive setup on first run)
npx -y kimaki@latest

# Projects
kimaki project add [directory]
kimaki project list
kimaki project create <name>
kimaki project remove <channel-id>

# Start or continue sessions programmatically
kimaki send --channel <channel-id> --prompt 'your prompt'
kimaki send --thread <thread-id> --prompt 'follow-up'
kimaki send --channel <channel-id> --prompt '...' --send-at '0 9 * * 1'

# Scheduled tasks
kimaki task list
kimaki task edit <id> --prompt 'new prompt'
kimaki task delete <id>

# Sessions
kimaki session list
kimaki session list --active
kimaki session search 'query'
kimaki session search 'query' --all
kimaki session read <session-id>
kimaki session export-events-jsonl --session <session-id> --out ./tmp/session.jsonl
# Paths are absolute; a worktree file is not the same path as main
kimaki session editors <file>
kimaki session title 'Short title' --session <session-id>

# OAuth account pools
kimaki multioauth list
kimaki multioauth anthropic list
kimaki multioauth openai check

# Bot presence
kimaki bot status set <text>
kimaki bot status clear
kimaki discord-install-url

# Tooling
kimaki tunnel -- pnpm dev
kimaki upload-to-discord --session <id> <file...>
kimaki tts 'text' -o out.mp3
kimaki sqlitedb
kimaki upgrade
```

See [CI & Automation](/docs/guides/ci-automation) for the full `kimaki send` flag reference and scheduling options. See [Debug sessions and report bugs](/docs/guides/report-bugs) to export jsonl and file an issue.


---
title: Gateway Architecture and Event Delivery
url: "https://kimaki.dev/docs/reference/gateway-architecture.md"
description: "How events flow from Discord through the gateway proxy to your CLI, including offline buffering and reconnection."
---

Kimaki's **gateway mode** lets multiple users share a single Discord bot instead of each user creating their own. A Rust proxy sits between Discord and every kimaki CLI instance, routing events to the right client based on guild authorization.

```diagram
  Discord servers              Gateway proxy (Rust)            Your machine
 ┌────────────────┐          ┌──────────────────────────┐    ┌──────────────────┐
 │                │          │                          │    │                  │
 │  Bot shard 0   │─────────>  Dispatch + broadcast     │    │  kimaki CLI      │
 │  Bot shard 1   │─────────>  Per-client guild filter  ├───>│  (discord.js)    │
 │  Bot shard N   │─────────>  Offline event buffering  │    │                  │
 │                │          │                          │    │  OpenCode server │
 └────────────────┘          └──────────────────────────┘    └──────────────────┘
        ^                                  ^
        │                                  │
   Real Discord                   Shared Postgres
   Gateway WebSocket              (gateway_clients table)
```

## How events flow

Discord sends all bot events to the proxy's shards. The proxy **broadcasts** each event to all connected clients, but every client only receives events for its **authorized guilds**. Authorization comes from the `gateway_clients` table in Postgres, polled every second.

```diagram
  Discord Gateway
        │
        v
  Shard receives event ──> broadcast_tx.send(payload, sequence, guild_id)
                                          │
                    ┌─────────────────────┼─────────────────────┐
                    v                     v                     v
              Client A                Client B              Client C
              guild: 111              guild: 222            guild: 111
              ✓ receives              ✓ receives            ✓ receives
              guild 111 events        guild 222 events      guild 111 events
```

Events without a `guild_id` (DMs, user updates) are **not forwarded** to multi-tenant clients. Only guild-scoped events pass through.

## Offline event buffering

When a client is **not connected**, the proxy buffers specific event types so they can be replayed later. This is the key mechanism that prevents message loss when the CLI restarts or loses connection.

**Buffered event types:**

| Event                                                                 | Buffered |
| --------------------------------------------------------------------- | -------- |
| `MESSAGE_CREATE`                                                      | Yes      |
| `MESSAGE_UPDATE`                                                      | Yes      |
| `MESSAGE_DELETE`                                                      | Yes      |
| `THREAD_CREATE`                                                       | Yes      |
| `THREAD_UPDATE`                                                       | Yes      |
| `THREAD_DELETE`                                                       | Yes      |
| Everything else (presence, typing, voice state, guild member updates) | No       |

<Aside>
  <Note>
    The buffer is a **FIFO queue capped at 200 events** per client. If more than 200 message/thread events arrive while the CLI is offline, the oldest are dropped.
  </Note>
</Aside>

Only message and thread events matter for coding sessions. Presence updates, typing indicators, and voice state changes are ephemeral and safe to lose.

## What happens on reconnect

When the CLI connects (or reconnects), the proxy delivers events in a strict three-step sequence before subscribing to the live stream:

```diagram
  Step 1                    Step 2                      Step 3
 ┌────────────────────┐   ┌──────────────────────┐    ┌────────────────────────┐
 │ Synthetic READY    │   │ Buffered events      │    │ Live event stream      │
 │                    │   │                      │    │                        │
 │ Filtered to only   │──>│ Up to 200 queued     │───>│ Real-time broadcast    │
 │ authorized guilds  │   │ MESSAGE/THREAD events│    │ with guild filtering   │
 │ + GUILD_CREATE     │   │ in FIFO order        │    │                        │
 └────────────────────┘   └──────────────────────┘    └────────────────────────┘
```

**Step 1: Synthetic READY.** The proxy builds a custom READY payload containing only the guilds this client is authorized for, followed by `GUILD_CREATE` events for each guild. This is necessary because Discord's native READY includes all bot guilds, but each client should only see its own.

**Step 2: Buffered event replay.** Any MESSAGE/THREAD events that arrived while the client was disconnected are drained from the offline buffer and sent in order. Sequence numbers are rewritten to maintain a continuous sequence for the client.

**Step 3: Live stream.** The client subscribes to the shard's broadcast channel and receives events in real time from this point forward.

## Discord's resume mechanism

Independently of the proxy's buffering, discord.js implements Discord's native **session resume** protocol:

* **RESUME**: discord.js sends `RESUME {session_id, seq}` to the gateway. If the session is still alive on Discord's servers (typically a few minutes), Discord replays all missed events since that sequence number.
* **Fresh IDENTIFY**: if the session expired or RESUME is rejected (`INVALID_SESSION`), discord.js falls back to a new IDENTIFY. The proxy then runs the full three-step reconnection flow described above.

After a **gateway proxy redeploy**, RESUME always fails because the proxy's in-memory sessions are gone. discord.js falls back to fresh IDENTIFY, re-authenticates via the `gateway_clients` table, and receives the synthetic READY plus any buffered events.

<Aside>
  <Tip>
    The CLI doesn't implement any custom missed-event recovery. All recovery is handled by two layers working together: Discord's RESUME protocol and the proxy's offline buffer.
  </Tip>
</Aside>

## Limitations

**Buffer is in-memory only.** If the gateway proxy itself restarts or redeploys, all offline buffers are lost. Events that arrived during the proxy's downtime are not recoverable through the proxy. Discord's own RESUME may recover some of them if the session is still valid.

**200 event cap per client.** If a guild is very active and generates more than 200 message/thread events while the CLI is offline, the oldest events are dropped. In practice this is rare for coding-focused servers.

**Non-message events are not buffered.** Typing indicators, presence updates, voice state changes, and guild member updates are not queued. These are ephemeral and regenerated on reconnect (guild state comes via `GUILD_CREATE`).

**Stale protection.** If the proxy cannot reach the Postgres database for more than 30 seconds, it rejects all client authentication to prevent stale authorization data from being used. Clients see a connection failure and retry.

## Summary

| Layer               | What it buffers                     | Capacity                   | Survives restart      |
| ------------------- | ----------------------------------- | -------------------------- | --------------------- |
| **Discord servers** | Full message persistence            | Unlimited                  | Yes                   |
| **Gateway proxy**   | MESSAGE/THREAD events per client    | 200 events FIFO, in-memory | No (lost on redeploy) |
| **Kimaki CLI**      | Nothing (delegates to above layers) | N/A                        | N/A                   |

The proxy's offline buffer is the primary mechanism that makes the CLI resilient to brief disconnections. For longer outages where both the proxy buffer and Discord's RESUME window are exhausted, messages still exist in Discord and can be fetched via REST, but real-time event replay is not available.


---
title: Internals
url: "https://kimaki.dev/docs/reference/internals.md"
description: "How Kimaki works under the hood — SQLite, lock port, channel metadata, voice processing, and more."
---

**SQLite Database** — Kimaki stores state in `<data-dir>/discord-sessions.db` (default: `~/.kimaki/discord-sessions.db`). This maps Discord threads to OpenCode sessions, channels to directories, and stores your bot credentials. Use `--data-dir` to change the location.

**Lock Port** — Kimaki enforces single-instance behavior by binding a lock port. By default, the port is derived from `--data-dir`; set `KIMAKI_LOCK_PORT=<port>` to override it when running an additional Kimaki process on the same machine.

**OpenCode Servers** — When you message a channel, Kimaki spawns (or reuses) an OpenCode server for that project directory. The server handles the actual AI coding session.

**Channel Metadata** — Each channel's topic contains XML metadata linking it to a directory and bot:

```xml
<kimaki><directory>/path/to/project</directory><app>bot_id</app></kimaki>
```

**Voice Processing** — Voice features run in a worker thread. Audio flows: Discord Opus > Decoder > Downsample (48kHz to 16kHz) > Gemini API > Response > Upsample > Opus > Discord.

**Log File** — Kimaki writes logs to `<data-dir>/kimaki.log` (default: `~/.kimaki/kimaki.log`). The log file is reset on every bot startup, so it only contains logs from the current run. Read this file to debug internal issues, session failures, or unexpected behavior.

**Graceful Restart** — Send `SIGUSR2` to restart the bot with new code without losing connections.

**Live CPU profiling** — In the terminal where Kimaki is running, type `cpuprof` and press Enter. Type it again to stop, or wait 20 seconds for auto-stop. Profiles land in `<data-dir>/cpu-profiles/` (default `~/.kimaki/cpu-profiles/`). Open the `.cpuprofile` file in Chrome DevTools (Performance tab) or with `bunx profano`. Stdin must be a TTY.


---
title: Privacy Policy
url: "https://kimaki.dev/docs/legal/privacy.md"
description: "What Kimaki processes when you use the shared bot, onboarding website, and related integrations."
---

import PrivacyPolicy from '../../../privacy-policy.md'

# Kimaki Privacy Policy

Effective date: March 28, 2026

Kimaki is a coding agent that can run through Discord and related onboarding pages
at `kimaki.dev`. This Privacy Policy explains what information Kimaki processes,
why it processes it, and how that information is shared when you use the shared
Kimaki bot, the website, or the Slack bridge onboarding flow.

## Summary

* Kimaki processes the messages, commands, files, and metadata needed to operate
  the product.
* Kimaki may send prompts and related context to AI model providers that power
  the assistant.
* Kimaki uses infrastructure providers to host the website, onboarding flow,
  logs, and database.
* Kimaki does not sell personal information.

## Information Kimaki processes

Kimaki may process the following categories of information:

### 1. Discord and Slack account data

* Discord user IDs, usernames, display names, guild IDs, channel IDs, thread
  IDs, role information, and similar server metadata.
* Slack workspace IDs, team IDs, channel IDs, and user IDs when using the Slack
  bridge flow.
* OAuth installation and onboarding data needed to connect a workspace or guild
  to Kimaki.

### 2. Content you provide

* Messages you send to Kimaki in Discord.
* Slash command inputs and follow-up messages.
* Files, screenshots, code snippets, terminal output, and other attachments you
  intentionally provide.
* Voice messages or audio attachments that Kimaki transcribes or processes.
* Repository or project content that you explicitly ask Kimaki to inspect,
  summarize, edit, or send to connected AI providers.

### 3. Technical and operational data

* Request logs, error logs, timestamps, and service diagnostics.
* Information about whether onboarding succeeded, which guild or workspace was
  connected, and related configuration records.
* Security and abuse-prevention signals needed to protect the service.

## How Kimaki uses information

Kimaki uses information to:

* authenticate installs and complete onboarding;
* respond to your prompts and operate the assistant;
* search for members or resolve mentions when you request that functionality;
* process files, voice messages, and other inputs you send to the bot;
* maintain service reliability, debug issues, and prevent abuse;
* comply with legal obligations and enforce the service rules.

## AI providers and subprocessors

Kimaki may send prompts, attached content, and related context to third-party AI
providers in order to generate responses or perform requested tasks. Depending on
the configuration, this may include model providers used through the OpenCode
stack.

Kimaki also relies on third-party infrastructure providers, which may process
data on Kimaki's behalf, including:

* Discord, for bot messaging, slash commands, guild installs, and message
  delivery;
* Slack, when the Slack bridge is used;
* Cloudflare, for website and edge hosting;
* PlanetScale or other configured database/storage providers;
* logging, observability, and infrastructure vendors used to operate the
  service.

These providers may retain and process data under their own terms and privacy
policies.

## Data retention

Kimaki keeps data for as long as reasonably necessary to provide the service,
maintain onboarding state, debug operational issues, and meet legal or security
obligations.

Retention can vary depending on the type of data:

* onboarding and connection records may be stored until they are removed or no
  longer needed;
* logs and diagnostics may be retained for a limited operational period;
* content processed by Discord, Slack, and AI providers may also be retained by
  those providers under their own policies;
* local session data stored on a user's own machine is controlled by that user.

## Data sharing

Kimaki shares information only as needed to operate the service, comply with the
law, protect users, or prevent fraud, abuse, and security incidents.

Kimaki does not sell personal information.

## Security

Kimaki uses reasonable administrative, technical, and organizational measures to
protect information. No method of transmission or storage is completely secure,
and Kimaki cannot guarantee absolute security.

## Your choices

If you do not want Kimaki to process message content, files, or repository
content, do not send that content to the service.

You can also stop using the service, remove the bot from your server, or contact
Kimaki to request deletion of onboarding data that Kimaki directly controls,
subject to legal and operational requirements.

## Children's privacy

Kimaki is not directed to children under 13 and should not be used in violation
of Discord's or Slack's platform rules.

## International data transfers

Kimaki and its providers may process information in countries other than your
own.

## Changes to this policy

Kimaki may update this Privacy Policy from time to time. The updated version
will be posted on this page with a new effective date.

## Contact

For privacy questions or data requests, contact: `tommy@kimaki.dev`


---
title: Terms of Service
url: "https://kimaki.dev/docs/legal/terms.md"
description: "Terms governing use of Kimaki, the shared bot, onboarding pages, and related integrations."
---

import TermsOfService from '../../../terms-of-service.md'

# Kimaki Terms of Service

Effective date: March 28, 2026

These Terms of Service govern your use of Kimaki, including the shared Discord
bot, `kimaki.dev`, onboarding pages, Slack bridge flows, and related services.
By using Kimaki, you agree to these terms.

## 1. Use of the service

Kimaki is a coding and automation assistant. You may use it only in compliance
with applicable law and the rules of the platforms it integrates with,
including Discord and Slack.

You are responsible for the prompts, files, code, commands, and other content
you send to Kimaki.

## 2. Acceptable use

You may not use Kimaki to:

* violate the law or another person's rights;
* access systems, repositories, tokens, or data without authorization;
* send malware, destructive payloads, spam, or abusive content;
* interfere with the service, infrastructure, or other users;
* attempt to bypass rate limits, permissions, or platform restrictions;
* use Kimaki in a way that violates Discord's, Slack's, or any AI provider's
  terms.

Kimaki may suspend or restrict access to protect the service or comply with law
or platform requirements.

## 3. AI-generated output

Kimaki uses third-party AI providers to generate responses. AI output may be
incorrect, incomplete, insecure, or inappropriate for your use case.

You are responsible for reviewing and validating any output, including code,
shell commands, infrastructure changes, or compliance-related text, before you
rely on it.

## 4. Your content

You retain whatever rights you have in the content you provide to Kimaki.

You grant Kimaki the limited rights needed to host, process, transmit, and
analyze that content in order to operate the service, including sending content
to infrastructure and AI providers used to fulfill your requests.

## 5. Third-party services

Kimaki depends on third-party services including Discord, Slack, Cloudflare,
database providers, and AI model providers. Availability and performance may
depend on those services.

Kimaki is not responsible for outages, policy changes, account restrictions, or
other acts of third-party services.

## 6. Availability and changes

Kimaki may change, suspend, or discontinue features at any time. Features may
be added, removed, rate-limited, or changed without notice.

## 7. Security and credentials

You are responsible for protecting your own credentials, repositories, files,
and connected systems.

Do not send secrets or sensitive information to Kimaki unless you accept the
risks of processing by third-party providers and networked systems.

## 8. Disclaimer of warranties

Kimaki is provided on an "as is" and "as available" basis, without warranties
of any kind, express or implied, including implied warranties of
merchantability, fitness for a particular purpose, and non-infringement.

## 9. Limitation of liability

To the maximum extent permitted by law, Kimaki will not be liable for indirect,
incidental, special, consequential, exemplary, or punitive damages, or for any
loss of data, profits, revenue, goodwill, or business interruption arising out
of or related to your use of the service.

## 10. Termination

You may stop using Kimaki at any time.

Kimaki may suspend or terminate access at any time if necessary to protect the
service, comply with law, enforce these terms, or respond to platform or
security requirements.

## 11. Privacy

Your use of Kimaki is also governed by the Kimaki Privacy Policy.

## 12. Changes to these terms

Kimaki may update these Terms of Service from time to time. The updated version
will be posted on this page with a new effective date. Continued use of Kimaki
after an update means you accept the revised terms.

## 13. Contact

For questions about these terms, contact: `tommy@kimaki.dev`
