Add bbox agents add/init scaffolding (#201) - #210
Conversation
Phase 2 of bring-your-own agents: author a custom agent without hand- editing YAML. - `agents add NAME --image ... --command ...` appends a validated custom agent to the GLOBAL config, refusing to overwrite a built-in or existing custom agent unless --force. Reuses the pure AgentFromOverride and ValidateCustomAgent from #200. - `agents init [NAME]` prints a commented starter agents: stanza to stdout, reusing the default config template's documentation. - UpsertAgent (infra) performs a comment-preserving YAML node round-trip; a comment-only file (the `config init` template) is kept verbatim and the stanza appended, so documentation is never dropped. - Both `agents add` and `agents doctor --json` emit a machine-readable receipt: agent summary, env vars by name only, MCP state, validator version, before/after config fingerprints, and explicit non-effects. Custom agents remain global-only; the domain stays pure (writing is I/O in internal/infra/config + cmd/bbox). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FAvtCCPrzJgL7z7WMPsLTG
CLAUDE.md's custom-agent section still listed only list|inspect|doctor; add and init were added in a prior PR but never mentioned.
buildAddAgentOverride only applied the authz profile inside the --mcp branch, so setting --mcp-authz-profile without --mcp silently had no effect and the receipt showed no mcp section at all.
Two concurrent `bbox agents add` invocations could silently lose one write (last-writer-wins, no error) since the read-modify-write had no locking. Serialize it with a blocking advisory flock on a sidecar file, matching the pattern already used in internal/infra/vm/firmware.go, and make the write itself atomic via temp-file-plus-rename so a crash mid-write can't corrupt the config or leave a torn file visible to readers. Also detect "---"-separated multi-document YAML in rootMapping and error instead of silently dropping every document after the first, which yaml.Unmarshal would otherwise do on re-encode.
Review summaryRan a full review of this PR (adapted ToolHive review categories to brood-box's DDD layers) using Security review: clean, no findings — Fixed in follow-up commits:
Deferred, tracked separately:
All fixes verified via |
jhrozek
left a comment
There was a problem hiding this comment.
Reviewed and fixed the issues found (see comment above). LGTM.
Follow-up to #191 (epic); builds on Phase 1 (#200). Adds the Phase 2 CLI for authoring a custom (bring-your-own) agent without hand-editing YAML.
What's new
bbox agents add NAME --image IMAGE --command CMD [flags]— appends a validated custom agent to the global config (~/.config/broodbox/config.yamlor--config), then runs the existingValidateCustomAgentand prints the result. Refuses to overwrite a built-in or an existing custom agent unless--force. Flags:--image,--command(repeatable),--description,--env,--env-required,--memory,--cpus,--tmp-size,--egress-profile,--allow-host,--mcp,--mcp-authz-profile,--force,--json.bbox agents init [NAME]— prints a commented starteragents:stanza to stdout, reusing the documentation fromwriter.go's default template (extracted into a sharedcustomAgentExampleBlockso the two can't drift).agents addandagents doctor --json— a re-checkable, paste-safe record: agent summary, env vars by name only with present/missing, MCP state, validator version, before/after config SHA-256 fingerprints (add), writer comment-handling note, and explicit non-effects. (Implements @HarperZ9's receipt suggestion.)Design notes
config.AgentFromOverrideandconfig.ValidateCustomAgentfrom Add declarative bring-your-own agent support (#191 Phase 1) #200. New code is CLI (cmd/bbox) + a config-file mutation ininternal/infra/config; the domain stays pure (only aCustomAgentValidatorVersionconstant added).agents addnever targets a workspace.broodbox.yaml.UpsertAgentdoes a comment-preserving YAML node round-trip (encoding the document node so leading comments survive). A comment-only file — like theconfig inittemplate — parses to an empty tree, so it is kept verbatim and the new stanza appended; documentation is never dropped. Verified: two sequentialadds into a freshconfig initkeep all 254 comment lines under a singleagents:key.Acceptance criteria — verified end to end
bbox agents add aider --image ghcr.io/acme/aider-bbox:latest --command aider --env OPENAI_API_KEY --mcp bbox agents doctor aider # [PASS]No manual YAML editing.
Tests
task fmtclean,task lint0 issues,task test(race) all 34 packages pass. New tests cover the flag→override mapping, add/doctor end-to-end + JSON receipt, built-in/duplicate refusal,initoutput, and the mutation's comment-preservation / append / force / non-mapping-root cases.🤖 Generated with Claude Code