---
phase: 13.2
status: shipped
date: 2026-04-18
audience: Mandu users (app developers + CI operators)
source_commands:
  - packages/cli/src/commands/db-seed.ts
  - packages/cli/src/commands/upgrade.ts
  - packages/cli/src/commands/mcp-register.ts
---

# Quick CLI commands — `db seed`, `upgrade`, `mcp register`

Three day-to-day CLI commands that land with Phase 13.2. All three
support `--dry-run`, emit stable exit codes, and are safe to script.

---

## `mandu db seed`

Replay deterministic fixture data against the live database. Seeds live
in `spec/seeds/*.seed.ts` and are versioned separately from schema
migrations (Phase 4c).

### Usage

```bash
mandu db seed                          # default env=dev
mandu db seed --env=staging
mandu db seed --env=prod               # requires MANDU_DB_SEED_PROD_CONFIRM=yes
mandu db seed --file=001_users         # prefix filter
mandu db seed --dry-run                # print INSERT previews, no execution
mandu db seed --reset                  # truncate tables before seeding
```

### Seed file shapes

**Declarative** — simplest path:

```ts
// spec/seeds/001_admin.seed.ts
export default {
  resource: "user",
  key: "email",                     // upsert conflict column
  env: ["dev", "staging"],          // default = ["dev", "staging"]
  data: [
    { email: "admin@example.com", name: "Admin" },
  ],
};
```

**Imperative** — when you need hashing, derived data, CSV imports, etc:

```ts
// spec/seeds/002_fixtures.seed.ts
export default async function seed(ctx) {
  await ctx.upsert("user", [
    { email: "alice@example.com", name: "Alice" },
  ], { by: "email" });
}

export const env = ["dev"] as const;
```

### Safety

- Every identifier flows through `quoteIdent()` — SQL injection blocked.
- Every value is parameter-bound via `Bun.SQL` — never concatenated.
- Rows are validated against the resource's Zod schema before any INSERT.
- Each seed file runs inside a transaction — any failure rolls back the whole file.
- Tamper detection: if a previously-applied file's bytes change, the runner
  refuses to replay until you `--reset`.

### Exit codes

| Code | Meaning                                            |
|------|----------------------------------------------------|
| 0    | success (or nothing to seed under current env)     |
| 1    | I/O, validation, or SQL error                      |
| 2    | usage error                                        |
| 3    | tampered seeds (a previously-applied file changed) |
| 4    | refused (`--env=prod` without confirmation)        |

---

## `mandu upgrade`

Self-updater for both the npm-installed `@mandujs/*` packages and the
standalone compile-binary builds.

### Usage

```bash
mandu upgrade                 # bun update @mandujs/* (package mode)
mandu upgrade --check         # show installed vs latest
mandu upgrade                 # self-update (binary mode, Phase 9b)
mandu upgrade --dry-run       # download + verify; skip the swap
mandu upgrade --rollback      # restore the previously-replaced binary
mandu upgrade --channel=canary  # use pre-release tags
```

The CLI auto-detects which mode to use based on `process.execPath`.
When invoked via `bun run mandu` or `bunx @mandujs/cli`, it runs in
package mode. When launched as a compile-binary (`mandu.exe` /
`mandu-bun-linux-x64`), it runs in binary mode.

### Binary integrity

Every GitHub Release ships a `SHA256SUMS.txt` sidecar generated by the
`release-binaries.yml` workflow. `mandu upgrade` downloads the sidecar,
verifies the SHA-256 of the fetched asset against it, and refuses to
swap if the hashes diverge.

Phase 11.A SLSA Build L2 provenance is attached to every artifact via
`actions/attest-build-provenance`. For the strongest check run
`gh attestation verify mandu-bun-linux-x64 --repo konamgil/mandu`
before invoking `mandu upgrade`.

### Atomic replacement

- **POSIX**: plain `rename(new, current)` — the running process keeps
  its own file descriptor open.
- **Windows**: rename the running `.exe` to `.old.<pid>.exe` first
  (allowed even while the image is loaded), then move the new binary
  into place. The old copy is stashed in `~/.mandu/bin/previous/` so
  `--rollback` can restore it.

### Exit codes

| Code | Meaning                              |
|------|--------------------------------------|
| 0    | upgrade applied (or `--check`)       |
| 1    | network / integrity / I/O failure    |
| 2    | usage error                          |
| 3    | rollback requested but nothing to restore |

---

## `mandu mcp register`

Automatic IDE integration — writes a Mandu MCP server entry into four
popular editors/coding assistants without overwriting their existing
configuration.

### Supported IDEs

| IDE         | Config path (per-user)            | Notes                    |
|-------------|-----------------------------------|--------------------------|
| Claude Code | `~/.claude/mcp.json`              | JSON(-C) tolerance       |
| Cursor      | `~/.cursor/mcp.json`              | JSON(-C) tolerance       |
| Continue    | `~/.continue/config.json`         | JSON(-C) tolerance       |
| Aider       | `<cwd>/.aider.conf.yml`           | YAML — project-local     |

### Usage

```bash
mandu mcp register                              # register for all four
mandu mcp register --ide=claude
mandu mcp register --ide=all
mandu mcp register --remove                     # remove Mandu entry
mandu mcp register --dry-run                    # preview without writing
mandu mcp register --token=env:MY_TOKEN         # custom env var
```

### Safety

- Reads existing config, merges into `mcpServers.mandu` without touching
  siblings.
- Stashes `<config>.bak.<unix-ms>` next to every modified file (atomic
  rename).
- Tolerates JSON-C comments in Claude/Cursor/Continue configs on read
  (comments are stripped on write — surfaced in the success message).
- Aider YAML merge preserves preamble + trailing keys verbatim.
- `env` values default to `${localEnv:MANDU_MCP_TOKEN}` placeholder —
  tokens are NEVER embedded in the config file unless you pass the raw
  value explicitly.

### Token strategies

| Flag                      | Behaviour                                          |
|---------------------------|----------------------------------------------------|
| (none)                    | `${localEnv:MANDU_MCP_TOKEN}` placeholder          |
| `--token=env:CUSTOM`      | `${localEnv:CUSTOM}` placeholder                   |
| `--token=generate`        | Generate a random base64url token, store inline    |
| `--token=<literal>`       | Store the literal value (NOT recommended — plain text) |

### Exit codes

| Code | Meaning                          |
|------|----------------------------------|
| 0    | success                          |
| 1    | I/O / parse error                |
| 2    | usage error (unknown `--ide`)    |

---

## Shared conventions

- **`--dry-run`**: every command previews its actions without side
  effects. Safe to use in CI gatekeeper pipelines.
- **`--json`**: machine-readable output on stdout (available on db seed).
- **Exit codes are stable**: shell wrappers may branch on them. They
  will not change within a major version.
- **No destructive action without explicit flag**: `--reset`, `--force`,
  `MANDU_DB_SEED_PROD_CONFIRM` — every destructive path is gated.

## See also

- `docs/bun/phase-13-diagnostics/cli-extensions.md` — design context
- `docs/rfcs/0001-db-resource-layer.md` — Phase 4c DDL + migrations
- `.github/workflows/release-binaries.yml` — SLSA-attested binary releases
