Skip to content

feat(cadre): ship Bonfire cadre as Claude Code subagent definitions#155

Draft
Antawari wants to merge 2 commits into
mainfrom
ishtar/bonfire-plugin-and-install-agents
Draft

feat(cadre): ship Bonfire cadre as Claude Code subagent definitions#155
Antawari wants to merge 2 commits into
mainfrom
ishtar/bonfire-plugin-and-install-agents

Conversation

@Antawari
Copy link
Copy Markdown
Contributor

Why

When the Bonfire cadre dispatches via Claude Code's Agent tool today, every dispatch surfaces as subagent_type: "general-purpose". The substance is Bonfire — the cadre methodology, the role discipline, the TDD law — but the label is generic. The framework is invisible at the most visible API boundary, and there's no discovery surface for users who install bonfire-ai and use Claude Code.

This PR fixes that. After install, dispatches surface as bonfire:scout-innovative, bonfire:knight, bonfire:warrior, etc. — the cadre at the API surface, sister-shape to vercel:*, agent-sdk-dev:*, and the other published Claude Code plugins.

What ships

Two install rails, one ship-train, same bonfire-public repo:

Plugin (canonical · colon-namespaced)

```bash
/plugin install bonfire@
```

Adds:

  • `.claude-plugin/plugin.json` — manifest with six agent entries and an author block.
  • `agents/.md` — six pre-rendered subagent files (`scout-innovative`, `scout-conservative`, `knight`, `warrior`, `sage`, `wizard`) with YAML frontmatter (`name`, `description`, `tools`, `model`, `cadre_contract`).

After install, the Claude Code subagent picker shows `bonfire:` entries and dispatches surface with that literal label.

Raw-files CLI (fallback · flat-named)

```bash

user scope (~/.claude/agents/bonfire/) is the default

bonfire install-agents

project scope

bonfire install-agents --scope project

inspect

bonfire list-agents

clean removal (manifest-driven)

bonfire uninstall-agents
```

The CLI rail exists for three structural reasons:

  1. Plugin-shipped agents silently drop `hooks`, `mcpServers`, and `permissionMode` frontmatter per Claude Code's security policy. v1 cadre roles don't require those fields, but the rail must exist for future roles that might.
  2. Raw user-scope agents are priority 4; plugin agents are priority 5. The CLI rail gives operators a "fork and override" lane — drop a customized `bonfire-scout.md` into `~/.claude/agents/` and the plugin copy is shadowed.
  3. Plugin path requires Claude Code CLI. Users on Cursor, Codex, raw SDK, or pipx-isolated environments cannot enable plugins. The CLI rail is the universal channel.

The CLI rail also installs the `bonfire-powered` catch-all — a Bonfire-flavored general-purpose agent that sits flat-named in the picker next to `general-purpose` for users who want the cadre's discipline without picking a specific role. The catch-all does NOT ship via the plugin (head-to-head brand contrast requires flat naming).

New modules

  • `src/bonfire/prompts/.md` — canonical prompt bodies (seven files: six cadre + catch-all). Wheel-bundled via the existing `[tool.hatch.build.targets.wheel].include` pattern.
  • `src/bonfire/agent/role_metadata.py` — per-role metadata (description, tools, model). Sits adjacent to `agent/roles.py`'s `AgentRole` StrEnum.
  • `src/bonfire/cadre/init.py` — `CADRE_CONTRACT_VERSION` constant + `resolve_role_prompt(role)` adapter (skeleton; see Out of scope below).
  • `src/bonfire/cli/commands/build_agents.py` — generator that emits `agents/.md` from prompts + metadata. CI gates with `bonfire build-agents --check`.
  • `src/bonfire/cli/commands/install_agents.py` — install / uninstall / list subcommands.

Versioning

Three orthogonal pins:

Pin What it tracks
`bonfire_ai.version` Library releases
Plugin `version` in `plugin.json` Prompt-text changes
`CADRE_CONTRACT_VERSION` (in `bonfire.cadre`) Dispatch-boundary breaking changes; stamped into each subagent's `cadre_contract` frontmatter

Inaugural ship: plugin `0.1.0` + `CADRE_CONTRACT_VERSION = "0.1.0"`.

Per-role tool scoping

Role Tools Notes
`scout-innovative` Read, Grep, Glob, WebSearch, WebFetch Read-only
`scout-conservative` Read, Grep, Glob, WebSearch, WebFetch Read-only
`knight` Read, Grep, Glob, Write, Edit No Bash — writes RED tests; Warrior runs the cycle
`warrior` Read, Grep, Glob, Write, Edit, Bash Runs the RED→GREEN cycle
`sage` Read, Grep, Glob, Write, Edit Writes synthesis docs
`wizard` Read, Grep, Glob Read-only; composes workflows (Agent tool is unavailable to subagents period)
`bonfire-powered` Read, Grep, Glob, WebSearch, WebFetch Safe read-only default

Warrior Bash discipline

Background-dispatched subagents auto-deny Bash calls outside cached permission rules — listing `Bash` in `tools:` is necessary but not sufficient. This PR ships `.claude/settings.local.json.example` with allow-list entries for the canonical Warrior-pattern Bash commands (`pytest`, `ruff`, `mypy`, `git add/commit/status/diff/log`). Documented as opt-in recommendation; never silently auto-installed.

Out of scope (intentional · follow-up tickets)

To keep this PR single-concern:

  • Library-side dispatch-path wiring. The `bonfire.cadre.resolve_role_prompt(role)` adapter ships as a SKELETON. The actual dispatch-side enforcement (refusal on `cadre_contract` mismatch with a structured error envelope) is deferred. The contract is pinned here; the enforcement is the next ticket.
  • Bracket roles (Architect, Artificer, Inquisitor, Loremaster). Deferred to a v1.1 follow-up pending the knowledge-backend distribution shape settling.
  • Cleric, Bard, Steward. Future scope — no on-disk prompts exist for these roles yet.
  • Marketplace submission to `claude-plugins-community`. This PR scaffolds the plugin; submission is a separate operational step.

Verification

```bash
.venv/bin/bonfire build-agents --check # OK: generated files match canonical sources
.venv/bin/pytest tests/unit/ -q # 3860 passed (54 new tests added by this PR)
.venv/bin/ruff check src/ tests/ # All checks passed!
.venv/bin/ruff format --check src/ tests/ # 8 files already formatted
claude plugin validate ./.claude-plugin # passes (one pre-existing warning about repo-root CLAUDE.md placement, unrelated to this PR)
```

The `claude plugin validate --strict` warning is about the repo-root `CLAUDE.md` being a contributor-facing doc (not a plugin skill); fixing it is a documentation-restructuring follow-up, not blocking.

Test plan

  • CI green (`pytest`, `ruff check`, `ruff format --check`)
  • Reviewer: confirm the plugin manifest installs cleanly via `claude --plugin-dir .` and `@agent-bonfire:scout-innovative` dispatches surface as `subagent_type: "bonfire:scout-innovative"` literally
  • Reviewer: confirm `bonfire install-agents --scope user --dry-run` lists 7 files + manifest
  • Reviewer: confirm `bonfire install-agents --scope user` then `bonfire uninstall-agents` is clean (no orphan files; manifest-driven removal)
  • Reviewer: confirm the catch-all `bonfire-powered` appears flat-named in the picker after CLI install (NOT in the plugin install)

Status

DRAFT. Opening for review and to make the surface visible; not for merge until reviewer sign-off and the marketplace submission decision lands separately.

Antawari added 2 commits May 27, 2026 22:01
Two install rails ship from this repo:

1. Claude Code plugin (canonical, colon-namespaced). Adds a
   `.claude-plugin/plugin.json` manifest and pre-rendered
   `agents/<role>.md` files. After install, dispatches surface as
   `bonfire:<role>` (sister-shape to vercel:*, agent-sdk-dev:*, etc.)
   instead of the generic `general-purpose` label.

2. Raw-files CLI fallback (flat-named). `bonfire install-agents`,
   `bonfire uninstall-agents`, `bonfire list-agents` Typer subcommands
   drop `bonfire-<role>.md` files at user or project scope, recorded
   via a manifest at `.installed.json` for paired-clean uninstall. Pip
   post-install hooks are deliberately rejected (wheel installs don't
   run install-time code; pip uninstall can't clean files outside
   site-packages).

v1 publishes six cadre roles plus a standalone catch-all:
  - bonfire:scout-innovative + bonfire:scout-conservative
    (dual-workflow split preserved at the subagent layer)
  - bonfire:knight (writes RED tests, no Bash)
  - bonfire:warrior (runs the RED->GREEN cycle, has Bash)
  - bonfire:sage (synthesizes across two Scouts)
  - bonfire:wizard (composes workflows; read-only)
  - bonfire-powered (flat-named catch-all, CLI-rail-only,
    head-to-head with general-purpose in the picker)

New modules:
  - src/bonfire/prompts/<role>.md - canonical prompt bodies
  - src/bonfire/agent/role_metadata.py - per-role metadata
    (description, tools, model)
  - src/bonfire/cadre/__init__.py - CADRE_CONTRACT_VERSION constant
    + resolve_role_prompt(role) adapter (skeleton; dispatch-side
    refusal-on-mismatch deferred to a follow-up to keep this PR
    single-concern)
  - src/bonfire/cli/commands/build_agents.py - generator that emits
    agents/<role>.md from prompts + metadata; CI gates with --check
  - src/bonfire/cli/commands/install_agents.py - install / uninstall /
    list subcommands

Versioning uses three orthogonal pins: bonfire_ai.__version__ (library
releases), plugin.json `version` (prompt-text changes), and
CADRE_CONTRACT_VERSION (dispatch-boundary breaking changes; stamped
into each subagent's `cadre_contract` frontmatter for drift detection).

Warrior Bash discipline: ships .claude/settings.local.json.example
with allow-list entries for the canonical Warrior-pattern Bash commands
(pytest, ruff, git add/commit, etc.) since background-dispatched
subagents auto-deny Bash outside cached permission rules.

Tests: 54 new (test_cadre.py + test_build_agents.py +
test_install_agents.py). Full unit suite green (3860 passed).

Knight extraction note: the knight prompt body was authored fresh in
this repo (no migration of any prior template); the source-of-truth
for the cadre body lives in src/bonfire/prompts/ from this PR forward.
…uble-prefix

Two related corrections in `bonfire install-agents`:

1. CLI-installed cadre files now write `name: bonfire-<role>` in the
   frontmatter (not the bare `name: <role>`). The plugin path produces
   `bonfire:<role>` by prepending the plugin name; the raw-files path
   has no plugin to prepend, so the brand prefix must live in the file
   for the subagent type to surface as `bonfire-<role>`.

2. The catch-all role is already named `bonfire-powered`; the install
   helper no longer prepends the prefix a second time. Previous output
   produced `bonfire-bonfire-powered.md` with `name: bonfire-bonfire-
   powered`; now produces `bonfire-powered.md` with `name: bonfire-powered`.

New helper `_flat_name(role_name)` centralises the prefix policy
(prefix if absent, leave alone if present). New `_compose_flat(role)`
builds the CLI-shape frontmatter using `_flat_name` so the install
output's `name:` field matches the filename.

Plugin-shipped agents (built via `bonfire build-agents`) are
unchanged — they continue to use the bare `name:` so the plugin loader
can prepend the colon namespace cleanly.

Two new tests pin the contract: `test_install_writes_flat_prefixed
_names_for_cadre` confirms the cadre files carry `name: bonfire-<role>`;
`test_install_does_not_double_prefix_catchall` confirms the catch-all
lands as `bonfire-powered.md` (not the double-prefixed form).
@Antawari Antawari force-pushed the ishtar/bonfire-plugin-and-install-agents branch from 6618d4c to d577669 Compare May 28, 2026 04:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant