firegoat 0.4.0 → 0.5.0
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.
- package/README.md +1 -1
- package/dist/cli.js +38 -18
- package/package.json +2 -3
- package/skills/firegoat-onboarding/SKILL.md +0 -115
- package/skills/firegoat-onboarding/references/examples.md +0 -114
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ Status: early (v0.4). See `plan.md`.
|
|
|
7
7
|
## Use
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npx firegoat init # writes firegoat.yaml with starter evals; --skills adds the onboarding skill
|
|
10
|
+
npx firegoat init # writes firegoat.yaml with starter evals; --skills adds the firegoat-onboarding skill (from team2027/skills)
|
|
11
11
|
npx firegoat login # once per machine: signs claude in to ~/.firegoat/claude
|
|
12
12
|
npx firegoat eval # runs each test with your local `claude`, sandboxed, in a fresh folder
|
|
13
13
|
npx firegoat open # opens the latest run's folder (--print to only print it)
|
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.
|
|
6950
|
+
version: "0.5.0",
|
|
6951
6951
|
repository: {
|
|
6952
6952
|
type: "git",
|
|
6953
6953
|
url: "git+https://github.com/team2027/firegoat-cli.git"
|
|
@@ -6968,8 +6968,7 @@ var package_default = {
|
|
|
6968
6968
|
node: ">=18"
|
|
6969
6969
|
},
|
|
6970
6970
|
files: [
|
|
6971
|
-
"dist"
|
|
6972
|
-
"skills"
|
|
6971
|
+
"dist"
|
|
6973
6972
|
],
|
|
6974
6973
|
homepage: "https://github.com/team2027/firegoat-cli#readme",
|
|
6975
6974
|
license: "MIT",
|
|
@@ -8004,17 +8003,27 @@ ${pass} passed, ${fail} failed, ${error} not graded (${results.length} runs)`);
|
|
|
8004
8003
|
}
|
|
8005
8004
|
|
|
8006
8005
|
// src/init.ts
|
|
8007
|
-
import {
|
|
8006
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
8007
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
8008
8008
|
import { basename as basename2, join as join6 } from "node:path";
|
|
8009
8009
|
import { createInterface as createInterface2 } from "node:readline/promises";
|
|
8010
|
-
import { fileURLToPath } from "node:url";
|
|
8011
8010
|
var INIT_USAGE = `Usage: firegoat init [--yes] [--force] [--skills]
|
|
8012
8011
|
|
|
8013
8012
|
--yes, -y don't ask questions (default when stdin is not a terminal)
|
|
8014
8013
|
--force, -f overwrite an existing ${CONFIG_FILE}
|
|
8015
|
-
--skills
|
|
8014
|
+
--skills install the firegoat-onboarding and suggesting-eval-prompts skills
|
|
8015
|
+
into .claude/skills/ (npx skills add team2027/skills)
|
|
8016
8016
|
`;
|
|
8017
|
-
var
|
|
8017
|
+
var SKILLS = ["firegoat-onboarding", "suggesting-eval-prompts"];
|
|
8018
|
+
var SKILLS_ARGV = [
|
|
8019
|
+
"skills",
|
|
8020
|
+
"add",
|
|
8021
|
+
"team2027/skills",
|
|
8022
|
+
...SKILLS.flatMap((s) => ["--skill", s]),
|
|
8023
|
+
"--agent",
|
|
8024
|
+
"claude-code",
|
|
8025
|
+
"-y"
|
|
8026
|
+
];
|
|
8018
8027
|
var GITIGNORE_LINE = ".firegoat/";
|
|
8019
8028
|
function readText(file) {
|
|
8020
8029
|
try {
|
|
@@ -8218,8 +8227,16 @@ function ensureGitignore(cwd) {
|
|
|
8218
8227
|
`);
|
|
8219
8228
|
return `added ${GITIGNORE_LINE} to .gitignore`;
|
|
8220
8229
|
}
|
|
8221
|
-
function
|
|
8222
|
-
|
|
8230
|
+
function installSkills(cwd) {
|
|
8231
|
+
const r = spawnSync3("npx", ["-y", ...SKILLS_ARGV], {
|
|
8232
|
+
cwd,
|
|
8233
|
+
stdio: "inherit"
|
|
8234
|
+
});
|
|
8235
|
+
if (r.error)
|
|
8236
|
+
return r.error.message;
|
|
8237
|
+
if (r.status !== 0)
|
|
8238
|
+
return `exit ${r.status ?? "by signal"}`;
|
|
8239
|
+
return;
|
|
8223
8240
|
}
|
|
8224
8241
|
async function askYesNo(question) {
|
|
8225
8242
|
const rl = createInterface2({ input: process.stdin, output: process.stdout });
|
|
@@ -8286,20 +8303,23 @@ ${INIT_USAGE}`);
|
|
|
8286
8303
|
console.log(ensureGitignore(cwd));
|
|
8287
8304
|
const interactive = !yes && process.stdin.isTTY && process.stdout.isTTY;
|
|
8288
8305
|
if (!skills && interactive)
|
|
8289
|
-
skills = await askYesNo(`Add the ${
|
|
8306
|
+
skills = await askYesNo(`Add the ${SKILLS.join(" and ")} skills to .claude/skills/ (help your agent draft better evals)?`);
|
|
8290
8307
|
if (skills) {
|
|
8291
|
-
|
|
8292
|
-
|
|
8293
|
-
|
|
8308
|
+
console.log(`installing skills: npx ${SKILLS_ARGV.join(" ")}`);
|
|
8309
|
+
const failed = installSkills(cwd);
|
|
8310
|
+
if (failed) {
|
|
8311
|
+
console.error(`firegoat init: installing the skills failed (${failed}). Run it yourself: npx ${SKILLS_ARGV.join(" ")}`);
|
|
8312
|
+
return EXIT.notGraded;
|
|
8313
|
+
}
|
|
8294
8314
|
} else {
|
|
8295
|
-
console.log(`hint: \`npx firegoat init --skills\` adds the ${
|
|
8315
|
+
console.log(`hint: \`npx firegoat init --skills\` adds the ${SKILLS.join(" and ")} skills to .claude/skills/ (keeps your ${CONFIG_FILE})`);
|
|
8296
8316
|
}
|
|
8297
8317
|
console.log("next: npx firegoat eval");
|
|
8298
8318
|
return 0;
|
|
8299
8319
|
}
|
|
8300
8320
|
|
|
8301
8321
|
// src/login.ts
|
|
8302
|
-
import { spawnSync as
|
|
8322
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
8303
8323
|
import { mkdirSync as mkdirSync4 } from "node:fs";
|
|
8304
8324
|
function loginCommand(args) {
|
|
8305
8325
|
let claudeBin = process.env.FIREGOAT_CLAUDE_BIN || "claude";
|
|
@@ -8322,7 +8342,7 @@ function loginCommand(args) {
|
|
|
8322
8342
|
};
|
|
8323
8343
|
delete env.ANTHROPIC_API_KEY;
|
|
8324
8344
|
console.error(`Signing claude in to ${configDir}. Evals run with this login, never your own ~/.claude.`);
|
|
8325
|
-
const r =
|
|
8345
|
+
const r = spawnSync4(claudeBin, ["auth", "login"], { env, stdio: "inherit" });
|
|
8326
8346
|
if (r.error) {
|
|
8327
8347
|
console.error(`firegoat login: could not run ${claudeBin}: ${r.error.message}`);
|
|
8328
8348
|
return EXIT.notGraded;
|
|
@@ -8336,7 +8356,7 @@ function loginCommand(args) {
|
|
|
8336
8356
|
}
|
|
8337
8357
|
|
|
8338
8358
|
// src/open.ts
|
|
8339
|
-
import { spawnSync as
|
|
8359
|
+
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
8340
8360
|
import { existsSync as existsSync5, readdirSync } from "node:fs";
|
|
8341
8361
|
import { join as join7 } from "node:path";
|
|
8342
8362
|
var OPENER = {
|
|
@@ -8375,7 +8395,7 @@ function openCommand(args, cwd) {
|
|
|
8375
8395
|
const opener = OPENER[process.platform];
|
|
8376
8396
|
if (print || !opener)
|
|
8377
8397
|
return EXIT.pass;
|
|
8378
|
-
const r =
|
|
8398
|
+
const r = spawnSync5(opener, [dir], { stdio: "inherit" });
|
|
8379
8399
|
if (r.error || r.status !== 0) {
|
|
8380
8400
|
console.error(`firegoat open: ${opener} failed (${r.error?.message ?? `exit ${r.status}`}); the run is at the path above`);
|
|
8381
8401
|
return EXIT.notGraded;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "firegoat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/team2027/firegoat-cli.git"
|
|
@@ -21,8 +21,7 @@
|
|
|
21
21
|
"node": ">=18"
|
|
22
22
|
},
|
|
23
23
|
"files": [
|
|
24
|
-
"dist"
|
|
25
|
-
"skills"
|
|
24
|
+
"dist"
|
|
26
25
|
],
|
|
27
26
|
"homepage": "https://github.com/team2027/firegoat-cli#readme",
|
|
28
27
|
"license": "MIT",
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: firegoat-onboarding
|
|
3
|
-
description: Draft 2-3 firegoat evals (firegoat.yaml) for the current repo by reading its README, docs and package manifests, phrasing each test as a real user's problem with a gradeable goal and script checks. Use when the user says "write firegoat evals", "draft evals", "set up firegoat", "firegoat init", or asks for agent-experience (AX) evals of their SDK, CLI, API or docs.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
# Draft firegoat evals
|
|
7
|
-
|
|
8
|
-
firegoat runs a coding agent (Claude Code) in an empty, isolated workdir with one prompt,
|
|
9
|
-
then grades the run. Your job: write 2-3 tests into `firegoat.yaml` that tell the
|
|
10
|
-
maintainer whether an agent can succeed with their product the way a new user would.
|
|
11
|
-
|
|
12
|
-
## 1. Learn the product (read, don't run)
|
|
13
|
-
|
|
14
|
-
Read, in this order, and stop when you can answer the questions below:
|
|
15
|
-
- `README*`, then the docs dir (`docs/`, `website/`, `*.mdx`), quickstart/getting-started pages first.
|
|
16
|
-
- Package manifests: `package.json` (`name`, `bin`, `exports`), `pyproject.toml`, `go.mod`,
|
|
17
|
-
`Cargo.toml`, `*.gemspec`, `Package.swift`. They tell you the install name and entry points.
|
|
18
|
-
- `examples/`, `templates/`, `samples/`: the shapes users actually build.
|
|
19
|
-
- An existing `firegoat.yaml`, if any: keep its tests (see step 5).
|
|
20
|
-
|
|
21
|
-
Answer: What is it (SDK, CLI, API, framework)? Who uses it? What is the first thing a new
|
|
22
|
-
user wants to have working? What is the public docs URL? Which credentials does it need
|
|
23
|
-
(env var names only)?
|
|
24
|
-
|
|
25
|
-
## 2. Pick 2-3 outcomes
|
|
26
|
-
|
|
27
|
-
Choose the most important first-time user outcomes, e.g. "first successful API call",
|
|
28
|
-
"the core feature end to end", "the thing users most often ask about". Prefer outcomes
|
|
29
|
-
that leave observable proof. Skip admin, billing and anything needing a browser login
|
|
30
|
-
unless credentials can come from env vars.
|
|
31
|
-
|
|
32
|
-
## 3. Write each prompt as a user's problem
|
|
33
|
-
|
|
34
|
-
A prompt is what a user would type to their agent, not a recipe. Each prompt has:
|
|
35
|
-
- **What they want, not how.** No numbered implementation steps, no "use template X, add package Y".
|
|
36
|
-
The eval measures whether the agent finds its way with your docs.
|
|
37
|
-
- **One concrete outcome**, small enough to finish in a few minutes.
|
|
38
|
-
- **Observable proof**: print a marker to stdout, write a file, or take a screenshot.
|
|
39
|
-
- **"Keep it minimal."** so the agent doesn't build a demo app.
|
|
40
|
-
- **The docs URL as the only hint** (omit it if the test is about discovery).
|
|
41
|
-
- **Credentials as env var names**, never values: "Credentials are in env vars ACME_API_KEY and ACME_API_URL."
|
|
42
|
-
- If the test should use the build under test, say so: "The package is already installed
|
|
43
|
-
from the build under test. Do not install or upgrade it from the registry."
|
|
44
|
-
|
|
45
|
-
## 4. Write the goal and checks
|
|
46
|
-
|
|
47
|
-
`goal`: one sentence a judge could check from the transcript and workdir, e.g. "A minimal
|
|
48
|
-
project runs and prints the answer, a numeric score and a reason."
|
|
49
|
-
|
|
50
|
-
`checks`: shell scripts, run in the agent's workdir after the run. Exit code 0 = pass.
|
|
51
|
-
Env: `FIREGOAT_WORKDIR` (the workdir) and `FIREGOAT_SESSION` (the agent's stream-json
|
|
52
|
-
transcript, one JSON event per line). Checks should prove the outcome and catch cheating:
|
|
53
|
-
- Output exists with the expected marker: `grep -rq hello-from-acme "$FIREGOAT_WORKDIR"`
|
|
54
|
-
or `test -s "$FIREGOAT_WORKDIR/screenshot.png"`.
|
|
55
|
-
- The real thing was used, not faked: the transcript shows the SDK/CLI being called,
|
|
56
|
-
e.g. `grep -q '"command":"[^"]*acme ' "$FIREGOAT_SESSION"`.
|
|
57
|
-
- Cleanup happened: the transcript shows the delete call after the create call.
|
|
58
|
-
- The local build was used, not a registry install:
|
|
59
|
-
`! grep -Eq '"command":"[^"]*(pip install|npm (install|i) [^"]*acme@)' "$FIREGOAT_SESSION"`.
|
|
60
|
-
- Only local docs were used (when that's the point): `! grep -q '"name":"WebFetch"' "$FIREGOAT_SESSION"`.
|
|
61
|
-
|
|
62
|
-
Match tool-call JSON (`"command":"...`) rather than plain words, so the prompt text or the
|
|
63
|
-
agent saying "I won't pip install" doesn't trip the check. Checks run in the agent's
|
|
64
|
-
workdir, which does not contain the repo, so keep them self-contained: one-liners, or a
|
|
65
|
-
`run: |` block for longer scripts. Never reference repo files by relative path.
|
|
66
|
-
|
|
67
|
-
Do not emit `judge:` checks: v1 skips them with a warning.
|
|
68
|
-
|
|
69
|
-
## 5. Write firegoat.yaml
|
|
70
|
-
|
|
71
|
-
Schema (v1):
|
|
72
|
-
|
|
73
|
-
```yaml
|
|
74
|
-
agents: [claude-code] # only claude-code in v1
|
|
75
|
-
runs: 1 # runs per test, positive integer
|
|
76
|
-
setup: npm pack --pack-destination "$FIREGOAT_OUT" # optional: once per eval, in the repo;
|
|
77
|
-
# $FIREGOAT_OUT is copied into each run and put on PATH
|
|
78
|
-
workdir_setup: npm init -y >/dev/null && npm install "$FIREGOAT_OUT"/*.tgz
|
|
79
|
-
# optional: in each fresh workdir before the agent starts
|
|
80
|
-
vars: # optional: defaults for {{vars.KEY}}; `--var KEY=value` overrides
|
|
81
|
-
SHA: main
|
|
82
|
-
tests:
|
|
83
|
-
- name: quickstart # letters, digits, '.', '_', '-'; unique
|
|
84
|
-
prompt: >-
|
|
85
|
-
...
|
|
86
|
-
goal: ...
|
|
87
|
-
checks: # optional
|
|
88
|
-
- run: grep -rq some-marker "$FIREGOAT_WORKDIR"
|
|
89
|
-
timeout: 600 # optional, seconds
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
Add `setup:` / `workdir_setup:` when the tests should use the repo's local build (python:
|
|
93
|
-
`uv build --wheel --out-dir "$FIREGOAT_OUT"` and `uv venv && uv pip install "$FIREGOAT_OUT"/*.whl`);
|
|
94
|
-
then the prompt can say "The package is already installed from the build under test."
|
|
95
|
-
|
|
96
|
-
If `firegoat.yaml` exists, merge: keep every existing test and top-level key untouched, append
|
|
97
|
-
yours, and rename yours (`-2` suffix) if a name collides. Never delete or rewrite user tests.
|
|
98
|
-
Quote prompts with `>-` folded blocks so colons and quotes stay valid YAML.
|
|
99
|
-
|
|
100
|
-
Before finishing, re-read the file and check: it parses as YAML, every test has a non-empty
|
|
101
|
-
`name`, `prompt` and `goal`, names are unique and match `^[A-Za-z0-9._-]+$`, and no credential
|
|
102
|
-
values appear anywhere.
|
|
103
|
-
|
|
104
|
-
## 6. Hand off
|
|
105
|
-
|
|
106
|
-
Tell the user, in a few lines: which tests you added and why, which env vars they must export,
|
|
107
|
-
and to run `npx firegoat eval`.
|
|
108
|
-
|
|
109
|
-
## Examples
|
|
110
|
-
|
|
111
|
-
See `references/examples.md` for good and bad tests with their YAML. Short version:
|
|
112
|
-
|
|
113
|
-
Good: "Create a new app - a coffee timer. Run it on the simulator and get the screenshot."
|
|
114
|
-
Bad: "Create a sandbox. https://docs.acme.dev" with no goal and no checks: every run passes,
|
|
115
|
-
so it measures nothing.
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
# Example firegoat tests
|
|
2
|
-
|
|
3
|
-
Product names are fictional. Copy the shape, not the words.
|
|
4
|
-
|
|
5
|
-
## Good
|
|
6
|
-
|
|
7
|
-
### 1. Core feature end to end, printed proof
|
|
8
|
-
|
|
9
|
-
An agent framework with scorers. The prompt names the outcome and what to print; the agent
|
|
10
|
-
has to find how in the docs.
|
|
11
|
-
|
|
12
|
-
```yaml
|
|
13
|
-
tests:
|
|
14
|
-
- name: agent-with-scorer
|
|
15
|
-
prompt: >-
|
|
16
|
-
Build an agent with a scorer using https://docs.acme.dev/agents. Ask the agent
|
|
17
|
-
to explain recursion in one sentence, then run a scorer that grades whether the
|
|
18
|
-
answer is concise and accurate. Print the agent answer, the numeric score and
|
|
19
|
-
the scorer reason to stdout. Keep it minimal.
|
|
20
|
-
goal: >-
|
|
21
|
-
A minimal project runs and prints the agent's answer, a numeric score and the
|
|
22
|
-
scorer's reason.
|
|
23
|
-
checks:
|
|
24
|
-
- run: grep -Eq '"command":"[^"]*(node|npx|tsx|bun) ' "$FIREGOAT_SESSION"
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
### 2. Multi-step workflow, one realistic input
|
|
28
|
-
|
|
29
|
-
```yaml
|
|
30
|
-
tests:
|
|
31
|
-
- name: support-workflow
|
|
32
|
-
prompt: >-
|
|
33
|
-
Using https://docs.acme.dev/workflows, build a three-step workflow: extract the
|
|
34
|
-
issue type from a support message, have an agent draft a response, then add a
|
|
35
|
-
priority label. Run it once on a customer asking for a refund and print the
|
|
36
|
-
final result. Keep it minimal.
|
|
37
|
-
goal: >-
|
|
38
|
-
The workflow runs once on a refund request and prints a result containing the
|
|
39
|
-
issue type, a drafted response and a priority label.
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
### 3. Local build under test, cleanup, no registry install
|
|
43
|
-
|
|
44
|
-
```yaml
|
|
45
|
-
tests:
|
|
46
|
-
- name: sandbox-hello
|
|
47
|
-
prompt: >-
|
|
48
|
-
Using the Acme Python SDK, create a sandbox, run `echo hello-from-acme` inside
|
|
49
|
-
it, print the output, then delete the sandbox. The package is already installed
|
|
50
|
-
from the build under test. Do not install or upgrade it from PyPI.
|
|
51
|
-
Docs: https://docs.acme.dev/python. Credentials are in env vars ACME_API_KEY
|
|
52
|
-
and ACME_API_URL.
|
|
53
|
-
goal: >-
|
|
54
|
-
The script prints hello-from-acme from inside a sandbox and deletes the sandbox
|
|
55
|
-
at the end.
|
|
56
|
-
checks:
|
|
57
|
-
- run: grep -Eq '\.(delete|remove)\(' -r "$FIREGOAT_WORKDIR" --include='*.py'
|
|
58
|
-
- run: "! grep -Eq '\"command\":\"[^\"]*pip3? install[^\"]*acme' \"$FIREGOAT_SESSION\""
|
|
59
|
-
timeout: 900
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
### 4. Pure user need, discovery
|
|
63
|
-
|
|
64
|
-
No docs URL, no package names: the test is whether the agent finds the right packages.
|
|
65
|
-
|
|
66
|
-
```yaml
|
|
67
|
-
tests:
|
|
68
|
-
- name: which-packages
|
|
69
|
-
prompt: >-
|
|
70
|
-
I'm building a mobile app with Acme. It needs a local database, maps, age
|
|
71
|
-
gating, localization, Sign in with Apple and a small on-device model. Does Acme
|
|
72
|
-
offer packages for these? Which ones would you use?
|
|
73
|
-
goal: >-
|
|
74
|
-
The answer names the right Acme package for each requirement and relies on the
|
|
75
|
-
docs shipped in the project, not web search.
|
|
76
|
-
checks:
|
|
77
|
-
- run: "! grep -q '\"name\":\"WebFetch\"' \"$FIREGOAT_SESSION\""
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
### 5. Shortest
|
|
81
|
-
|
|
82
|
-
```yaml
|
|
83
|
-
tests:
|
|
84
|
-
- name: coffee-timer
|
|
85
|
-
prompt: Create a new app - a coffee timer. Run it on the simulator and get the screenshot.
|
|
86
|
-
goal: A screenshot shows a working coffee timer app running on the simulator.
|
|
87
|
-
checks:
|
|
88
|
-
- run: find "$FIREGOAT_WORKDIR" -name '*.png' -size +1k | grep -q .
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
## Bad
|
|
92
|
-
|
|
93
|
-
### No goal, no checks
|
|
94
|
-
|
|
95
|
-
```text
|
|
96
|
-
prompt: "Create a sandbox. https://docs.acme.dev"
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
Ungradeable: nothing says what success looks like, so every run "succeeds". Add an
|
|
100
|
-
observable outcome ("run echo hello-from-acme inside it and print the output"), a goal
|
|
101
|
-
and a check.
|
|
102
|
-
|
|
103
|
-
### Implementation steps
|
|
104
|
-
|
|
105
|
-
```text
|
|
106
|
-
prompt: >-
|
|
107
|
-
1. Create a project from the acme-starter template.
|
|
108
|
-
2. Add the acme-maps package.
|
|
109
|
-
3. Render a map centered on Berlin.
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
This tests instruction-following, not whether an agent finds its way with your docs.
|
|
113
|
-
Rewrite as the user's need: "Show a map centered on Berlin in a new Acme app. Keep it
|
|
114
|
-
minimal. Take a screenshot."
|