# Writing Claude Code Skills for bzr
This document is specific to Claude Code's skill format. For IBM Bob's skill format, see [bob-skills.md](bob-skills.md).
A guide for writing [Claude Code skills](https://docs.anthropic.com/en/docs/claude-code/skills) that drive bzr. Skills turn natural-language prompts into repeatable Bugzilla workflows your agent can execute.
bzr is a good fit for skills because every command supports `--json`, flags are consistent across subcommands, and the CLI covers the full Bugzilla lifecycle (bugs, comments, attachments, flags, products, users, groups, templates, saved queries, config).
For the full command reference, see [bzr-cli.md](bzr-cli.md).
## Directory Layout
Skills live in `.claude/skills/` as `SKILL.md` files. Since bzr skills are server-agnostic (use `--server` to target any configured instance), place them in your home directory so they work across all projects:
```
~/.claude/skills/
bzr-triage/SKILL.md
bzr-investigate/SKILL.md
bzr-bug-summary/SKILL.md
bzr-file-bug/SKILL.md
bzr-review/SKILL.md
bzr-setup/SKILL.md
bzr-admin/SKILL.md
bzr-saved-queries/SKILL.md
```
One skill per directory. The directory name becomes the `/slash-command` name.
## Key Patterns
Three patterns every bzr skill should use:
### `--json` goes before the subcommand
The `--json` flag is global — it must appear before the subcommand:
```bash
# preferred (shorthand)
bzr --json bug list --product Fedora
# also works (original form)
bzr --output json bug list --product Fedora
# wrong — flag is ignored
bzr bug list --product Fedora --output json
```
Always use `--json` in skills so the agent gets structured data it can parse.
### `allowed-tools` scopes permissions
Restrict what the skill can run. Add `Bash(jq *)` when the skill needs to parse JSON:
```yaml
allowed-tools: Bash(bzr *), Bash(jq *)
```
### `$ARGUMENTS` passes dynamic input
Use `$ARGUMENTS` for bug IDs, search terms, product names. The user provides them when invoking the skill:
```
/bzr-investigate 12345
```
Inside the skill body, `$ARGUMENTS` expands to `12345`.
### Avoid interactive or ambiguous steps
Skills should prefer explicit, non-interactive commands and should gather missing inputs before invoking write operations.
Examples:
- `bzr comment add 12345` at a TTY opens `$EDITOR`, which hangs non-interactive skill runs. Use `bzr comment add 12345 --body "text"` or pipe stdin instead.
- "File a bug" is incomplete if the skill has not collected `product`, `component`, and a useful description. Ask for them first, or use `bzr bug create --template <name> --summary "..." --description "..."`.
- "Resolve this bug" is risky if the skill has not checked valid status and resolution values for that server. Verify them with `bzr field list status` and `bzr field list resolution` before calling `bzr bug update`.
## Skill Examples
Eight complete skills covering common Bugzilla workflows. Copy any of these into `~/.claude/skills/<name>/SKILL.md`.
### bzr-triage — Search and prioritize NEW bugs
```yaml
---
name: bzr-triage
description: Search for NEW bugs in a product and help prioritize them
argument-hint: [product]
allowed-tools: Bash(bzr *), Bash(jq *)
---
# Triage NEW Bugs
Search for untriaged bugs in the **$ARGUMENTS** product and help prioritize them.
## Steps
1. List NEW bugs:
`bzr --json bug list --product "$ARGUMENTS" --status NEW --limit 50`
2. Look up valid priorities and severities:
`bzr --json field list priority`
`bzr --json field list severity`
3. Present a summary table of bugs (ID, summary, severity, assignee)
4. For each bug the user selects, update priority/severity/assignee:
`bzr bug update <ID> --priority <P> --severity <S>`
Always confirm before updating bugs.
```
### bzr-investigate — Deep-dive into a single bug
```yaml
---
name: bzr-investigate
description: Gather all context for a bug — details, comments, history, and attachments
argument-hint: [bug-id]
allowed-tools: Bash(bzr *), Bash(jq *)
---
# Investigate Bug
Gather full context for bug **$ARGUMENTS**.
## Steps
1. View the bug:
`bzr --json bug view $ARGUMENTS`
2. List comments:
`bzr --json comment list $ARGUMENTS`
3. View change history:
`bzr --json bug history $ARGUMENTS`
4. List attachments:
`bzr --json attachment list $ARGUMENTS`
5. Summarize findings: current status, key discussion points,
recent changes, and any pending review requests or needinfo flags.
```
### bzr-bug-summary — Analyze and summarize bug history
```yaml
---
name: bzr-bug-summary
description: Fetch bug details and generate a concise summary of status, history, blockers, and next actions
argument-hint: [bug-id...]
allowed-tools: Bash(bzr *), Bash(jq *)
---
# Summarize Bugzilla Bugs
Analyze one or more bugs from **$ARGUMENTS** and produce a concise technical summary.
## Steps
1. For each bug, fetch core details:
`bzr --json bug view <BUG_ID>`
2. Fetch change history:
`bzr --json bug history <BUG_ID>`
3. Fetch comments when recent discussion matters:
`bzr --json comment list <BUG_ID>`
4. Fetch attachments when patches, logs, or test cases may affect the summary:
`bzr --json attachment list <BUG_ID>`
5. Extract the important points: status, assignee, component, whiteboard, dependencies, recent activity, and any blockers or next actions.
6. Present results:
- For a single bug, summarize the issue, current state, recent activity, blockers, and recommended next step.
- For multiple bugs, start with a comparison table and then give short summaries for each bug.
Keep the summary factual and concise. Do not speculate about root cause when the bug history does not support it.
```
### bzr-file-bug — File a new bug interactively
```yaml
---
name: bzr-file-bug
description: File a new bug with product/component validation
argument-hint: [product]
disable-model-invocation: true
allowed-tools: Bash(bzr *), Bash(jq *)
---
# File a New Bug
File a bug in the **$ARGUMENTS** product.
## Steps
1. Verify the product exists and list its components:
`bzr --json product view "$ARGUMENTS"`
2. Look up valid priorities and severities:
`bzr --json field list priority`
`bzr --json field list severity`
3. Ask the user for: component, summary, description, priority, severity
4. Check for saved templates:
`bzr template list`
If one matches the product, use it:
`bzr bug create --template <TMPL> --summary "<S>" --description "<D>"`
5. Otherwise, create the bug (always pass `--description` to avoid opening `$EDITOR`):
`bzr bug create --product "$ARGUMENTS" --component <C> --summary "<S>" --description "<D>" --priority <P> --severity <S>`
6. Show the created bug ID.
```
### bzr-review — Review patches on a bug
```yaml
---
name: bzr-review
description: Download and review patch attachments on a bug
argument-hint: [bug-id]
allowed-tools: Bash(bzr *), Bash(jq *), Bash(cat /tmp/bzr-review-*)
---
# Review Patches
Review patch attachments on bug **$ARGUMENTS**.
## Steps
1. List attachments and filter for patches:
`bzr --json attachment list $ARGUMENTS | jq '[.[] | select(.is_patch == true and .is_obsolete == false)]'`
2. For each active patch, download to `/tmp`:
`bzr attachment download <ID> -o /tmp/bzr-review-<ID>.patch`
3. Read and review each patch file
4. Present a review summary for each patch:
- What the patch changes
- Potential issues or risks
- Suggested review flag (review+, review-, review?)
5. If the user approves, post a review comment:
`bzr comment add $ARGUMENTS --body "<review text>"`
And optionally set the review flag:
`bzr attachment update <ID> --flag "review+(user@example.com)"`
```
### bzr-setup — Configure a Bugzilla server
```yaml
---
name: bzr-setup
description: Walk through configuring a new Bugzilla server connection
disable-model-invocation: true
allowed-tools: Bash(bzr *)
---
# Set Up a Bugzilla Server
Walk the user through connecting to a Bugzilla server.
## Steps
1. Ask for: server alias name, URL, API key, and optionally email
(email is needed for Bugzilla 5.0 or earlier)
2. Configure the server:
`bzr config set-server <NAME> --url <URL> --api-key-env <ENV_VAR>`
Add `--email <EMAIL>` if provided.
3. Verify the connection:
`bzr --server <NAME> whoami`
4. Show server info:
`bzr --server <NAME> server info`
5. If this is the first server, it becomes the default automatically.
Otherwise ask if the user wants to set it as default:
`bzr config set-default <NAME>`
6. Show final config:
`bzr --json config show`
```
### bzr-admin — Admin: products, users, groups
```yaml
---
name: bzr-admin
description: Admin operations — create/update products, components, users, and groups
disable-model-invocation: true
allowed-tools: Bash(bzr *), Bash(jq *)
---
# Bugzilla Admin Operations
Perform admin operations on the Bugzilla server. All commands below
require admin privileges.
## Available operations
Ask the user what they want to do:
### Products
- List products: `bzr --json product list`
- View product: `bzr --json product view "<NAME>"`
- Create product: `bzr product create --name "<N>" --description "<D>"`
- Update product: `bzr product update "<NAME>" --description "<D>"`
### Components
- Create: `bzr component create --product "<P>" --name "<N>" --description "<D>" --default-assignee <E>`
- Update: `bzr component update <ID> --name "<N>"`
### Users
- Search: `bzr --json user search "<QUERY>"`
- Create: `bzr user create --email <E> --full-name "<N>"`
- Update: `bzr user update <USER> --real-name "<N>"`
### Groups
- View: `bzr --json group view <GROUP>`
- List members: `bzr --json group list-users --group <G>`
- Create: `bzr group create --name "<N>" --description "<D>"`
- Add user: `bzr group add-user --group <G> --user <U>`
- Remove user: `bzr group remove-user --group <G> --user <U>`
Confirm before every write operation.
```
### bzr-saved-queries — Manage and run saved queries
```yaml
---
name: bzr-saved-queries
description: Save, list, and run reusable bug queries
argument-hint: [action] [name]
allowed-tools: Bash(bzr *), Bash(jq *)
---
# Saved Queries
Manage saved bug queries. Saved queries store filter combinations
locally and can be executed later with `query run`.
## Steps
Parse `$ARGUMENTS` as `<action> [name] [extra]`:
### save
1. Ask the user for filters: product, component, status, priority,
severity, assignee, creator, limit. At least one filter is required.
For free-text search queries, use `--search` instead of list filters.
2. Save the query:
`bzr query save <name> --product P --status NEW --status CONFIRMED --limit 50`
Or for search queries:
`bzr query save <name> --search "crash on startup" --limit 20`
3. Confirm: the response shows `"action": "saved"` or `"action": "updated"`.
### list
1. List all saved queries:
`bzr query list`
### show
1. Show details of a saved query:
`bzr --json query show <name>`
### run
1. Execute a saved query against the server:
`bzr --json query run <name>`
Override limit or fields at runtime:
`bzr --json query run <name> --limit 10 --fields id,summary,status`
### delete
1. Delete a saved query:
`bzr query delete <name>`
```
## Composing Multi-Step Skills
Skills can chain multiple bzr commands into a pipeline. Two examples:
### Bug-to-patch review pipeline
This skill searches for bugs with unreviewed patches and builds a review queue:
```yaml
---
name: bzr-review-queue
description: Find bugs with unreviewed patches and present a review queue
argument-hint: [product]
allowed-tools: Bash(bzr *), Bash(jq *), Bash(cat /tmp/bzr-review-*)
---
# Patch Review Queue
Find bugs in **$ARGUMENTS** that have unreviewed patches.
## Steps
1. List open bugs in the product:
`bzr --json bug list --product "$ARGUMENTS" --status NEW --status ASSIGNED --limit 100`
2. For each bug, check for non-obsolete patch attachments:
`bzr --json attachment list <BUG_ID> | jq '[.[] | select(.is_patch == true and .is_obsolete == false)]'`
3. Collect bugs that have at least one active patch into a review queue.
4. For each bug in the queue, download the latest patch:
`bzr attachment download <ATTACHMENT_ID> -o /tmp/bzr-review-<ATTACHMENT_ID>.patch`
5. Present the review queue as a numbered list:
- Bug ID, summary, patch count, last updated
6. Let the user pick a bug to review, then show the patch content
and help them write a review comment.
```
### Sprint report
This skill generates a summary of resolved bugs over a date range:
```yaml
---
name: bzr-sprint-report
description: Summarize resolved bugs in a product for a date range
argument-hint: [product]
allowed-tools: Bash(bzr *), Bash(jq *)
---
# Sprint Report
Generate a report of resolved bugs in **$ARGUMENTS**.
## Steps
1. Ask the user for the date range (start and end dates).
2. List resolved bugs:
`bzr --json bug list --product "$ARGUMENTS" --status RESOLVED --limit 200`
3. Use `jq` to filter bugs by last-change date within the range
and group by resolution:
`jq '[.[] | select(.last_change_time >= "START" and .last_change_time <= "END")]
| group_by(.resolution)
| map({resolution: .[0].resolution, count: length, bugs: [.[].id]})'`
4. Present a summary:
- Total bugs resolved
- Breakdown by resolution (FIXED, WONTFIX, DUPLICATE, etc.)
- List of bug IDs per resolution category
5. Optionally drill into any category for full bug details.
```
## Tips and Gotchas
- **Use `--json` shorthand**: `--json` is equivalent to `--output json` but shorter. Skills can also set `BZR_OUTPUT=json` in the environment to avoid repeating the flag.
- **Auto-detection**: When bzr's stdout is not a TTY (piped, redirected, or captured with `$(...)`), bzr outputs JSON automatically. At an interactive TTY, use `--json` or set `BZR_OUTPUT=json`.
- **Flag positioning**: `--json` and `--server` go *before* the subcommand. `bzr --json bug list`, not `bzr bug list --json`.
- **Exit codes**: bzr uses distinct exit codes 0–13 (e.g. 0=success, 2=usage/not-found, 3=config, 4=API, 5=network, 6=IO, 9=auth, 12=keyring, 13=TLS pin mismatch). See `docs/bzr-cli.md` for the full table. Skills can check `$?` to handle errors without parsing stderr.
- **JSON errors**: When `--json` is active, errors are emitted as JSON on stderr: `{"error":{"type":"api","message":"...","exit_code":4}}`.
- **Mutation responses**: Create/update commands return structured JSON with `--json`: `{"id":123,"resource":"bug","action":"created"}`. No need for regex to extract IDs.
- **Adding comments**: Use `--body "<text>"` in skills — it is explicit and works in all contexts. Alternatively, `echo "text" | bzr comment add 12345` works when stdin is not a TTY. Omitting both at a TTY opens `$EDITOR`, which hangs in non-interactive skill runs.
- **Attachment downloads**: Use `-o /tmp/<name>` to save attachments to a known path the agent can read back.
- **Validate before updating**: Use `bzr field list <field>` to check valid values for status, priority, severity, and resolution before calling `bug update`.
- **Flag syntax**: `review?(user@example.com)` requests review, `review+` grants, `review-` denies. See [Flag Syntax](bzr-cli.md#flag-syntax).
- **Default limit is 50**: `bug list` and `bug search` return at most 50 results by default. Set `--limit` explicitly when you need more (or fewer).
- **`disable-model-invocation: true`**: Use this for skills with side effects (creating bugs, updating configs, admin ops) so Claude only runs them when you explicitly invoke `/skill-name`.
- **Templates**: Save common field sets with `bzr template save <name> --product P --component C --priority Normal --severity normal`, then file bugs with `bzr bug create --template <name> --summary "..."`. Templates are local config, not server state.
- **Saved queries**: Store reusable search filters with `bzr query save <name> --product P --status NEW --limit 50`, then run them with `bzr --json query run <name>`. Supports runtime overrides (`--limit`, `--fields`).
- **Field aliases**: `bzr field aliases` lists all field name aliases (e.g. `status` → `bug_status`, `severity` → `bug_severity`). Use the short names in skills.
- **TLS**: For self-signed or internal Bugzilla servers, set `--tls-insecure` when configuring the server: `bzr config set-server internal --url https://bugzilla.internal --api-key-env INTERNAL_BZ_API_KEY --tls-insecure`. The flag is persisted per-server, not passed on every command.
- **Multi-server**: Pass `--server <name>` to target a non-default server. Works with every command.
## Quick Reference
Common tasks mapped to bzr commands for use in skills:
| Task | Command |
|------|---------|
| List open bugs | `bzr --json bug list --product P --status NEW` |
| Full-text search | `bzr --json bug search "query" --limit 20` |
| View bug details | `bzr --json bug view ID` |
| View bug history | `bzr --json bug history ID` |
| Create a bug | `bzr bug create --product P --component C --summary "S" --description "D"` |
| Update bug status | `bzr bug update ID --status S --resolution R` |
| Set a flag | `bzr bug update ID --flag "review?(user@example.com)"` |
| List comments | `bzr --json comment list BUG_ID` |
| Add a comment | `bzr comment add BUG_ID --body "text"` |
| List attachments | `bzr --json attachment list BUG_ID` |
| Download attachment | `bzr attachment download ID -o /tmp/file` |
| Upload attachment | `bzr attachment upload BUG_ID file.patch --flag "review?(user)"` |
| List products | `bzr --json product list` |
| View product | `bzr --json product view NAME` |
| Look up field values | `bzr --json field list priority` |
| List field aliases | `bzr --json field aliases` |
| Save a template | `bzr template save NAME --product P --component C` |
| Create bug from template | `bzr bug create --template NAME --summary "S"` |
| List templates | `bzr template list` |
| Save a query | `bzr query save NAME --product P --status NEW --limit 50` |
| Run a saved query | `bzr --json query run NAME` |
| List saved queries | `bzr query list` |
| Search users | `bzr --json user search "query"` |
| Check auth | `bzr --json whoami` |
| Server version | `bzr --json server info` |
| Show config | `bzr --json config show` |