firegoat 0.22.0 → 0.22.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +20 -253
  2. package/dist/cli.js +1 -1
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,40 +1,28 @@
1
1
  # firegoat
2
2
 
3
- Local AX evals: how well coding agents use your SDK, CLI, API and docs. firegoat gives your own `claude` one prompt in a fresh folder, saves the session, and runs your checks.
3
+ Firegoat is an agent experience platform that lets devtools see how agents use their product and improve the agent experience.
4
4
 
5
- Status: early. See `plan.md`.
5
+ It's for developers who build for agents as customers: SDKs, CLIs, APIs, MCP servers, cloud products, local products, and the people writing documentation.
6
6
 
7
- ## Quickstart
7
+ So far we've got the CLI. It lets anyone use a simplified version of our product, running on their local machine with their own Claude subscription and the Claude Code they already have installed. Support for other harnesses is coming soon.
8
8
 
9
- ```bash
10
- npx firegoat@latest init # Creates firegoat.yaml and .firegoat/, installs the firegoat-onboarding skill, copies a prompt for your agent
11
- # Edit firegoat.yaml (or paste the prompt into your coding agent and let it)
12
- npx firegoat@latest eval # Runs your claude on each test, in .firegoat/runs/<runId>/<test>/<n>/work
13
- ```
9
+ ## What you get
14
10
 
15
- That's it: two commands. `npx firegoat@latest eval --dry-run` checks `firegoat.yaml` and lists the runs it would start (`<test>#<n> [model]`, with step counts) without building, running claude or uploading; config errors exit 3 as in a real eval. Use `npx firegoat@latest`: plain `npx firegoat` can keep running an old cached version. `eval` prints its version first (`firegoat X.Y.Z`), and `summary.json` records it as `firegoatVersion`. `claude` must be installed and logged in (run `claude` once).
11
+ When you run `firegoat eval`, you get visibility into how agents use your product: whether they fail or succeed, and how much time they take doing it. You can point them at your local codebase or at your cloud product, and see how Claude acts on the simple tasks your product aims to do.
16
12
 
17
- `init` installs the firegoat-onboarding skill globally, into `~/.claude/skills/` (in a terminal it asks: global, this repo, or none). `--local-skills` puts it in this repo's `.claude/skills/` instead (commit it so teammates and CI get it); `--no-skills` skips it.
18
-
19
- ## Upload to firegoat.ai
13
+ ## Start
20
14
 
21
15
  ```bash
22
- npx firegoat login # Device flow: open the URL, enter the code; key saved to ~/.firegoat/credentials.json (0600)
23
- npx firegoat eval # Now also uploads each run as a private trace and prints its URL
24
- npx firegoat logout # Forgets the login (revoke the key itself on the firegoat.ai traces page)
16
+ npx firegoat@latest init
25
17
  ```
26
18
 
27
- Logging in means agreeing to the firegoat.ai [Terms of Service](https://firegoat.ai/terms) and [Privacy Policy](https://firegoat.ai/privacy); login says so before showing the code. `init` offers the login in a terminal (`--no-login` skips it). In CI, set `FIREGOAT_API_KEY=fire_...` instead. `eval --no-upload` keeps runs local; without a key, runs stay local and eval says so in one line.
28
-
29
- - Per run, eval uploads claude's session (`session.jsonl`: prompts, tool calls and outputs, file contents, so possibly secrets from your env or repo), `checks.txt` and `stderr.txt` (check output sees `vars:`), plus the run's test, status, checks, the model asked for and the one claude reported, and (in a git repo) the branch, commit and whether the tree was dirty. firegoat.ai masks secrets when it shows a trace, but stores what was sent. Traces start private; you share one from its page.
30
- - Before any run, eval checks the key (`GET /api/traces/me`) and prints `Uploading sessions to <origin> as <email>`: a key answered with 401/403 exits 3; any other failure (unreachable, 429, 5xx, a wrong `FIREGOAT_BASE_URL` path) exits 2.
31
- - Each run prints `✓ <test>#<n> <url>` or `✗ <test>#<n> Upload failed: <why>`, and `run.json` / `summary.json` get `upload` (`{status: "uploaded", url}`, `{status: "failed", error}`, `{status: "skipped", reason}`, or null when not uploading). A failed upload never changes a run's status or eval's exit code (0/1/2 stay the eval's verdict); eval ends with `N uploads failed` on stderr. Each file may be at most 20 MiB: a bigger one fails that run's upload. Runs without a session file are skipped.
32
- - The key is never passed to the agent, build, install or checks (every `FIREGOAT_*` var is removed from their env), is never written to run files, and goes only to the base URL's origin, never through a redirect; presigned upload URLs get no auth header. But there is no sandbox: the agent runs as you, so it can read `~/.firegoat/credentials.json`, and with `FIREGOAT_API_KEY` it can read the key from firegoat's own process environment (`ps eww $PPID`, `/proc/<pid>/environ`), which no program can scrub. In CI, use a dedicated key and revoke it if a run looks off. The key can only upload and read your own traces.
33
- - `FIREGOAT_BASE_URL` (default `https://firegoat.ai/evals`) points firegoat elsewhere: https only, http only for `localhost` / `127.0.0.1`. A saved login only works for the base URL it was made for.
19
+ Then paste the prompt it prints into your agent, and let it set up the evals that point agents at your product. Then run them and see the results:
34
20
 
35
- ## Examples
21
+ ```bash
22
+ npx firegoat@latest eval
23
+ ```
36
24
 
37
- ### Can an agent get started with my npm package?
25
+ ## Example
38
26
 
39
27
  ```yaml
40
28
  build: npm pack --pack-destination "$FIREGOAT_OUT"
@@ -49,236 +37,15 @@ tests:
49
37
  - run: grep -qE 'evt_[A-Za-z0-9]+' "$FIREGOAT_TRANSCRIPT"
50
38
  ```
51
39
 
52
- `build` builds your package once; `install` installs that build into each fresh folder, so the agent uses your local code, not the registry.
53
-
54
- ### Does my CLI make sense to an agent?
55
-
56
- ```yaml
57
- build: go build -o "$FIREGOAT_OUT/acme" ./cmd/acme
58
- tests:
59
- - name: deploy
60
- prompt: Deploy the hello-world template to a preview environment with the acme CLI and print the URL.
61
- end_goal: The agent finds the right acme commands and ends with a working preview URL.
62
- checks:
63
- - run: grep -q '\$ acme deploy' "$FIREGOAT_TRANSCRIPT"
64
- ```
65
-
66
- Whatever `build` writes to `$FIREGOAT_OUT` is first on the agent's `PATH`. That only helps for binaries called by bare name: if users run your CLI via `npx`/`uvx`, pack and install it like the npm example, or `npx` fetches the registry release.
40
+ ## Uploads
67
41
 
68
- ### Can an agent find the right package from my docs alone?
42
+ Log in to your firegoat.ai account and each trace is uploaded to our system automatically. Secrets are masked in everything you see, but the uploaded files are stored as sent. You can opt out of uploading with `eval --no-upload`. You can also delete your traces on the dashboard, and the files and the record are gone from our site.
69
43
 
70
- ```yaml
71
- tests:
72
- - name: which-packages
73
- prompt: >-
74
- I'm building a mobile app with Acme. It needs a local database, maps and Sign in
75
- with Apple. Which Acme packages should I use? Write the list to packages.txt.
76
- end_goal: packages.txt names the right Acme package for each need, found in Acme's docs.
77
- checks:
78
- - run: test -s packages.txt
79
- ```
44
+ ## Links
80
45
 
81
- ### Python, three runs, and a value from the command line
46
+ - Everything else (every config key, more examples, how eval runs claude): [docs/reference.md](docs/reference.md) and `npx firegoat@latest eval --help`
47
+ - The skill your agent uses to write evals: [firegoat-ai/skills](https://github.com/firegoat-ai/skills)
48
+ - Tell us what got in your way: `npx firegoat@latest feedback "<what happened>"`
49
+ - [firegoat.ai](https://firegoat.ai)
82
50
 
83
- ```yaml
84
- runs: 3
85
- build: uv build --wheel --out-dir "$FIREGOAT_OUT"
86
- install: uv venv && uv pip install "$FIREGOAT_OUT"/*.whl
87
- tests:
88
- - name: sandbox-hello
89
- prompt: >-
90
- Using the acme Python SDK (already installed), create a sandbox on commit
91
- {{vars.SHA}}, run `echo hello-from-acme` in it, print the output, then delete it.
92
- end_goal: The script prints hello-from-acme from a sandbox and deletes the sandbox.
93
- checks:
94
- - run: grep -rq hello-from-acme "$FIREGOAT_WORKDIR"
95
- ```
96
-
97
- ```bash
98
- npx firegoat eval --var SHA=$(git rev-parse HEAD)
99
- ```
100
-
101
- ### Start from a template project
102
-
103
- ```yaml
104
- build: cp -R fixtures/starter-app "$FIREGOAT_OUT/starter-app"
105
- install: cp -R "$FIREGOAT_OUT/starter-app/." .
106
- tests:
107
- - name: add-auth
108
- prompt: Add acme login to this app. Keep the existing pages working.
109
- end_goal: The app signs users in with acme, and the existing pages still render.
110
- checks:
111
- - run: npm test
112
- ```
113
-
114
- No template feature needed: keep the project committed in `fixtures/starter-app/`, `build` copies it once per eval, and `install` copies it into each fresh folder, so every run begins in its own copy of that project.
115
-
116
- ### Same tests on several models
117
-
118
- ```yaml
119
- model: [claude-haiku-5-5, claude-sonnet-5-5]
120
- runs: 2
121
- tests:
122
- - name: quickstart
123
- prompt: Get the acme SDK's hello-world example running and save its output to output.txt.
124
- end_goal: output.txt holds the example's output.
125
- checks:
126
- - run: test -s output.txt
127
- ```
128
-
129
- `model:` is passed to claude as `--model`. A list is a matrix: each test runs `runs` times per model, numbered `1..runs×models` (here `quickstart/1`, `2` on haiku, `3`, `4` on sonnet). `npx firegoat eval --model claude-opus-5-5` replaces it for one eval; repeat the flag for a matrix. Unset, firegoat passes no `--model` and claude uses its default. Each run's `run.json` records `model` (what firegoat asked for, or null) and `claudeModel` (what claude reported starting with), and the output labels each run with its model.
130
-
131
- ### Compare two setups
132
-
133
- ```yaml
134
- build: npm pack --pack-destination "$FIREGOAT_OUT"
135
- tests:
136
- - name: deploy-local-build
137
- prompt: &deploy Deploy this app to an acme preview environment and print the URL.
138
- end_goal: &deployed The app runs on an acme preview URL, and the URL is printed.
139
- install: npm install "$FIREGOAT_OUT"/acme-*.tgz
140
- - name: deploy-npm-release
141
- prompt: *deploy
142
- end_goal: *deployed
143
- install: npm install acme@latest
144
- ```
145
-
146
- There is no variants feature: to compare setups (your local build vs the npm release, with vs without `--chrome`, docs A vs docs B), write one test per setup with the same prompt and a different per-test `install:`, `env:` or `claude_args:`. A YAML anchor (`&deploy`) and alias (`*deploy`) keep the prompt in one place. Skills installed into the run's folder are not loaded as skills: eval runs claude with `--setting-sources ""` and `--disable-slash-commands`, so a with/without-skill comparison isn't possible yet.
147
-
148
- ### Extra claude flags
149
-
150
- ```yaml
151
- claude_args: [--plugin-dir, ./plugins/acme] # Every test
152
- tests:
153
- - name: dashboard-key
154
- prompt: Get an acme API key from the acme dashboard (I'm logged in in Chrome) and put it in .env as ACME_API_KEY.
155
- end_goal: .env holds a key copied from the dashboard, not a made-up one.
156
- claude_args: [--chrome] # Claude in Chrome, this test only
157
- ```
158
-
159
- `claude_args:` (top level, and per test after the top-level ones) is passed to claude after firegoat's own flags and `--model`. Each entry is one argument, as-is, with no shell splitting: `[--plugin-dir, ./plugins/acme]`, not `["--plugin-dir ./plugins/acme"]`. Use it for `--chrome`, `--plugin-dir`, `--mcp-config`, `--agents`, `--append-system-prompt`. The flags firegoat sets itself are config errors, also as `--flag=value`: `-p`, `--print`, `--output-format`, `--verbose`, `--setting-sources`, `--strict-mcp-config`, `--disable-slash-commands`, `--dangerously-skip-permissions`, `--model` (use `model:`), and `--session-id`, `--resume`, `-r`, `--continue`, `-c` (every run is a fresh session). Entries expand like `env:` values: `$FIREGOAT_WORKDIR`, `$FIREGOAT_HOME`, `$FIREGOAT_OUT` (also `${...}`), `{{vars.KEY}}`, `{{run.id}}`, `{{run.n}}`, `{{test.name}}`; no other `$` expansion. Each run's `run.json` records the expanded `claudeArgs`.
160
-
161
- ### Testing an MCP server
162
-
163
- Commit the server's config as `fixtures/mcp.json`:
164
-
165
- ```json
166
- {"mcpServers": {"acme": {"command": "./node_modules/.bin/acme-mcp",
167
- "env": {"HOME": "${FIREGOAT_HOME}"}}}}
168
- ```
169
-
170
- Copy it in `build:` and pass it with `claude_args:`:
171
-
172
- ```yaml
173
- build:
174
- - npm pack --pack-destination "$FIREGOAT_OUT"
175
- - cp -R fixtures/. "$FIREGOAT_OUT"
176
- install: npm init -y >/dev/null && npm install "$FIREGOAT_OUT"/*.tgz
177
- claude_args: [--mcp-config, $FIREGOAT_OUT/mcp.json]
178
- ```
179
-
180
- claude gets `FIREGOAT_HOME`, `FIREGOAT_WORKDIR` and `FIREGOAT_OUT` in its own env, set per run, and expands `${VAR}` in an MCP config's `command`, `args`, `env`, `url` and `headers` from its env (an unset var stays the literal text). Relative paths in the config are from the run's folder, claude's cwd: `./node_modules/.bin/acme-mcp` is the build under test, installed by `install:`. MCP servers that claude starts inherit claude's own env, which keeps your real `HOME` (claude needs it for its login): set `"env": {"HOME": "${FIREGOAT_HOME}"}` on the server, as above, or it writes to your real home. Per-run state the server keeps elsewhere goes the same way, e.g. `"ACME_DB": "${FIREGOAT_WORKDIR}/acme.db"`. `--strict-mcp-config` is always on, so only the configs you pass load. To check that the agent used a tool, grep the transcript: `grep -Eq '^\[[0-9]+\] mcp__acme__search' "$FIREGOAT_TRANSCRIPT"`.
181
-
182
- With `--chrome`, claude points the Claude in Chrome extension's native host (`~/Library/Application Support/<browser>/NativeMessagingHosts/com.anthropic.claude_code_browser_extension.json`) at `.firegoat/.claude`. If you delete `.firegoat/`, run a normal `claude --chrome` once to point it back.
183
-
184
- ### Env vars for the agent: a feature flag, a tool that ignores HOME
185
-
186
- ```yaml
187
- env:
188
- ACME_HOME: "$FIREGOAT_HOME/.acme" # Only for a tool that ignores HOME
189
- ACME_TELEMETRY: "off"
190
- UV_FIND_LINKS: "$FIREGOAT_OUT" # Resolve the wheel build: made
191
- tests:
192
- - name: login
193
- prompt: Log in to acme with the CLI and print the current user.
194
- end_goal: The agent completes acme's login flow and prints the user.
195
- env:
196
- ACME_BETA_LOGIN: "1" # This test only, on top of env:
197
- checks:
198
- - run: ls "$ACME_HOME"/sessions | grep -q .
199
- ```
200
-
201
- `env:` goes to the agent, and to `install`, checks and `cleanup` of the same run, so checks see the same values. Placeholders: `{{vars.KEY}}`, `{{run.id}}` (the eval's run id), `{{run.n}}` (1..runs, or 1..runs×models), `{{test.name}}`, and `$FIREGOAT_OUT` / `$FIREGOAT_WORKDIR` / `$FIREGOAT_HOME` (also `${...}`), the same dirs `build`, `install` and checks see. There is no other `$` expansion: `$HOME` stays the literal text `$HOME`. `build` runs once, before any run: it gets only the top-level entries that use nothing but `{{vars.*}}`, `{{run.id}}` and `$FIREGOAT_OUT`. Values are strings (quote numbers). `PATH`, `CLAUDE_CONFIG_DIR` and `FIREGOAT_*` are firegoat's and can't be set; `ANTHROPIC_MODEL` neither: choose the model with `model:`. `HOME`, `XDG_CONFIG_HOME`, `XDG_CACHE_HOME`, `XDG_DATA_HOME`, `XDG_STATE_HOME` and `CLAUDE_ENV_FILE` are reserved too: every run gets its own home.
202
-
203
- ### Each run gets its own HOME
204
-
205
- Every run has its own home, `.firegoat/runs/<runId>/<test>/<n>/home/`, with `.config`, `.cache`, `.local/share` and `.local/state` for the XDG dirs. A tool that keeps logins, config or caches in `~` starts fresh in every run, and parallel runs don't collide. Point a tool's own `*_HOME` var at `$FIREGOAT_HOME` only if the tool ignores `HOME`.
206
-
207
- - The agent's shell commands see it: firegoat writes `<n>/home.env` (exporting `HOME` and `XDG_CONFIG_HOME`, `XDG_CACHE_HOME`, `XDG_DATA_HOME`, `XDG_STATE_HOME`) and passes it to claude as `CLAUDE_ENV_FILE`, which claude sources before every Bash command. claude itself keeps your real `HOME`, where its login is (the macOS keychain lookup, `~/.claude/.credentials.json` on Linux).
208
- - `install`, checks and `cleanup` get `HOME` and the XDG vars set to the run's home, plus `$FIREGOAT_HOME`. `build` keeps your real home.
209
- - `run.json` records it as `home`.
210
-
211
- Limits:
212
-
213
- - claude's own Read/Write/Edit tools expand `~` to your real home; only Bash commands get the run's home.
214
- - Lookups by user id (`os.userInfo().homedir`, python's `pwd`, `getpwuid`) still return your real home.
215
- - Keychain-backed logins (`gh`, `docker`, git credential helpers) look logged out inside a run. Log tools in from `install:`, or pass tokens with `env:`.
216
- - The fresh home has no `.gitconfig`: if the agent commits, set `git config --global user.name ...` / `user.email` in `install:`.
217
- - claude's shell snapshot still sources your real `~/.zshrc` (or `~/.bashrc`).
218
-
219
- ## What eval does
220
-
221
- - claude runs on its own login (or `ANTHROPIC_API_KEY`, if set) with `CLAUDE_CONFIG_DIR=.firegoat/.claude` and `--setting-sources "" --strict-mcp-config --disable-slash-commands`: none of your settings, CLAUDE.md, plugins, skills or MCP servers (add flags with `claude_args:`, see above). claude saves its session there, in its own format.
222
- - No sandbox: the agent's folder is inside `.firegoat/runs/`, and it can reach the rest of your machine. `.firegoat/` holds local run output and ignores itself in git. Keep test fixtures (scripts, sample data) in `fixtures/` next to `firegoat.yaml`, committed.
223
- - In a linked git worktree (`git worktree add`), `.firegoat/runs/` and `.firegoat/.claude/` are in the main checkout, so runs (and their session files) survive `git worktree remove`; `firegoat.yaml`, fixtures and `build:` stay in the worktree. Each `summary.json` records `dir` (where eval ran) and `git: {branch, commit, dirty}`, and `.firegoat/runs/index.jsonl` gets one line per eval (`runId, startedAt, dir, branch, commit, passed, failed, notGraded, cost, uploaded, uploadFailed`) to tell runs from different branches apart.
224
- - The agent gets your environment plus `env:`, never `vars:`. Vars that npm/npx set for firegoat itself (`npm_config_*`, `npm_lifecycle_*`, `npm_package_*`, `npm_execpath`, `npm_node_execpath`, `npm_command`, `INIT_CWD`) are removed from every env firegoat starts (agent, build, install, checks): `npx -y firegoat` sets `npm_config_yes=true`, which would make the agent's `npm init` / `npx create-*` skip their prompts. Set one under `env:` to keep it. Every `FIREGOAT_*` var firegoat was started with (`FIREGOAT_API_KEY` above all) is removed too (claude then gets the run's own `FIREGOAT_OUT`, `FIREGOAT_WORKDIR` and `FIREGOAT_HOME`, for MCP configs; the agent's commands see them too), and so are the vars a parent Claude Code session sets (when firegoat runs from claude, or its agent runs firegoat), so the claude under test doesn't act as a nested session: `CLAUDECODE`, `CLAUDE_CODE_*`, `CLAUDE_PID`, `CLAUDE_ENV_FILE`, `CLAUDE_SESSION_ID`, `CLAUDE_PROJECT_DIR`, `CLAUDE_EFFORT`, `CLAUDE_AGENT_SDK_*`, `CLAUDE_PLUGIN_ROOT`, `CLAUDE_PLUGIN_DATA`, `CLAUDE_SKILL_DIR`. Login and provider vars stay: `ANTHROPIC_*`, `CLAUDE_CODE_OAUTH_TOKEN`, `CLAUDE_CODE_OAUTH_REFRESH_TOKEN`, `CLAUDE_CODE_USE_*` (Bedrock, Vertex, ...), `CLAUDE_CODE_SKIP_*_AUTH`, `CLAUDE_CODE_CLIENT_CERT`, `CLAUDE_CODE_CLIENT_KEY`, `CLAUDE_CODE_CLIENT_KEY_PASSPHRASE`.
225
- - Runs (tests × models × `runs`) go 4 at a time, each in its own folder with its own session and HOME; `--parallel N` changes that, `--parallel 1` runs them one after another. `summary.json` lists them in `firegoat.yaml` order.
226
- - Script checks run in the agent's folder afterwards and set the exit code. `end_goal` is not graded by firegoat: it's for you, or your coding agent, to judge from `run.json`, the session and the folder.
227
- - Output per run, in `.firegoat/runs/<runId>/<test>/<n>/`: `transcript.txt` (each tool call with its command and short output, the final answer, files made; checks get it as `$FIREGOAT_TRANSCRIPT`; format below), `run.json` (status, prompt, end_goal, check results, paths), `work/`, `home/` and `home.env` (the run's HOME), logs. Read the transcript to judge `end_goal`; claude's full session file is ~50x larger. `summary.json` covers the whole eval; `--json` prints it.
228
-
229
- ### The transcript
230
-
231
- `transcript.txt` is greppable line by line; `n` counts the agent's tool calls:
232
-
233
- - `[n] $ <command>`: a shell call, on every line of the command (heredocs and multi-line scripts too), never cut. So `grep -E '^\[[0-9]+\] \$ .*npm install'` matches only commands.
234
- - `[n] <Tool> <target>`: a tool (MCP ones too) whose input has a `file_path`, `path`, `url`, `pattern` or `query`: just that value, e.g. `[2] Write src/app.ts`. The written body is not in the transcript: check the file in the workdir.
235
- - `[n] <Tool> <json>`: any other tool, MCP tools included, with its whole input as one line of JSON, cut at 4000 chars: `[3] mcp__acme__push_html {"canvas":"demo","html":"<p>Hi</p>"}`. So `grep -Eq '^\[[0-9]+\] mcp__acme__push_html .*<h1>' "$FIREGOAT_TRANSCRIPT"` checks what the agent pushed.
236
- - ` → ok` or ` → error`, then the tool's output indented 4 spaces, cut at 15 lines or 1500 chars (`… (N more chars)`).
237
- - `agent: ` on every line of the agent's text (cut at 30 lines or 1500 chars) and of its final answer (uncut), then the files in the workdir (without `node_modules`, `.git`, `.venv`, `__pycache__`).
238
-
239
- `firegoat.yaml` keys: `runs`, `model`, `build`, `install`, `cleanup`, `vars`, `env`, `claude_args`, and `tests` (`name`, `prompt`, `end_goal`, `checks`, `timeout`, `env`, `install`, `cleanup`, `claude_args`). Unknown keys are errors. Full reference: `npx firegoat init --help`.
240
-
241
- ### Stop what the agent left running, seed one test
242
-
243
- ```yaml
244
- install: npm init -y >/dev/null
245
- cleanup: pkill -f "$FIREGOAT_WORKDIR" || true # A dev server the agent started
246
- tests:
247
- - name: migrate
248
- prompt: Migrate the app in this folder to acme v2 and start it.
249
- end_goal: The app runs on acme v2.
250
- install: cp -R "$FIREGOAT_OUT"/v1-app/. . # This test only, after the top-level install
251
- cleanup: acme down || true # This test only, before the top-level cleanup
252
- ```
253
-
254
- Step order: `build` once per eval, in your folder; then per run, in its folder: `install` (top-level, then the test's), the agent, checks, `cleanup` (the test's, then top-level). `cleanup` runs whenever the run's folder exists: after the checks, and also after a failed install, a claude error or a timeout. Every cleanup step runs; a failing one prints a warning and is recorded in `run.json` (`cleanup: [{run, code}]`, output in `cleanup.txt`), but never changes the run's status or eval's exit code.
255
-
256
- Want a plain `npm i acme-sdk` to get your build, with no hint in the prompt? See [`examples/local-npm-registry`](examples/local-npm-registry/README.md).
257
-
258
- | exit code | meaning |
259
- |---|---|
260
- | 0 | Every run's checks passed |
261
- | 1 | A check failed |
262
- | 2 | At least one run was not graded (claude missing or logged out, crash, timeout, build failed); wins over 1 |
263
- | 3 | Usage or config error (including firegoat.ai rejecting the upload key) |
264
-
265
- Upload failures don't change the exit code.
266
-
267
- ## Feedback
268
-
269
- Something in firegoat got in your way (or your agent's)? Tell us from the terminal:
270
-
271
- ```sh
272
- npx firegoat feedback "eval hid claude's spend-limit error" --type bug --body "ran npx firegoat eval, got 'not graded' x3"
273
- ```
274
-
275
- It goes to the firegoat team with the firegoat version, your OS and node version, and which coding agent sent it. Don't include secrets: a report that looks like it has one is refused. The first send registers your device and saves its token to `~/.config/firegoat/feedback-device.json` (`$XDG_CONFIG_HOME` respected), which raises the daily report limit.
276
-
277
- ## Develop
278
-
279
- ```bash
280
- bun install
281
- bun run format
282
- bun test # End-to-end: packs the package and drives the installed bin
283
- FIREGOAT_E2E_REAL=1 bun test # Also runs one eval against your real claude
284
- ```
51
+ If every run passes, firegoat calls you GOATED. Who's the GOAT? You are.
package/dist/cli.js CHANGED
@@ -6947,7 +6947,7 @@ var require_public_api = __commonJS(function(exports) {
6947
6947
  // package.json
6948
6948
  var package_default = {
6949
6949
  name: "firegoat",
6950
- version: "0.22.0",
6950
+ version: "0.22.1",
6951
6951
  repository: {
6952
6952
  type: "git",
6953
6953
  url: "git+https://github.com/firegoat-ai/firegoat-cli.git"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "firegoat",
3
- "version": "0.22.0",
3
+ "version": "0.22.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/firegoat-ai/firegoat-cli.git"