# AGENTS.md — using `wk` from an LLM-driven agent
This file is for **AI agents** (Claude, GPT, Cursor, code assistants) and
the humans orchestrating them. If you're generating shell calls to `wk`
from a model, this is the contract — follow it and the surface stays
predictable.
You can also read this guide directly from an installed binary:
```sh
wk agents
```
Human-facing docs live at <https://wavekat.com/docs/cli/>.
## Install
```sh
Verify:
```sh
wk --version
wk version --json # also probes /api/health on the platform
```
Supported targets: macOS (arm64, x86_64), Linux (x86_64, aarch64; musl-static).
## Authentication
Two paths. **Pre-minted token is the right one for non-interactive agents.**
```sh
export WK_TOKEN='wk_…' # required
export WK_BASE_URL='https://platform.wavekat.com' # optional; this is the default
wk login # verifies + persists the token
```
After `wk login` succeeds, the token is saved to disk (`~/.config/wavekat/auth.json`
on Linux, `~/Library/Application Support/wavekat/auth.json` on macOS, mode `0600`).
Subsequent commands read it from disk; you don't need to keep `WK_TOKEN`
exported.
For interactive use only (a real human at a real keyboard):
```sh
wk login # opens a browser
wk login --no-browser # prints a URL the user opens manually (e.g. SSH)
```
`wk logout` revokes the current token and clears the local file.
## Output contract
Every read command takes `--json`. Without it you get a styled human
table — **do not parse the non-JSON output**, it includes ANSI codes
and the layout is not stable.
| `wk version --json` | `cli`, `api`, `endpoint` |
| `wk projects list --json` | `projects`, `page`, `pageSize`, `total`, `totalPages` |
| `wk projects show <id> --json` | full project row |
| `wk annotations list <project-id> --json` | `annotations`, `page`, `pageSize`, `total`, `totalPages` |
| `wk exports list <project-id> --json` | `exports`, `page`, `pageSize`, `total`, `totalPages` |
| `wk exports show <id> --json` | full export row |
| `wk exports create … --json` | the newly created export row (includes `id`, `status`) |
| `wk files list <project-id> --json` | `files`, `page`, `pageSize`, `total`, `totalPages` |
| `wk files reserve <id> [<id>…] --json` | array of `{id, …}` rows (one per file) on success |
| `wk files unreserve <id> [<id>…] --json` | array of `{id, ok\|error}` rows |
| `wk files summary <project-id> --json` | `{fileCount, annotationCount, labelledSeconds}` |
| `wk models list <project-id> --json` | `models`, `page`, `pageSize`, `total`, `totalPages` |
| `wk models show <id> --json` | full model row (lineage, metrics, artifacts list) |
| `wk models push … --json` | the finalized model row (or existing row if idempotent) |
Local file producers (`wk exports download`, `wk exports adapt smart-turn`)
write files to disk and print the output path on stdout. Progress goes
to stderr.
`--json` is a pass-through — every field the platform returns on a list
row is visible. Useful fields the human table also surfaces (so an agent
can target the same signal):
| `projects list` | `myRoleInProject`, `filesCount`, `annotationsCount`, `annotationsReviewedCount`, `updatedAt` |
| `files list` | `annotationCount`, `labelCounts` (per-key), `labelledSeconds`, `sampleRate`, `testReservedAt` |
| `exports list` | `status`, `clipCount`, `clipsTotal` / `clipsWritten` (writer progress), `totalBytes`, `splitCounts` |
| `models list` | `valF1`, `valThreshold`, `testF1`, `testF1Ci95Low/High`, `testAp`, `createdByLogin`, `status` |
## Exit codes
- `0` — success
- non-zero — error; a single-line message goes to stderr (anyhow-style
context chain). There are no fine-grained codes. To distinguish
"command failed" from "command succeeded but returned an empty list",
rely on the exit status and the JSON document on stdout — never on
parsing stderr.
## Self-update
```sh
wk update --check # is a newer release out?
wk update # download + replace this binary
wk update --version v0.0.7 # pin a specific tag
```
`wk update` reuses the official `install.sh` and writes to the same
directory the running binary lives in.
## Discovery
`wk` is built on clap; every subcommand has self-describing help. A
model that can run shell commands can explore the full surface
without external docs:
```sh
wk --help # top-level
wk exports --help # one subcommand group
wk exports create --help # all flags, types, defaults
```
When in doubt, run `--help` rather than guessing flags.
## Recipes
### Confirm auth is wired up
```sh
wk me --json # exits non-zero if not signed in
```
### List every project the current user can see
```sh
### Snapshot a labelled project into a HuggingFace-loadable dataset
```sh
EXPORT_ID=$(
wk exports create "$PROJECT_ID" \
--name "snapshot $(date -I)" \
--review-status approved \
--label-key end_of_turn \
--label-key continuation \
--split random --seed 42 --ratios 0.8,0.1,0.1 \
--json | jq -r .id
)
wk exports download "$EXPORT_ID" --out ./snapshot
wk exports adapt smart-turn \
--export-dir ./snapshot \
--out ./dataset \
--language zh
```
### Poll an export until it's ready
```sh
until [ "$(wk exports show "$EXPORT_ID" --json | jq -r .status)" = "ready" ]; do
sleep 5
done
```
### Push a trained model after a lab run
```sh
# Drop the FP32 + INT8 ONNX checkpoints and the run's results.json
# into the registry. The push is idempotent on
# (training-export, recipe, sha256(model.onnx)) — re-running with the
# same files exits 0 without re-uploading.
wk models push \
--project "$PROJECT_ID" \
--training-export "$EXPORT_ID" \
--recipe specaugment \
--results ./checkpoints/specaugment/results.json \
--artifact ./checkpoints/specaugment/onnx/model.onnx \
--artifact ./checkpoints/specaugment/onnx/model.int8.onnx \
--name "smart-turn-zh 0504-specaug" \
### List every model trained on a given export
```sh
wk models list "$PROJECT_ID" --training-export "$EXPORT_ID" --json \
### Download a specific INT8 ONNX
```sh
wk models download "$MODEL_ID" --artifact model.int8.onnx --out ./
```
### Find every annotation that needs review
```sh
wk annotations list "$PROJECT_ID" \
--review-status needs_fix --review-status unreviewed \
--json
```
### Reserve a stable held-out test set
The platform supports per-file test-set reservation
(see `docs/08-test-set-reservation.md` in `wavekat-platform`). Reserved
files get pinned to the `test` split on every export so the holdout
stays stable across reshuffles. Owner / `root` only.
```sh
# Mark a curated batch of files as the test set.
wk files reserve "$FILE_ID_A" "$FILE_ID_B" "$FILE_ID_C"
# Inspect the reservation surface for a project.
wk files summary "$PROJECT_ID" --json
wk files list "$PROJECT_ID" --test-reserved true --json
# Export with the reserved files as the test split. Note the 2-tuple
# `--ratios` — the third slot is implicit 0 because `test` is filled
# from reserved files only.
wk exports create "$PROJECT_ID" \
--name "snapshot $(date -I)" \
--review-status approved \
--use-reserved-test-files \
--split random --seed 42 --ratios 0.9,0.1 \
--json
```
## Quirks worth knowing
- **`wk login` runs a loopback OAuth handshake.** Don't try to script
it without `WK_TOKEN`; there is no headless-browser fallback.
- **`wk exports create` blocks** until the platform finishes copying
clips to R2. Seconds-to-minutes is normal. Exit status reflects
success/failure of the whole operation, not just submission.
- **`wk exports download` fetches clips in parallel** (default 8
concurrent). Tune with `--concurrency N`; cranking past ~16 hits
Worker subrequest budgets without meaningfully improving wall time.
The bar tracks every manifest entry — already-on-disk clips count
toward progress, so resumes look fast.
- **All list endpoints paginate.** Default `--page-size` is 20. Use
`total` / `totalPages` to know when to stop.
- **The smart-turn adapter only handles two label keys** (`end_of_turn`
→ 1, `continuation` → 0). Richer label sets must be collapsed at
export time via the `--label-key` filter, not silently in the
adapter.
- **The smart-turn adapter canonicalises audio.** Every clip is decoded,
downmixed to mono, resampled to 16 kHz, and re-encoded as 16-bit PCM
WAV before landing in the parquet. A clip that won't decode aborts
the run with the failing path in the error — agents can treat such
errors as a manifest/clip integrity issue, not an adapter bug.
- **Crash reporting is on by default** in the shipped binary. The CLI
sends anonymous error events (version, OS, subcommand, templated
endpoint, error category) to Sentry. Request bodies, response
bodies, file paths, tokens, and argv values are never sent — see
the scrubber in `src/telemetry.rs`. Disable for a run with
`WK_TELEMETRY=0`, or persistently with `wk config telemetry off`.
Agents running in a CI sandbox where you don't want any network
side effect beyond the platform call should set `WK_TELEMETRY=0`.
## Reporting problems
If `--json` shapes look inconsistent, a flag is missing, or `wk` is
misbehaving in a way that breaks agent use specifically, open an
issue at <https://github.com/wavekat/wavekat-cli/issues> and mention
that the report comes from agent integration.