Bruno
Bruno, the Git-friendly API client, and its CLI (bru). Use when authoring or editing .bru requests/collections, generating a collection from an OpenAPI spec or a codebase's routes, writing Bruno tests or scripts, managing environments and secrets, or running collections with `bru run` (local or CI).From its SKILL.md
npx -y skills add punkaze/skills --skill brunoAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
- runs commandsInstructs the agent to run 3 commands, including `npm install -g @usebruno/cli` and 2 more.
SKILL.md
6.5 KB, ~1.7k tokens by cl100k_base, as published. Nobody here has run it
Bruno — Author, Generate, and Run .bru Collections
Requires the Bruno CLI: npm install -g @usebruno/cli. Everything below is
verified against bru 3.5.0 (bru --version).
Generating a collection — two paths
Path A — an OpenAPI/Swagger spec exists (preferred). Look for one first:
openapi.yaml, swagger.json, a framework's swagger endpoint (Elysia,
NestJS @nestjs/swagger, etc.). Then:
bru import openapi -s <spec.yaml|url> -o <out-dir> -n "My API" --collection-format bru -g path
--collection-format bru is required — the CLI default (opencollection)
emits .yml files, not a .bru collection. -g picks folder grouping: path
(by URL structure, usually what you want) or tags (by OpenAPI tags, the CLI
default). Done when <out-dir> contains bruno.json and .bru files; an
opencollection.yml there means the format flag was dropped.
Path B — no spec. Read the codebase and write a routes manifest (shape below), then run the bundled generator:
node "${CLAUDE_PLUGIN_ROOT}/skills/bruno/scripts/generate-bruno.mjs" --manifest routes.json --out ./api-collection
Flags: --force (overwrite existing files), --dry-run (preview, write
nothing). Extract routes by reading the code — do not write a
per-framework regex route scanner. Done when the generator reports
wrote N file(s) and, with the API running, bru run . -r --env <env> from
the output dir executes every request.
Routes manifest
Schema: ${CLAUDE_PLUGIN_ROOT}/skills/bruno/references/routes.schema.json —
full example: ${CLAUDE_PLUGIN_ROOT}/skills/bruno/references/sample-manifest.json.
{
"name": "My API",
"baseUrl": "http://localhost:3000",
"auth": { "type": "bearer" },
"environments": [{ "name": "dev", "baseUrl": "http://localhost:3000", "secretVars": ["authToken"] }],
"routes": [
{ "method": "POST", "path": "/auth/login", "name": "Login", "body": {"email":"","password":""},
"auth": false, "script": "token-capture", "tests": ["status-2xx"] },
{ "method": "GET", "path": "/users/:id", "name": "Get User", "auth": true, "tests": ["status-200"] }
]
}
- Manifest-level
auth.typebecomes collection-level auth incollection.bru; routes withauth: trueinherit it. - Route
auth:true→auth: inherit,false→none, or an explicit"bearer"|"basic"|"inherit"|"none"per-request override. - Use
:paraminpathfor path params. script: "token-capture"adds a post-response script that capturesres.body.tokeninto the runtime variableauthToken.testsacceptsstatus-200,status-201,status-2xx.
.bru syntax (verified against Bruno 3.5.0)
meta {
name: Get User
type: http
seq: 1
tags: [smoke]
}
get {
url: {{baseUrl}}/users/:id
body: none
auth: inherit
}
params:path {
id: 1
}
params:query {
expand: profile
}
headers {
Accept: application/json
}
body:json {
{
"name": "John"
}
}
script:post-response {
if (res.status >= 200 && res.status < 300 && res.body && res.body.token) {
bru.setVar("authToken", res.body.token);
}
}
tests {
test("status is 200", function() {
expect(res.status).to.equal(200);
});
}
docs {
Retrieve a single user by id.
}
Critical syntax rules (these were wrong in other community skills):
- Query params use
params:query { }— NOT legacyquery { }. - Path params: keep
:paramin the URL and add aparams:path { }block — do NOT substitute{{param}}. - Child requests should use
auth: inheritto reuse collection/folder auth; only emitauth:bearer { token }/auth:basic { ... }for an explicit per-request override.
Collection-level auth — collection.bru
auth: inherit resolves against collection.bru at the collection root (or a
folder's folder.bru). Without it, "inherited" requests send no auth at all:
meta {
name: My API
}
auth {
mode: bearer
}
auth:bearer {
token: {{authToken}}
}
Environments
vars {
baseUrl: http://localhost:3000
}
vars:secret [
authToken
]
Secret variable names go in vars:secret [ ]; their values are never
written to disk — set them at runtime or via the GUI. Bruno also supports typed
vars (@number, @boolean, @object) and multiline values with '''…'''.
Variables in scripts
bru.setVar(key, val)/bru.getVar(key)— runtime/in-memory, scoped to a single run, never written to disk. Use this for chaining secrets like auth tokens between requests.bru.setEnvVar(key, val)/bru.getEnvVar(key)— environment variable. The Bruno GUI can save environment edits into the environment file — a secret set this way can end up committed to Git. UsesetEnvVaronly for non-secret config; prefersetVarfor tokens.- Response:
res.status,res.body,res.responseTime.
Running with bru run (CLI 3.5.0)
Run from the collection root (the directory containing bruno.json) —
anywhere else fails with "You can run only at the root of a collection".
bru run . -r --env dev # run the whole collection recursively
bru run folder -r --env dev # run a folder recursively
bru run request.bru --env-file env.bru # explicit env file
bru run . -r --env dev --env-var token=$TOK # override one var from shell (no secret in history)
bru run . -r --reporter-junit results.xml --reporter-html report.html
bru run . -r --bail --tests-only # CI: stop on first failure, only requests with tests
bru run . -r --csv-file-path data.csv --parallel # data-driven, parallel iterations
bru run . -r --tags smoke --exclude-tags wip # filter by meta tags
Sandbox (v3 default changed): the CLI defaults to --sandbox safe — no
filesystem access, no require() of external npm packages. Only pass
--sandbox developer when you trust the collection and genuinely need fs/exec/
npm in scripts; it is dangerous with untrusted collections.
Secrets: prefer --env-file or --env-var KEY=$SHELL_VAR; never paste a live
secret literal on the command line (it leaks into shell history).
What ships with it: 4 files
18.3 KB alongside SKILL.md, 2 of them executable
references/
- routes.schema.json1.7 KB
- sample-manifest.json785 B
scripts/
- generate-bruno.mjsruns7.8 KB
- generate-bruno.test.mjsruns8.1 KB