# CapaKit CLI Docs

CapaKit CLI runs, tests, packages, and shares local-first AI app Kits.

Current alpha scope:

- macOS only.
- workload toolchains: Bun only.

Known limitations:

- macOS sandbox is currently too strict for some workloads, known not to work:
  - Chromium: requires too many permissions, probably will support once added VM based sandbox backend.

## Quick Start: macOS

The public alpha currently targets macOS and Bun workloads.

### Install

Install CapaKit:

```sh
curl -fsSL https://capakit.com/install.sh | sh
```

The installer places `capakit` under `~/.capakit/bin` by default and updates your shell profile unless
`CAPAKIT_NO_MODIFY_PATH=1` is set.

Restart your shell if `capakit` is not found immediately:

```sh
capakit --version
```

### Run a Kit

Run the hello-world Kit from GitHub:

```sh
capakit run https://github.com/capakit/hello-world-demo-kit
```

CapaKit downloads the Kit, prepares the workload, starts the local runtime, and prints the local URLs you can open or
call.

Keep the command running while you use the app. Press `Ctrl-C` to stop it.

### Run as a Skill

AI app Kits that expose MCP can be used as skills. CapaKit generates the provider-specific skill structure, including
agent instructions and a command entrypoint for calling the Kit's MCP tools.

```sh
# Run the hello-world Kit as a Codex skill:
capakit run https://github.com/capakit/hello-world-demo-kit --global-skill codex
# Use another terminal to run the "hello-world" MCP tool
~/.codex/skills/hello-world/hello-world hello-world
> hello world
```

**Supported skill providers:**

- global skill installs: (`--global-skill <provider>`)
  - `codex`: installs in `~/.codex/skills/<skill-name>/`.
  - `claude`: installs in `~/.claude/skills/<skill-name>/`
- "project" skill installs: (`--project-skill <provider> --project-root <project>`)
  - `portable`: installs in `<project>/<skill-name>/`
  - `claude`: installs in `<project>/.claude/skills/<skill-name>/`.

Generated skill files are temporary and are removed when `capakit run` exits.

### Edit a Local Kit

Clone a Kit, test it, then run it:

```sh
git clone https://github.com/capakit/hello-world-demo-kit
cd hello-world-demo-kit
capakit test
capakit run
```

Use `capakit exec [WORKLOAD] -- <COMMAND>` to safely run workload commands like `bun add <package>`.

### Where CapaKit Writes

CapaKit uses these host paths:

- `~/.capakit/bin`: default install location for the `capakit` executable.
- `~/.capakit`: CapaKit home. Stores materialized package sources, runtime state and persistent secret stores.
- `~/.cache/capakit`: persistent build cache.
- `<kit>/_state/`: generated state for a local Kit.

**Storage usage:**

```sh
capakit storage status
capakit storage status --detailed
```

## Working With Coding Agents

CapaKit is designed to be used with coding agents. You do not need to know the full CLI before asking an agent to build
something. Tell the agent to use `capakit` to create or edit a CapaKit AI app Kit, then describe the app you want in
plain product terms.

**Useful words to use:**

- AI app Kit: a CapaKit project the agent can create, edit, test, run, and package.
- Web UI: a local browser app, usually for forms, dashboards, workflows, uploads, or visual output.
- Tool: a named action an AI assistant can call, such as `summarize_file`, `search_docs`, or `extract_invoice_fields`.
- MCP server: a set of tools exposed to AI assistants and coding agents.
- Skill: a tool app installed into an agent environment, such as a Codex skill.

The most important instruction is simple: ask the coding agent to use CapaKit. CapaKit provides agent instructions that
the agent knows how to follow.

### Prompting Agents

Start with the outcome. Mention the user-facing surface only when you already know it.

Create a web app:

```text
Use capakit to create a new AI app Kit called invoice-helper.
I want a web UI where I can upload invoice text and see extracted fields: vendor, date, total, and line items.
Please test it and leave clear run instructions.
```

Create tools for an assistant:

```text
Use capakit to create an AI app Kit that works as an MCP server.
It should provide a tool named extract_invoice_fields that accepts invoice text and returns structured invoice fields.
Please make the tool description clear enough for an AI assistant to choose it correctly.
```

Modify an existing Kit:

```text
This is an existing CapaKit AI app Kit.
Add a web UI for reviewing the results, keep the existing tools working, run the CapaKit tests, and update the README.
```

## Glossary

These terms appear throughout the docs. You do not need to memorize them before using CapaKit.

- AI app: the app you build, run, test, and share with CapaKit. It can be a web UI, assistant tools, or both.
- Kit: the project or package for one AI app. A Kit can be a local directory, a GitHub repository, or a `.capakit`
  archive.
- Workload: one isolated code process inside a Kit, such as Bun code that serves a web UI or MCP tools.
- Tool: a named action an AI assistant can call, such as `search_docs` or `extract_invoice_fields`.
- Agent protocols: standard ways for assistants, agents, and apps to talk to each other.
  - MCP: exposes tools and context that an assistant or coding agent can call.
  - A2A: exposes an agent service with an agent card and callable skills.
  - OpenAI-compatible: exposes a model-style API that existing OpenAI-compatible clients can call.
- Public path: the local path CapaKit exposes for a protocol surface, such as `/ui` for a web UI or `/mcp` for tools.
- CapaKit runtime: the CapaKit process that orchestrates an AI app.
- Local models: AI models that run on your machine instead of a cloud API.

## Security

CapaKit treats Kits and their workloads as code that should run with explicit boundaries. The local runtime prepares the
Kit, starts workload processes, routes requests between them, and limits what each workload can see or call according to
the Kit manifest.

At a high level, the runtime isolation boundaries:

```text
Kit
  AI app package with host-exposed public paths

  Workloads
    Code processes that implement the app
    Connected through an isolated workload service mesh

    Workload
      Sandboxed runtime process, such as `bun run start`
      Access to its workload files and declared resources
```

Each workload full lifecycle is sandboxed from the host, including dependency installs such as `bun install` and runtime
commands such as `bun run start`.

### Secrets

CapaKit has two secret stores:

- Kit secrets: per-Kit secrets that are exposed only to configured workloads, not limited to CapaKit-managed
  integrations.
- Vault secrets: protected secrets that can be shared between Kits, safely used only with trusted CapaKit-managed
  integrations (Relays).

Secret values are not stored in `capability.yml`. The manifest declares the secret key, usage text, and which store it
belongs to. The value is provided by prompting the user when the Kit starts or via CLI in advance, if the secret value
is not already available.

#### Kit Secrets

Kit secrets are for values a workload may need directly, such as an API key used by a Kit's own source code.

The secret values are stored in encrypted form in CapaKit home (`~/.capakit`).

Workloads do not receive any Kit secret automatically. A workload record in the manifest must list the secret in its
exposed secrets. At runtime, the workload can resolve only those exposed secrets through the runtime workload SDK.

**Important:** Kit secrets are intentionally available to workload code that declares access. Treat any Kit that
receives a Kit secret as code that can use that secret.

#### Vault Secrets

Vault secrets are protected secrets stored in the global secret store under `~/.capakit`. They are intended for trusted
CapaKit-managed integrations, such as secure exit nodes (Relays), rather than ordinary Kit source workloads.

Source workloads cannot expose vault secrets. If a workload is added with a vault secret, CapaKit rejects it; workload
code can only use Kit secrets. This keeps the user's higher-trust vault separate from untrusted or semi-trusted Kit
code.

### Isolation

CapaKit isolation is manifest-driven. The Kit declares what each workload may use, and the CapaKit runtime turns those
declarations into sandbox policy.

#### Resources

Workload resource access is explicit and granular:

- Environment:
  - host environment is not inherited.
  - CapaKit constructs workload and toolchain-specific env when starting it.
- Filesystem:
  - by default workloads have access to:
    - workload root
    - isolated per-instance home/cache/tmp directories
    - CapaKit-owned toolchain roots
    - CapaKit-owned shared toolchain cache roots
  - manifest-declared host mounts, granted to each workload as read-only or read-write
  - `capakit exec` and `capakit run --mode source` run root Kit workloads from real source roots
- Network:
  - workloads communicate with other workloads through the CapaKit service mesh
  - workload-to-workload access is limited to manifest-declared connections
  - prepare commands default to full network access for dependency installation
  - start commands default to no IP networking
  - workload commands can override the default with `network: none`, `network: loopback`, or `network: full`
- Secrets:
  - workload code can resolve only exposed [Kit secrets](#kit-secrets)
  - [Vault secrets](#vault-secrets) are reserved for trusted CapaKit-managed integrations
- RPC:
  - CapaKit service-mesh RPC links use mTLS with SPIFFE-style service identities

#### Run Modes

CapaKit has two main local run modes:

- Source mode: runs root Kit workloads from local source roots.
- Managed mode: builds workload artifacts and runs from those artifacts.

Imported Kit dependencies use managed artifact execution by default.

### Sandbox Backends

CapaKit translates the same workload isolation model into different runtime backends.

#### macOS Seatbelt

On macOS, CapaKit runs workload commands through a generated Seatbelt policy.

The generated policy:

- starts from default deny
- allowlists required system files, devices, runtime libraries, and process primitives
- allowlists only the workload roots, CapaKit toolchain/cache roots, runtime home/cache/tmp dirs, host mounts, and IPC
  paths needed by that workload
- grants host mounts according to the workload's read-only or read-write manifest grant
- exposes only constructed workload env; the host environment is not inherited
- gives prepare commands full network access by default
- gives start commands no IP networking by default
- allows commands to override the default with `network: none`, `network: loopback`, or `network: full`
- enables Metal GPU access only when the workload declares the Metal GPU capability

Set `CAPAKIT_DEBUG_SEATBELT_POLICY=1` to have CapaKit write the generated Seatbelt policy path to stderr for debugging.

### Relays

Relays are CapaKit-managed exit workloads for calling trusted external providers.

Instead of injecting secrets such as API keys into potentially vulnerable source workloads, Relay exit nodes are
CapaKit-native trusted services that attach headers in transit and direct connections to preconfigured URLs.

The source workload talks to the Relay like any other workload. The workload SDK provides provider clients that are
already routed through the Relay.

This gives the Kit a narrower trust shape:

- source workload code does not read the provider API key
- the provider key can live in the global vault secret store
- external provider access is represented as a workload connection in `capability.yml`
- the Relay is a CapaKit-managed runtime attachment, not arbitrary Kit source code

Relay workloads are relay-only. A workload cannot mix source runtime code and relay attachments.

**Example:**

```yaml
vault_secrets:
  - key: openai_api_key
    usage: OpenAI API key

workloads:
  app:
    connections:
      - openai-exit
    endpoints:
      - mcp
    runtime:
      source:
        toolchain: bun
        prepare:
          command: bun install
        start:
          command: bun run src/index.ts

  openai-exit:
    endpoints:
      - oaic
    runtime:
      attachments:
        relays:
          - kind: mesh_to_open_ai
            endpoint: oaic
            api_key_secret: openai_api_key
```

Related manifest section: [Workloads](#workloads).

In this example, `app` can call the `openai-exit` workload because it is listed under `connections`. The Relay resolves
`openai_api_key` from vault secrets and uses it only inside the managed Relay path.

Relays are not a general-purpose proxy. They are provider-specific adapters with explicit authentication behavior.

#### Providers

CapaKit currently supports these Relay kinds:

- `mesh_to_open_ai`
  - upstream host: `api.openai.com`
  - auth header written by Relay: `Authorization: Bearer <api-key>`
  - endpoint protocol: `oaic`
- `mesh_to_anthropic`
  - upstream host: `api.anthropic.com`
  - auth header written by Relay: `x-api-key: <api-key>`
  - default Anthropic version header: `anthropic-version: 2023-06-01` when the request does not set one
  - endpoint protocol: `http`
- `mesh_to_google_ai_studio`
  - upstream host: `generativelanguage.googleapis.com`
  - auth header written by Relay: `x-goog-api-key: <api-key>`
  - endpoint protocol: `http`

The Relay strips caller-supplied provider auth headers before forwarding, then writes the configured provider auth
header from the vault secret.

## Kit Anatomy

A Kit is the source or package for one CapaKit AI app.

The root file is `capability.yml`. It declares the app name, workloads, public paths, options, dependencies, host
mounts, secrets, and connection policy. Workload source code lives under `workloads/<workload-name>/`.

Kits can come from these sources:

- Local source directory:
  - editable Kit root containing `capability.yml`
  - works with `run`, `test`, `exec`, `kit package`, `kit clean`, `kit workloads`, and `kit secrets`
- Local `.capakit` archive:
  - packaged Kit created by `capakit kit package`
  - runnable with `capakit run`
  - not editable, testable, or packageable in place
- GitHub repository URL:
  - remote Kit source
  - runnable with `capakit run`
  - not editable, testable, or packageable in place

**Examples:**

```sh
capakit run .
capakit run ./hello-world.capakit
capakit run https://github.com/capakit/hello-world-demo-kit
```

When CapaKit runs a packaged or remote source, it materializes the Kit under CapaKit-managed storage. Edit the original
source directory when you want to change the Kit.

**Breakdown of Kit files:**

- `AGENTS.md`: Kit-local instructions for coding agents.
- `capability.yml`: required runtime manifest.
- `capability-test.yml`: optional test manifest for `capakit test`.
- `tests/`: optional fixture directories used by `capakit test`.
- `workloads/<workload>/`: workload source roots.
- `README.md`: user-facing usage notes for the Kit.
- `.gitignore`: excludes generated state and dependency/build output.
- `_state/`: generated local CapaKit state. (Do not edit it.)

### `AGENTS.md`

Points coding agents to `capakit agents-md print`.

### `capability.yml`

`capability.yml` is the source of truth for how the Kit runs. It declares the app name, options, workloads, public
paths, host mounts, secrets, dependencies, and workload-to-workload connection wiring.

**Example:** [hello-world kit](https://github.com/capakit/hello-world-demo-kit)

```yaml
version: '1'
name: hello-world
workloads:
  hello:
    endpoints:
      - mcp
    runtime:
      source:
        toolchain: bun
        prepare:
          command: bun install
        start:
          command: bun run src/index.ts
expose:
  - path: /mcp
    target: hello # -> workloads.hello
    default_mcp: true # some CLI commands, such as run, do not need an explicit MCP endpoint
    endpoints:
      - mcp # points to the MCP endpoint -> workloads.hello.endpoints
```

#### Workloads

Each `workloads` key names one addressable workload and maps to `workloads/<name>/` for source workloads.

A workload entry can be a source runtime, a CapaKit-managed Relay runtime, an imported workload, or a set of runtime
variants. Source workloads declare the protocol surfaces they serve, the runtime commands CapaKit should run, and the
explicit resources they may use.

```yaml
workloads:
  app: # workload id
    connections:
      - tools # outbound peer connection
    endpoints: # expose running services
      - http
      - mcp # shorthand format: `path: /mcp` and `protocol: mcp`
      - protocol: mcp # extended format, can provide a custom path
        path: /agent
        config:
          timeout_ms: 30000
    mounts:
      - uploads # mount that was declared in host_mounts
      - mount_mid: cache
        access: read_write
    exposed_secrets:
      - vendor_api_key # secret that was defined in kit_secrets
    runtime:
      source:
        toolchain: bun
        prepare:
          command: bun install
          network: full
        hydrate:
          command: bun run hydrate
          env:
            CACHE_MODE: warm
          env_from_options:
            MODEL_NAME: model # from top-level `options`
          network: loopback # none | loopback | full
        start:
          command: bun run src/index.ts
          env:
            NODE_ENV: production
          env_from_options:
            APP_MODE: mode
          network: none
      capabilities:
        gpu: metal

  tools:
    endpoints:
      - mcp
    runtime:
      source:
        toolchain: bun
        prepare:
          command: bun install
        start:
          command: bun run src/index.ts
```

Relay workloads are CapaKit-managed runtime attachments. They are useful for trusted provider exits, where the source
workload should call a provider without reading the provider secret directly.

```yaml
vault_secrets:
  - key: openai_api_key
    usage: OpenAI API key

workloads:
  app:
    connections:
      - openai-exit
    endpoints:
      - mcp
    runtime:
      source:
        toolchain: bun
        prepare:
          command: bun install
        start:
          command: bun run src/index.ts

  openai-exit:
    endpoints:
      - oaic
    runtime:
      attachments:
        relays:
          - kind: mesh_to_open_ai
            endpoint: oaic
            api_key_secret: openai_api_key
```

Imported workloads point at a public path exposed by a dependency Kit. The importing Kit can connect to or expose the
imported workload like any other workload.

```yaml
dependencies:
  shared-tools:
    source:
      package: https://github.com/capakit/shared-tools-kit
      version: main

workloads:
  shared-tools:
    import:
      dependency: shared-tools
      exposed_path: /mcp
```

Variant workloads declare multiple runtime implementations under one workload id. Keep variants within the same trust
shape: do not mix source variants and relay variants under one workload.

```yaml
workloads:
  model:
    endpoints:
      - oaic
    variants:
      default:
        runtime:
          source:
            toolchain: bun
            prepare:
              command: bun install
            start:
              command: bun run src/local-model.ts
      gpu:
        runtime:
          source:
            toolchain: bun
            prepare:
              command: bun install
            start:
              command: bun run src/gpu-model.ts
          capabilities:
            gpu: metal
```

**Common workload fields:**

- `endpoints`: protocol endpoints served by the workload. Supported endpoint protocols are `http`, `mcp`, `a2a`, and
  `oaic`.
- `connections`: workloads this workload may call through the CapaKit service mesh.
- `mounts`: Kit-level host mounts granted to this workload.
- `exposed_secrets`: Kit secrets this workload may resolve through the workload SDK.

**Example Kits:**

- [hello-world-demo-kit capability.yml](https://github.com/capakit/hello-world-demo-kit/blob/main/capability.yml):
  minimal Bun MCP source workload.
- [local-image-tagger-demo-kit capability.yml](https://github.com/capakit/local-image-tagger-demo-kit/blob/main/capability.yml):
  source workload with HTTP and MCP endpoints, host mounts, and an imported local model dependency.
- [llama-cpp-local-kit capability.yml](https://github.com/capakit/llama-cpp-local-kit/blob/main/capability.yml):
  source workload with `hydrate`, host mounts, options, GPU capability, and MCP/OAIC endpoints.

#### Expose

`expose` maps host-reachable paths to workload endpoints.

```yaml
expose:
  - path: /ui
    target: app
    endpoints:
      - http
  - path: /mcp
    target: app
    endpoints:
      - mcp
    default_mcp: true
```

Set exactly one `default_mcp: true` when the Kit exposes a primary MCP endpoint for `capakit mcp` or skill installs.

**Example Kits:**

- [hello-world-demo-kit capability.yml](https://github.com/capakit/hello-world-demo-kit/blob/main/capability.yml):
  exposes `/mcp` as the default MCP endpoint.
- [local-image-tagger-demo-kit capability.yml](https://github.com/capakit/local-image-tagger-demo-kit/blob/main/capability.yml):
  exposes both `/mcp` and `/` from the same workload.

#### Host Mounts

Declare host mounts at the Kit level, then opt workloads into them:

```yaml
host_mounts:
  models:
    usage: Local model files
    access: read_only # read_only | read_write

workloads:
  app:
    mounts:
      - models
```

Bind host paths at run, test, MCP, or exec time:

```sh
capakit run --mount models=~/.capakit/models
```

Mount access defaults to `read_only`. A workload can request narrower or equal access, but not broader access than the
top-level mount declaration allows.

**Example Kits:**

- [local-image-tagger-demo-kit capability.yml](https://github.com/capakit/local-image-tagger-demo-kit/blob/main/capability.yml):
  read-only image input mount plus read-write model cache mount.
- [stable-diffusion-local-kit capability.yml](https://github.com/capakit/stable-diffusion-local-kit/blob/main/capability.yml):
  read-write model and binary cache mount used during hydration and runtime.

#### Dependencies

Kit dependencies let one Kit import a public path exposed by another Kit. A dependency can come from any supported Kit
source.

**Dependency Kit:**

```yaml
expose:
  - path: /mcp
    target: tools
    endpoints:
      - mcp
```

**Consumer Kit:**

```yaml
dependencies:
  shared-tools:
    source:
      package: https://github.com/capakit/shared-tools-kit
      version: main

workloads:
  tools:
    import:
      dependency: shared-tools
      exposed_path: /mcp
```

Use `options`, `mount_bindings`, and `secret_bindings` under a dependency when the dependency Kit requires configured
inputs from the parent Kit.

**Example Kits:**

- [kids-storybook-creator-demo-kit capability.yml](https://github.com/capakit/kids-storybook-creator-demo-kit/blob/main/capability.yml):
  imports both llama.cpp and stable-diffusion local Kits, passing options and mount bindings.
- [realtime-voice-demo-kit capability.yml](https://github.com/capakit/realtime-voice-demo-kit/blob/main/capability.yml):
  imports llama.cpp and connects it to local ASR/TTS workloads.

#### Secrets

`kit_secrets` are for values source workload code may use. A workload must list a Kit secret in `exposed_secrets` before
it can resolve the value.

`vault_secrets` are for trusted CapaKit-managed integrations such as Relays. Source workloads cannot expose vault
secrets.

```yaml
kit_secrets:
  - key: vendor_api_key
    usage: Vendor lookup API key

workloads:
  app:
    exposed_secrets:
      - vendor_api_key
```

Secret values are not stored in `capability.yml`.

#### Options

`options` declares typed Kit inputs that can be reused elsewhere in the manifest. Options are useful when the same Kit
needs small configuration differences without editing workload code.

```yaml
options:
  model:
    kind: enum
    default: small
    enum_values:
      - small
      - large
    description: Model profile to use

  cache_enabled:
    kind: boolean
    default: true
    description: Enable workload cache behavior

  gpu:
    kind: enum
    default: none
    enum_values:
      - none
      - metal
    description: Optional GPU capability

workloads:
  app:
    endpoints:
      - mcp
    runtime:
      source:
        toolchain: bun
        prepare:
          command: bun install
        start:
          command: bun run src/index.ts
          env_from_options:
            MODEL_PROFILE: model
            CACHE_ENABLED: cache_enabled

  model:
    endpoints:
      - oaic
    runtime:
      source:
        toolchain: bun
        prepare:
          command: bun install
        start:
          command: bun run src/model.ts
      capabilities:
        gpu:
          from_option: gpu
```

Option kinds are `string`, `number`, `boolean`, `enum`, and `path`. Enum options may declare `enum_values`.

**Common option fields:**

- `kind`: required option type.
- `default`: optional default value. The value must match `kind`.
- `required`: set `true` when the Kit must receive a value if no default is declared.
- `enum_values`: allowed values for `kind: enum`.
- `description`: user-facing helper text for `capakit kit info` and generated guidance.
- `secret`: metadata for display and guidance. Use `kit_secrets` or `vault_secrets` for actual secret values.
- `scope`: optional display metadata, usually `public` or `internal`.

Options can be referenced from workload command `env_from_options`, dependency `options`, and option-backed capability
fields such as `runtime.capabilities.gpu`. If a manifest references an unset option, CapaKit rejects the Kit.

```yaml
dependencies:
  shared-tools:
    source:
      package: https://github.com/capakit/shared-tools-kit
      version: main
    options:
      model:
        from_option: model
      cache_enabled: true
```

**Example Kits:**

- [llama-cpp-local-kit capability.yml](https://github.com/capakit/llama-cpp-local-kit/blob/main/capability.yml):
  model, hydration, context, thread, and GPU options wired into workload env and capabilities.
- [stable-diffusion-local-kit capability.yml](https://github.com/capakit/stable-diffusion-local-kit/blob/main/capability.yml):
  model, backend, sampling, and hydration options wired into prepare/start commands.
- [kids-storybook-creator-demo-kit capability.yml](https://github.com/capakit/kids-storybook-creator-demo-kit/blob/main/capability.yml):
  parent Kit options passed through to dependency Kits and workload env.

### `capability-test.yml`

`capability-test.yml` is the test suite consumed by `capakit test`. Tests can call MCP tools, send HTTP requests, or run
exec preflight commands against prepared workload artifacts.

**Example:**

```yaml
tests:
  - id: extract-fields
    test: extracts invoice fields
    exposed_path: /mcp
    tool: extract_invoice_fields
    inputs:
      text: "Invoice total: 42.00"
    validations:
      - $.total.eq(42)
      - $.vendor.exists()

  - id: ui-health
    test: web UI returns health JSON
    kind: http
    target:
      workload: app
      endpoint: /http
    request:
      method: GET
      path: /health
    validations:
      - $.ok.eq(true)

  - id: typecheck
    test: workload typechecks
    kind: exec
    workload: app
    command: bun run build
    validations:
      - $.exit_code.eq(0)
```

**Example test manifests:**

- [hello-world-demo-kit capability-test.yml](https://github.com/capakit/hello-world-demo-kit/blob/main/capability-test.yml):
  MCP structured result validation and exec typecheck.
- [local-image-tagger-demo-kit capability-test.yml](https://github.com/capakit/local-image-tagger-demo-kit/blob/main/capability-test.yml):
  MCP test with fixture-backed host mount input.
- [stable-diffusion-local-kit capability-test.yml](https://github.com/capakit/stable-diffusion-local-kit/blob/main/capability-test.yml):
  HTTP test against a workload-local test endpoint plus exec typecheck.
- [realtime-voice-demo-kit capability-test.yml](https://github.com/capakit/realtime-voice-demo-kit/blob/main/capability-test.yml):
  HTTP test covering multiple connected workloads and generated result assertions.

#### Test Cases

`tests` may be a list of cases, or the file may contain a single case. Test kind is inferred when possible:

- `tool` means MCP.
- `command` means exec.
- otherwise the case is HTTP.

MCP and HTTP tests can target an existing public `exposed_path`, or they can declare `target.workload` and
`target.endpoint`. Target-based tests create a temporary test expose for that case.

Exec tests run before live HTTP/MCP tests. They execute against prepared workload artifacts, so they check the managed
runtime shape rather than only the source tree.

#### Fixtures

Test fixtures live under `tests/<test-id>/<mount-id>/`. When a test runs, CapaKit auto-binds each fixture directory to
the matching declared host mount unless the same mount was explicitly passed with `--mount`.

**Example:**

```text
tests/
  extract-fields/
    uploads/
      invoice.txt
```

The `id` field controls the fixture directory name. Without `id`, CapaKit derives a slug from the test name.

#### Assertions

Assertions are JSON-path expressions evaluated against the test result:

- MCP: the MCP tool result's `structuredContent` or `structured_content` when present, otherwise the full MCP result.
- HTTP: the parsed JSON response body.
- Exec: a CapaKit-generated result object for the completed preflight command.

**MCP result value:**

```json
{
  "total": 42,
  "vendor": "ACME"
}
```

Generated MCP tools usually return `structuredContent`, so validations address the structured fields directly:

```yaml
validations:
  - $.total.eq(42)
  - $.vendor.exists()
```

If a tool returns no structured content, validations run against the full MCP result object. For example:

```json
{
  "content": [
    {
      "type": "text",
      "text": "done"
    }
  ]
}
```

MCP tool results with `isError` or `is_error` are treated as failed tests before validations run.

**HTTP result value:**

```json
{
  "ok": true,
  "items": [
    {
      "id": "invoice-1",
      "total": 42
    }
  ]
}
```

HTTP validations address the response JSON body:

```yaml
validations:
  - $.ok.eq(true)
  - $.items.lenGte(1)
  - $.items[0].total.eq(42)
```

The response body must be JSON. An empty response body is treated as `null`. HTTP responses with status `300` or higher
fail before validations run.

**Exec result value:**

```json
{
  "exit_code": 0,
  "target_count": 1,
  "targets": [
    {
      "workload": "app",
      "variant": "default",
      "artifact_root": "/path/to/prepared/artifact",
      "command_root": "/path/to/command/root"
    }
  ]
}
```

Exec validations address that generated object:

```yaml
validations:
  - $.exit_code.eq(0)
  - $.target_count.eq(1)
  - $.targets[0].workload.eq("app")
  - $.targets[0].variant.eq("default")
```

Exec tests only produce a result object when the command exits successfully. A non-zero command exit fails the test
before validations run.

**Supported assertion operators:**

```text
$.field.exists()
$.field.notNull()
$.field.eq(value)
$.field.ne(value)
$.field.gte(number)
$.field.lte(number)
$.field.lenEq(count)
$.field.lenGte(count)
$.field.lenLte(count)
$.field.contains(value)
```

Expected values are parsed as JSON when possible. Otherwise they are treated as strings.

### `workloads/`

Each source workload lives in `workloads/<workload-name>/`. CapaKit treats each workload directory as its own source
root and sandbox boundary. Workloads can read their own files, declared mounts, runtime home/cache/tmp dirs, and
CapaKit toolchain paths; they cannot read sibling workload source by default.

Typical generated Bun workload:

```text
workloads/app/
  .gitignore
  package.json
  tsconfig.json
  src/
    index.ts
    capakit_mcp.ts
```

HTTP UI workloads also include UI files and Vite config:

```text
workloads/web/
  package.json
  vite.config.ts
  src/
    index.ts
    capakit_http.ts
    ui/
      App.tsx
      index.html
      main.tsx
      styles.css
```

Run workload commands through CapaKit:

```sh
capakit exec app -- bun install
capakit exec app -- bun test
capakit exec --all -- bun run build
```

`capakit exec` runs from the workload source root. File writes affect the local workload directory, but process
execution uses the CapaKit workload sandbox.

#### Bun Workloads

The public alpha targets Bun workloads. Generated Bun workloads use `@capakit/sdk` and protocol-specific helpers:

- MCP: `@capakit/sdk/mcp`
- HTTP: `@capakit/sdk` plus Hono/Vite/React scaffold
- A2A: `@capakit/sdk/a2a`
- OpenAI-compatible: `@capakit/sdk/oaic`

Generated Bun manifests use `prepare.command: bun install`. MCP, A2A, and OpenAI-compatible workloads usually start with
`bun run src/index.ts`; generated HTTP UI workloads start with `bun run start`.

### `README.md`

`README.md` is the user-facing guide for the Kit. Keep it specific to the app, not a copy of the CapaKit manual.

**Useful README content:**

- what the app does
- how to run it
- how to test it
- required mounts or secrets
- public paths or tools users should call
- screenshots for UI or visual-output Kits
- known limitations

Coding agents should update `README.md` when app behavior, setup, commands, secrets, mounts, or user-visible outputs
change.

## CLI

The `capakit` CLI utility is the runtime and toolkit for coding agents.

Use the CLI with `-h` or `--help` options to explore.

```sh
capakit --help
capakit --version
capakit <command> --help
```

**Output Formats:**

`capakit` defaults to human-readable text output.

Use `--output json` for commands that produce structured summaries or tables:

```sh
capakit kit info --output json
capakit kit workloads list --output json
capakit storage status --output json
```

Set the default output format with:

```sh
CAPAKIT_OUTPUT_FORMAT=json
```

Supported values are `text` and `json`. `--output` takes precedence over `CAPAKIT_OUTPUT_FORMAT`.

Runtime logs are separate from command output. For machine-readable logs, set:

```sh
CAPAKIT_LOG_FORMAT=json
```

Useful diagnostic controls:

- `--verbose`: raise CapaKit log verbosity for the command.
- `--log-details lineage|fields|all|none`: include internal lineage and/or structured log fields.
- `CAPAKIT_LOG_DETAILS=lineage|fields|all|none`: default log detail mode.
- `CAPAKIT_CLI_THEME=auto|dark|light|mono`: override terminal log styling.

## Workload SDKs

Workload SDKs are used inside workload source code. They give a workload its CapaKit runtime context, expose protocol
servers to the runtime, call connected workloads, resolve declared secrets, and inspect declared host mounts.

**TypeScript SDK:**

The public alpha uses the TypeScript SDK package [`@capakit/sdk`](https://github.com/capakit/capakit-sdk-ts) from Bun
workloads.

Use the root module for runtime primitives:

```ts
import {
  createRunnerSdk,
  endpointPath,
  hostMountMid,
  secretMid,
  workloadMid,
} from "@capakit/sdk";
```

Core SDK APIs:

- `createRunnerSdk()`: creates the workload runtime SDK.
- `sdk.start()`: starts the workload's mounted protocol endpoints.
- `sdk.stop()`: stops mounted endpoints and SDK clients.
- `sdk.mount(...)`: mounts an HTTP-style endpoint handler.
- `sdk.workloads.endpoint(...)`: resolves a manifest-declared connected workload endpoint.
- `sdk.secrets.resolve(secretMid("..."))`: resolves a Kit secret listed in `exposed_secrets`.
- `sdk.mounts.get(hostMountMid("..."))`: reads a declared host mount binding.
- `endpointPath`, `workloadMid`, `secretMid`, `hostMountMid`: typed id helpers for SDK calls.

Minimal HTTP workload:

```ts
import { createRunnerSdk, endpointPath } from "@capakit/sdk";

const sdk = createRunnerSdk();

sdk.mount({
  protocol: "http",
  endpoint: endpointPath("/http"),
  handler: async () =>
    Response.json({
      ok: true,
    }),
});

await sdk.start();
```

Provider helpers live in protocol-specific modules:

- `@capakit/sdk/mcp`: `mountMcp`, `createMcpClient`.
- `@capakit/sdk/oaic`: `mountOaic`, `createOaicClient`.
- `@capakit/sdk/a2a`: `mountA2a`, `createA2aClient`.
- `@capakit/sdk/anthropic`: `createAnthropicClient`.
- `@capakit/sdk/google-ai-studio`: `createGoogleAiStudioClient`.
- `@capakit/sdk/websocket`: `connectWebSocket`.

Minimal MCP workload:

```ts
import { createRunnerSdk } from "@capakit/sdk";
import { mountMcp } from "@capakit/sdk/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";

const sdk = createRunnerSdk();
const server = new McpServer({
  name: "hello",
  version: "1.0.0",
});

mountMcp(sdk, {
  endpoint: "/mcp",
  server,
});

await sdk.start();
```

Calling a connected MCP workload:

```ts
import { createRunnerSdk, endpointPath, workloadMid } from "@capakit/sdk";
import { createMcpClient } from "@capakit/sdk/mcp";

const sdk = createRunnerSdk();
const tools = await createMcpClient(
  sdk,
  workloadMid("tools"),
  endpointPath("/mcp"),
);

const result = await tools.callTool({
  name: "lookup",
  arguments: { query: "hello" },
});
```

The target workload must be listed in the caller's `connections`; CapaKit does not expose arbitrary sibling workloads
to source code.

**Example Kits:**

- [hello-world-demo-kit](https://github.com/capakit/hello-world-demo-kit): minimal Bun MCP workload.
- [local-image-tagger-demo-kit](https://github.com/capakit/local-image-tagger-demo-kit): SDK usage with mounts,
  connected model dependency, HTTP, and MCP.
- [llama-cpp-local-kit](https://github.com/capakit/llama-cpp-local-kit): OpenAI-compatible local model workload.

## Registry

The public registry is a catalog of example and reusable AI app Kits. The source of truth is
[`github.com/capakit/apps`](https://github.com/capakit/apps); the CLI reads the registry catalog from that repository.

Use it to discover the current public Kits:

```sh
capakit registry tags
capakit registry search --tag llama.cpp
capakit registry info llama-cpp-local
```

## Support

For runtime bugs, CLI failures, docs issues, or confusing behavior, open an issue in
[github.com/capakit/cli](https://github.com/capakit/cli/issues).

Include:

- `capakit --version`
- macOS version and CPU architecture
- the command you ran
- the Kit source, preferably a GitHub link when possible
- relevant error output or logs

### Security Issues

Do not open public GitHub issues for vulnerabilities or sensitive reports. Use the security contact flow on
[capakit.com/security](/security).

Include the affected version, impact, reproduction steps, and whether the issue involves secrets, sandbox escape,
network isolation, package integrity, or unauthorized workload access.
