task_id: danbooru-cli-build
repo: /tmp/danbooru-starter
ref: master
prompt: |
  Build a CLI for the Danbooru API from scratch.

  The repository has a minimal scaffold (pyproject.toml, empty
  danbooru_cli package, cli.py stub). The full spec is in README.md.
  Read it carefully — it covers:

  - binary name (`danbooru`) and entry point
  - 7 subcommands: `tags`, `wiki`, `related`, `aliases`, `post`,
    `posts`, `download`
  - a global `--base` flag and `DANBOORU_BASE` env var that **must
    accept a URL scheme** so the tool can point at plain-HTTP test
    servers
  - the Danbooru API endpoints (including the nested-bracket search
    syntax like `search[name_matches]=...&search[order]=count`)
  - the post object shape and per-category tag strings
  - exit codes (0 on success / soft-not-found; 1 on HTTP errors)

  Implement so that `uv run danbooru <cmd> --raw` emits a stable JSON
  contract per command. The grader pipes `--raw` output into pytest
  assertions; treat the shape in README.md as a hard contract.

  Structure the code across files inside `src/danbooru_cli/` as you
  like; keep `danbooru_cli.cli:app` as the entry point.

setup: uv sync
timeout_minutes: 30
node_version: ""

hidden_tests_dir: hidden/danbooru-cli-build
hidden_test_command: |
  pytest tests-hidden/ -v --tb=short -p no:cacheprovider 2>&1 | tail -80

danbooru-cli — specification (README.md)

Build a CLI for the Danbooru booru API.
Written in Python with Typer for command
handling and Rich for formatted output.

The package is scaffolded in src/danbooru_cli/. The binary is named
danbooru (see pyproject.toml). Use uv sync to install deps and the
entry point; uv run danbooru … to run.

Environment

The API base URL is controlled by DANBOORU_BASE (env) or a global
--base flag. Default: danbooru.donmai.us (implied scheme https).

Important: DANBOORU_BASE / --base may include a URL scheme.
If it starts with http:// or https://, use it verbatim. Otherwise
prefix https://. This lets the tool point at local test servers over
plain HTTP.

API endpoints

All endpoints return JSON. All GET requests.

  • GET /tags.json?search[name_matches]=Q&search[order]=count&limit=N
    — returns an array of tag objects:
    { name, post_count, category, is_deprecated, ... }
    Category codes: 0=general, 1=artist, 3=copyright, 4=character, 5=meta.
    You may also pass search[category]=N to filter by category.
  • GET /wiki_pages.json?search[title]=X&limit=1 — returns an array
    (may be empty); each page has title, body, other_names.
  • GET /related_tag.json?query=X — returns an object:
    { tag: { name, post_count }, related_tags: [ { tag, jaccard_similarity } ], wiki_page_tags: [ { tag } ] }
  • GET /tag_aliases.json?search[antecedent_name_matches]=X&search[status]=active&limit=N
    — array of { antecedent_name, consequent_name, ... }.
  • GET /posts.json?tags=T&limit=N — array of post objects.
  • GET /posts/{id}.json — a single post object.
  • GET /posts/{id}.json followed by GET of file_url — download
    image bytes.

Post shape (relevant fields)

{
  "id": int,
  "score": int,
  "fav_count": int,
  "rating": str,
  "file_url": str,
  "large_file_url": str,
  "tag_string_general": str,
  "tag_string_character": str,
  "tag_string_copyright": str,
  "tag_string_artist": str,
  "tag_string_meta": str
}

On HTTP error: red message on stderr + exit 1.

Commands

All commands accept --raw to emit the API JSON on stdout (verbatim
where possible). Exit 0 on success or soft "nothing found"; exit 1 on
HTTP/parse errors.

danbooru tags <query> [-n LIMIT] [-c CATEGORY] [--raw]

Search tags by name pattern (* wildcards passed through to
search[name_matches]). -c filters by category. Ordered by post count
descending. Default LIMIT 20. --raw: the array of tag objects verbatim.

danbooru wiki <title> [--raw]

Fetch the wiki page for a tag name. Non-raw prints title, other names,
body. --raw: the first wiki page object (or empty output/null if not
found — either is acceptable; the command must exit 0).

Returns the raw related_tag.json response. Non-raw prints two sections
(wiki_page_tags, related_tags) each truncated to LIMIT. Default LIMIT 20.
--raw: the full response object (not truncated).

danbooru aliases <query> [-n LIMIT] [--raw]

Search active tag aliases where antecedent matches the pattern. Default
LIMIT 10. --raw: the array of alias objects verbatim.

danbooru post <id> [--raw]

Fetch a single post. Non-raw prints score/favs/rating + per-category
tag listings + image URL. --raw: the post object verbatim.

danbooru posts <tag_query> [-n LIMIT] [--raw]

Search posts by tags (space-separated, anonymous caps at 2). Default
LIMIT 10. --raw: the array of post objects verbatim.

danbooru download <id> [-o PATH] [--original]

Fetch the post by id, then GET the image URL. By default use
large_file_url (falls back to file_url); --original forces
file_url. Write bytes to PATH, falling back to
/tmp/danbooru_{id}.{ext} (ext derived from URL). Print the path as
the last stdout line. No --raw flag for this command.

What to deliver

A working implementation in src/danbooru_cli/ where uv sync installs
and uv run danbooru … works. Split across files as you like; keep
danbooru_cli.cli:app exported.

The grader invokes uv run danbooru … as a subprocess with
DANBOORU_BASE pointing at a local HTTP fixture server over plain HTTP.
The --raw shape is the hard contract.

Edit

Pub: 25 Apr 2026 03:17 UTC

Views: 28