CLI: Categories and Boards Commands — Design#
Date: 2026-02-18
Status: Approved
Context: Extends the existing @atbb/cli bootstrap tool (ATB-28) to support creating categories and boards — both as part of the init wizard and as standalone management commands.
Problem#
The current atbb init wizard bootstraps the forum record, roles, and owner, but defers category/board creation to "the admin panel" (which doesn't exist yet). Forum operators need a way to set up the initial content structure from the CLI.
Design#
New Commands#
atbb category add # Create a new category on the Forum DID's PDS and DB
atbb board add # Create a new board within a category
Both follow the same pattern as existing CLI steps: write to PDS first (using Forum DID credentials), then insert into the database. Idempotent: skip if a record with the same name already exists.
Extended init — Step 4: Seed Initial Structure#
After the existing Step 3 (assign owner), init prompts:
Seed initial categories and boards? (Y/n)
If yes (the default), it prompts for:
- Category name (default: "General")
- Category description (optional)
- Board name (default: "General Discussion")
- Board description (optional)
Uses the new createCategory / createBoard step functions. Skips if records already exist.
File Structure#
packages/cli/src/
lib/steps/
create-category.ts ← new
create-board.ts ← new
commands/
category.ts ← new citty subcommand group
board.ts ← new citty subcommand group
index.ts ← updated: register category + board
commands/init.ts ← updated: Step 4 = seed initial structure
atbb category add#
Flags (all optional — prompts if absent):
--name— display name (max 100 graphemes per lexicon)--description— optional description--slug— URL-friendly identifier; auto-derived from name if omitted--sort-order— numeric sort position (integer ≥ 0)
Flow:
- Preflight checks + DB connection + PDS auth
- Write
space.atbb.forum.categoryrecord to Forum DID's PDS (TID key) - Insert into
categoriestable withforumIdreference (looked up from DB) - Print created URI
Slug auto-derivation:
name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '')
atbb board add#
Flags (all optional — prompts if absent):
--category-uri— AT URI of parent category (at://did/space.atbb.forum.category/rkey)--name— display name--description— optional--slug— auto-derived if omitted--sort-order— optional
If --category-uri is not provided, fetches all categories from DB and presents an interactive select list via @inquirer/prompts.
Flow:
- Preflight checks + DB connection + PDS auth
- Resolve category: look up
categoryUriin DB to getcategoryId; return error if not found - Write
space.atbb.forum.boardrecord to PDS withcategoryref (strongRef:{ uri, cid }) - Insert into
boardstable withcategoryId+categoryUri - Print created URI
Step Modules#
createCategory(db, agent, forumDid, input)#
Input: { name, description?, slug?, sortOrder? }
Returns: { created, skipped, uri?, cid? }
Idempotent: checks categories table for existing row with (did, name) before writing.
createBoard(db, agent, forumDid, input)#
Input: { name, description?, slug?, sortOrder?, categoryUri, categoryId, categoryCid }
Returns: { created, skipped, uri?, cid? }
Idempotent: checks boards table for existing row with (did, name) before writing.
Error Handling#
Follows existing patterns:
isProgrammingError(error)— re-throw TypeErrors/ReferenceErrors- Network errors (PDS unreachable) — exit with friendly message
- Not found (category missing from DB when adding board) — exit with actionable message
- All errors in standalone commands: log +
process.exit(1)(same as init)
Testing#
src/__tests__/create-category.test.ts— create/skip/PDS error/DB errorsrc/__tests__/create-board.test.ts— create/skip/missing category/PDS error/DB error- Commands (
category.ts,board.ts) tested via CLI integration tests following init test patterns
Not In Scope#
category list,board list— can be added in a follow-upcategory edit,board edit— post-MVP- Deleting categories/boards — post-MVP