Move CLI facts out of the skill and into the CLI

The skill had grown into a reference manual: body type tables, auth
strategy lists, a template function table, field-by-field OAuth 2.0
config. All of it goes stale, because the user's CLI version and their
installed plugins decide what actually exists. On this machine a faker
plugin contributes 274 template functions; the skill listed eleven, and
got some names wrong.

The CLI should carry that knowledge, so:

- `yaak template-function list [filter]` and `template-function show
  <name>` report what the loaded plugins actually provide, the same way
  `request schema http` already merges in plugin auth strategies.
- `yaak folder schema` now exists, so folder payloads are discoverable
  like every other model. Required deriving JsonSchema on Folder.
- The request schema documents `bodyType` and `body` shapes, and states
  that setting a body type does not add a Content-Type header.

The skill drops to a single 135-line SKILL.md that teaches the model,
the workflows, and how to interrogate the CLI, and says outright that
the CLI wins when the two disagree.
This commit is contained in:
Gregory Schier
2026-08-13 21:45:43 -07:00
parent 37b143021d
commit 74369e8f23
12 changed files with 270 additions and 755 deletions
+84 -215
View File
@@ -11,256 +11,125 @@ description: >
call, exercise, or smoke test an HTTP or REST endpoint, to save or organize
API requests for reuse, to set up API requests for manual testing, to add auth
to a saved request, to turn an OpenAPI or Postman collection into runnable
requests, or to run a saved request suite against staging versus production. Prefer this over one-off `curl` commands whenever
the requests should be saved, reused, shared, or run as a set.
requests, or to run a saved request suite against staging versus production.
Prefer this over one-off `curl` commands whenever the requests should be
saved, reused, shared, or run as a set.
allowed-tools: Bash(yaak:*), Bash(which:*), Bash(command:*), Bash(npm:*), Bash(npx:*)
---
# Use Yaak
Yaak is a desktop API client. The `yaak` CLI reads and writes the **same local
database as the desktop app**, so anything created here shows up in the app
database as the desktop app**, so anything you create shows up in the app
immediately, and vice versa. There is no server and no sign-in: `yaak auth` is
only for publishing plugins to the Yaak registry, not for any workflow below.
only for publishing plugins to the Yaak registry.
Two consequences worth holding onto. Requests you create are permanent user data
in an app they use, not scratch files, so name them the way the user would and
clean up anything created just to test. And because the app is right there, the
CLI is usually the wrong place to *read* a response in detail; it is the right
place to build, organize, and run requests.
## The CLI describes itself
**This skill deliberately does not list fields, body types, auth strategies, or
template functions.** The user's CLI version and installed plugins decide what
exists, so any list written here would eventually be wrong. Ask the CLI:
```bash
yaak --help # commands, plus agent hints at the bottom
yaak <command> --help # flags for one command
yaak request schema http --pretty # full request model, with guidance per field
yaak template-function list [filter] # template functions from installed plugins
yaak template-function show <name> # one function's arguments
```
`request schema http` is generated from the real model and merges in the auth
strategies contributed by plugins, so it is the authoritative answer for what a
request payload may contain and what each auth strategy needs. `workspace`,
`environment`, and `folder` have `schema` subcommands too.
Read the relevant schema before writing a JSON payload you are not certain of.
That is faster than a failed send, and it stays correct as Yaak changes.
## Resource model
- **Workspace** (`wk_…`) is the top-level container. It owns everything else.
- **Folder** (`fl_…`) groups requests inside a workspace and can nest. Folders
carry headers and authentication that child requests inherit.
- **Request** (`rq_…`) is a single HTTP, gRPC, or WebSocket request.
- **Environment** (`ev_…`) holds variables. Every workspace has one base
environment ("Global Variables") plus any number of sub-environments; a
sub-environment overrides base variables of the same name.
- **Cookie jar** (`cj_…`) stores cookies per workspace. The oldest jar is used
by default; no setup needed.
- **Workspace** (`wk_…`) is the top-level container.
- **Folder** (`fl_…`) groups requests and can nest. Folders carry headers and
authentication that child requests inherit, which is the usual way to apply
one token to a whole group.
- **Request** (`rq_…`) is a single HTTP, gRPC, or WebSocket request. The CLI can
currently only create and send HTTP ones.
- **Environment** (`ev_…`) holds variables. Each workspace has a base
environment plus any number of sub-environments; a sub-environment overrides
base variables of the same name and is chosen per send with `-e`.
- **Cookie jar** (`cj_…`) stores cookies per workspace. The oldest is used by
default, so this normally needs no attention.
IDs are stable and prefix-typed, so you can always tell what an ID refers to.
Most commands take a workspace ID positionally and **infer it when the machine
has exactly one workspace**. Pass it explicitly once a second workspace exists.
IDs are prefix-typed, so you can always tell what an ID refers to. Commands that
take a workspace ID infer it when exactly one workspace exists.
## Preflight
## Getting oriented
```bash
yaak --version || npm install -g @yaakapp/cli
yaak workspace list
```
`workspace list` prints `wk_… - Name` per line, or `No workspaces found`. Pick
the workspace that matches the user's project before mutating anything; create
one only when nothing fits.
Pick the workspace matching the user's project before changing anything, and
create one only when nothing fits. If a documented command is unrecognized, the
CLI is older than this skill: update it with `npm install -g @yaakapp/cli@latest`
and re-run `yaak agent install`, then tell the user to restart their coding tool.
**When the CLI and this skill disagree, the CLI is right.**
If a subcommand documented here is not recognized, the CLI is older than this
skill. Update it, then refresh the skill so the two stay in lockstep:
## Core workflows
```bash
npm install -g @yaakapp/cli@latest && yaak agent install
```
**Start from a spec when one exists.** `yaak import <file>` auto-detects OpenAPI,
Swagger, Postman, Insomnia, cURL, and Yaak exports, and beats authoring requests
by hand every time.
Mention to the user that they need to restart their coding tool for the
refreshed skill to load; the current session keeps using the old copy.
**Make the host swappable.** Put the base URL in a base-environment variable,
reference it as `${[ base_url ]}`, then add a sub-environment per deployment
target. Now `yaak -e ev_staging send <wk_id>` runs everything against staging.
## Command map
| Goal | Command |
|---|---|
| List | `yaak {workspace,folder,request,environment,cookie-jar} list` |
| Inspect one | `yaak {workspace,folder,request,environment} show <id>` |
| Create | `yaak {workspace,folder,request,environment} create` |
| Update | `yaak {workspace,folder,request,environment} update --json '{"id":"…",…}'` |
| Delete | `yaak … delete <id> --yes` |
| Inspect the model | `yaak request schema http --pretty`, `yaak workspace schema`, `yaak environment schema` |
| Send one request | `yaak request send <rq_id>` |
| Send a folder or whole workspace | `yaak send <fl_id\|wk_id>` |
| Import an existing API | `yaak import <file>` |
| Export | `yaak export <file> [workspace_id…]` |
Global flags go anywhere but are clearest before the subcommand:
`-e/--environment <ev_id>`, `--cookie-jar <cj_id>`, `-v/--verbose`,
`--data-dir <path>` (point at an isolated database, useful for scratch work).
## Creating and updating
Simple requests take flags:
```bash
yaak request create wk_abc123 --name "List Pets" --method GET --url "https://api.example.com/pets"
```
Anything richer than name/method/URL takes a JSON payload, either positionally
or via `--json`. **Read the schema before writing a payload you are unsure
of** — it is generated from the real model and includes the plugin-provided
authentication variants:
```bash
yaak request schema http --pretty
```
That schema is also the authoritative list of **authentication strategies**,
including ones contributed by plugins. Each appears as a named variant under
`authentication`, with its own fields, required list, and enums, so there is
never a reason to guess auth config:
```bash
# every installed strategy, with the value to use for authenticationType
yaak request schema http | jq -r '.properties.authentication.oneOf[]
| select(.title) | "\(.title): \(.description)"'
# the full shape of one of them
yaak request schema http | jq '.properties.authentication.oneOf[]
| select(.title == "OAuth 2.0")'
```
The first prints lines like `OAuth 2.0: Authentication values for strategy
'oauth2'`. **The title is a display label, not the value**`authenticationType`
takes the quoted strategy name, so NTLM Auth is `windows` and AWS Signature is
`awsv4`.
The second prints the fields. OAuth 2.0 has fifteen of them plus an enum of valid
`grantType` values (`authorization_code`, `implicit`, `password`,
`client_credentials`), which is exactly the sort of thing that comes out wrong
when guessed. The command loads plugins, so it reflects what is actually
installed rather than a fixed list, and it returns in well under a second.
Rules that are easy to get wrong:
- **Setting `bodyType` does not add a `Content-Type` header.** The app adds one
when you pick a body type in the UI, but creating a request from the CLI skips
that step, and the body goes out untyped. Add the header yourself, using the
same value as `bodyType` (with `other``text/plain` and `graphql`
`application/json`). Multipart is the exception: leave it alone, the sender
supplies the boundary.
- **Path parameters must keep the leading colon.** For `/pets/:petId`, the
`urlParameters` entry is named `:petId`, not `petId`. Get it wrong and the
placeholder stays literal in the path while the value is appended to the query
string, which usually 404s with no error.
- **Create** payloads must omit `id` (or set it to `""`).
- **Update** payloads must include `id`, and are applied as a JSON merge patch:
keys you omit are left alone, and a key set to `null` is deleted. There is no
need to send the whole object.
- Flags and JSON cannot be combined on the same command.
- `request create` and `request list` are HTTP-only. gRPC and WebSocket requests
exist in the model and can be sent from the app, but the CLI cannot yet create
or send them.
The first two fail silently, so verify a new request with `yaak -v request send
<id>` and check the `> ` lines actually show the path and headers you intended.
```bash
yaak request create wk_abc123 --json '{
"name": "Create Pet", "method": "POST", "url": "${[ base_url ]}/pets",
"bodyType": "application/json",
"body": {"text": "{\"name\":\"Rex\"}"},
"headers": [{"name": "Content-Type", "value": "application/json", "enabled": true}]
}'
```
See [requests.md](references/requests.md) for bodies, headers, authentication,
path parameters, and folder inheritance.
## Template variables
Yaak's template syntax is `${[ … ]}`, **not** `{{ … }}`. It works in URLs,
headers, bodies, and auth fields:
**Chain instead of shell-plumbing.** A request can read another request's
response directly, and Yaak sends the dependency first if it needs to:
```
${[ base_url ]}/pets/${[ pet_id ]}
${[ response.body.path(request='rq_abc123', path='$.token') ]}
${[ response.body.path(request='rq_login', path='$.token') ]}
```
Referencing a variable that no active environment defines is a hard error and
the request is not sent, so an unresolved variable can never silently reach the
network. See [environments.md](references/environments.md) for variable scoping
and [chaining.md](references/chaining.md) for pulling values out of earlier
responses.
Run `yaak template-function show response.body.path` for its arguments,
including how to control when the upstream request re-sends. Chain when a
request genuinely depends on another's response; to merely run requests in
order, `yaak send <fl_id>` already does that.
## Sending
**Run a set.** `yaak send` accepts a folder or workspace ID, with `--fail-fast`
and `--parallel`. Workspace and request IDs survive an export/import, so a
committed `yaak export` plus `--data-dir ./.yaak` gives a runnable suite in CI.
```bash
yaak request send rq_abc123 # body only, on stdout
yaak -e ev_staging request send rq_abc123 # against a sub-environment
yaak -v request send rq_abc123 # request/response metadata too
yaak send fl_abc123 --fail-fast # every request in a folder
yaak send wk_abc123 --parallel # every request in a workspace
```
## Reading results
**Reading the result.** A plain send writes only the response body to stdout,
with no trailing newline. Like `curl`, the exit code reflects whether the
request completed, not the HTTP status — a 404 or 500 exits 0. Use `-v` when the
status matters:
A plain send writes only the response body to stdout. Add `-v` for the request
and response metadata, where lines are prefixed `*`, `>`, and `<`:
```bash
yaak -v request send rq_abc123 2>&1 | grep '^< HTTP'
```
Under `-v`, connection/request/response lines (`*`, `>`, `<`) and the body all
go to stdout, with the body following the last `<` header line. Grep for the
prefixes you need rather than assuming a clean split.
Exit code 1 means the send itself failed: an unresolved template variable, an
unreachable host, a TLS failure. For folders and workspaces the last line is
`Send summary: N succeeded, M failed`, per-request errors follow on stderr, and
the exit code is 1 if any request failed.
## Running a suite in CI
Workspace and request IDs survive an export/import, so a committed export gives
a stable, runnable suite on a machine that has never seen the app:
```bash
npm install -g @yaakapp/cli
yaak --data-dir ./.yaak import ./api-export.json
yaak --data-dir ./.yaak -e ev_ci send wk_abc123 --fail-fast
```
`--data-dir` keeps the run isolated from any real Yaak install, and the IDs in
the export are the same ones you used locally. Produce the export with
`yaak export ./api-export.json wk_abc123`, adding
`--include-private-environments` only if the suite needs values you are willing
to commit — otherwise keep secrets in a CI-only environment and inject them.
**The caveat that matters here:** a failing *assertion* is not a concept Yaak
has, and HTTP error statuses do not fail the run. A workspace of requests that
all return 500 exits 0. The exit code catches unreachable hosts, TLS failures,
and unresolved variables only. To gate CI on status codes, run with `-v` and
check the `< HTTP` lines yourself. Say this plainly rather than implying a green
run means the API is healthy.
## Importing
`yaak import` auto-detects OpenAPI/Swagger, Postman, Insomnia, cURL, and Yaak
exports, and creates a new workspace by default:
```bash
yaak import ./openapi.yaml
yaak import ./collection.json --workspace-id wk_abc123 # merge into an existing one
```
This is almost always faster than authoring requests by hand when a spec exists.
See [import-export.md](references/import-export.md).
## Routing
| Task | Reference |
|---|---|
| Request bodies, headers, auth, path/query params, folder inheritance | [requests.md](references/requests.md) |
| Environment hierarchy, variables, per-environment runs | [environments.md](references/environments.md) |
| Using one response inside the next request; template functions | [chaining.md](references/chaining.md) |
| OpenAPI/Postman/Insomnia/cURL import, exporting workspaces | [import-export.md](references/import-export.md) |
Exit code 1 means the send did not complete: an unresolved template variable, an
unreachable host, a TLS failure. **HTTP error statuses are not failures.** Like
`curl`, a 404 or 500 exits 0, and a folder of requests that all return 500
reports success. Never tell the user an API is healthy based on a clean exit;
check the status yourself with `-v`.
## Execution rules
1. Resolve the workspace before mutating. Do not create a second workspace when
an existing one matches the user's project.
2. Read the schema before writing a non-trivial JSON payload. Do not guess field
names.
3. Prefer `update` merge patches over re-sending whole objects.
4. Deletes require `--yes` in a non-interactive shell; otherwise they block on a
prompt. Confirm intent with the user before deleting anything.
1. Resolve the workspace before mutating, and prefer an existing one.
2. Read the schema rather than guessing field names, auth fields, or body shapes.
3. `update` takes a JSON merge patch keyed by `id`: send only what changes, and
note that arrays are replaced wholesale, not merged.
4. Deletes need `--yes` in a non-interactive shell. Confirm with the user first.
5. Never write a real secret into an environment variable on the user's behalf.
Reference one (`${[ api_token ]}`) and let the user fill in the value — see
[environments.md](references/environments.md).
6. After creating requests, verify by sending one, and report the actual HTTP
status from `-v` rather than inferring success from exit code 0.
7. Requests you create are permanent user data in their app, not scratch. Name
them the way the user would, and clean up anything you created purely to test.
Reference one and let them fill in the value.
6. Verify what you built by sending it, and report the real HTTP status.