7.2 KiB
nbd — Implementation Plan
Overview
nbd is a CLI tool for managing work tickets, primarily targeted at agent workflows. It stores tickets as JSON files in .nbd/tickets/ in the project root (found by traversing up from cwd, like git).
Crate location: vibed/nbd/
Language: Rust (edition 2021)
Key crates: clap, async-std, serde / serde_json, tempfile (tests)
Phase 1: Crate Scaffold
Set up the crate structure with no logic yet.
- Create
nbd/Cargo.tomlwith dependencies:clap(derive feature),async-std,serde,serde_json; dev-dependencies:tempfile - Add
[profile.release]optimizations (opt-level = "z",lto = true,strip = true,codegen-units = 1) - Create
nbd/src/main.rs—async-stdentry point, placeholderfn main() - Create
nbd/src/ticket.rs— empty module - Create
nbd/src/store.rs— empty module - Create
nbd/src/display.rs— empty module - Create
nbd/src/tests.rs— empty test module - Create
nbd/tests/integration.rs— empty integration test file - Verify:
cargo checkpasses
Phase 2: Data Model (ticket.rs)
Define the core types.
- Define
Statusenum:Todo,InProgress,Done(default:Todo)- Derive:
Serialize,Deserialize,Debug,Clone,PartialEq - Serialize to/from lowercase strings (
"todo","in_progress","done")
- Derive:
- Define
TicketTypeenum:Project,Feature,Task,Bug(default:Task)- Derive same traits
- Serialize to/from lowercase strings
- Define
Ticketstruct:id: String // 6-char hex, e.g. "a3f9c2" title: String body: String priority: u8 // 0..=10, default 5 status: Status dependencies: Vec<String> // Vec of ticket IDs ticket_type: TicketType // field name avoids keyword collision- Derive:
Serialize,Deserialize,Debug,Clone
- Derive:
- Implement
Ticket::new(id, title)constructor with all defaults - Implement priority validation (error if > 10)
- Implement ID generation: 3 random bytes → 6 hex chars (use
std::collections::hash_map::RandomStateor similar; no external crate needed for MVP) - Unit tests: serialization roundtrip, priority validation, ID format (6 hex chars, unique across N calls)
Phase 3: Storage (store.rs)
File I/O and directory traversal using async-std.
- Implement
find_nbd_root() -> Result<PathBuf>: walk from cwd upward until.nbd/is found; error with helpful message if not found - Implement
find_nbd_root_from(start: &Path) -> Result<PathBuf>: testable variant that accepts a starting path - Implement
tickets_dir(root: &Path) -> PathBuf: returnsroot/.nbd/tickets/ - Implement
ensure_tickets_dir(root: &Path) -> Result<()>: creates.nbd/tickets/if missing (used only bycreate) - Implement
ticket_path(root: &Path, id: &str) -> PathBuf: returns.nbd/tickets/{id}.json - Implement
write_ticket(root: &Path, ticket: &Ticket) -> Result<()>: serialize to JSON, write file - Implement
read_ticket(root: &Path, id: &str) -> Result<Ticket>: read file, deserialize; error if not found - Implement
list_tickets(root: &Path) -> Result<Vec<Ticket>>: read all*.jsonfrom tickets dir, deserialize all, sort by priority descending - Unit tests: roundtrip write/read with tempdir, list returns all tickets, traversal finds
.nbd/in grandparent dir
Phase 4: Display (display.rs)
Output formatting.
- Implement
print_ticket(ticket: &Ticket): full tabular displayID: a3f9c2 Title: Fix login bug Body: Users cannot log in with email addresses containing + Priority: 8 Status: in_progress Type: bug Dependencies: b7d41e, c9e823 - Implement
print_ticket_json(ticket: &Ticket): pretty-printed JSON to stdout - Implement
print_list(tickets: &[Ticket]): short tableID PRI TYPE STATUS TITLE a3f9c2 8 bug in_progress Fix login bug b7d41e 5 task todo Add rate limiting - Implement
print_list_json(tickets: &[Ticket]): JSON array to stdout - Unit tests: table output contains expected field values, JSON output is valid
Phase 5: CLI Commands (main.rs)
Wire up clap subcommands to storage and display.
- Define CLI structure with
clapderive:- Global flag:
--json(all commands) - Subcommand
create:--title(required),--body,--priority,--status,--type,--deps(comma-separated IDs) - Subcommand
read: positional<id> - Subcommand
list: no args - Subcommand
update: positional<id>, same optional flags ascreate
- Global flag:
- Implement
cmd_create: generate ID, validate deps exist, write ticket, print - Implement
cmd_read: find ticket by ID, print - Implement
cmd_list: list all tickets, print - Implement
cmd_update: read existing ticket, merge only provided flags, write, print - Integration tests (tempdir): create → read roundtrip, list shows created tickets, update merges correctly, traversal test (run from subdir), error on unknown ID
Phase 6: Documentation & Validation
- Write
nbd/README.md: what, how, run, test, license, Claude Code disclaimer - Write
nbd/docs/PLANNING.md: this plan + work log - Write
nbd/docs/ARCHITECTURE.md: module overview and interactions - Run validation in order:
cargo fmt,cargo check,cargo clippy,cargo test - Verify end-to-end:
cargo run -- create --title "Test ticket" --priority 7 --type bug cargo run -- list cargo run -- read <id> cargo run -- update <id> --status in_progress cargo run -- list --json
Post-MVP
The following features are planned but excluded from the MVP:
nbd init— explicit initialization commandnbd ready— list tickets with no blockersnbd archive— mark tickets as Closednbd updategit-diff style +/- output- Partial ID matching (
nbd read a3ffindinga3f9c2) - SQLite cache for performance
- Nix flake for depending on
nbdin other projects
Multiple File Format Support
nbd will support multiple ticket file formats selectable via --ftype=[json|md|toml|jsonb] on create and update.
Format detection is by file extension: .json, .md, .toml, .jsonb. The store layer detects format from the file extension when reading; no format metadata is stored separately.
Markdown format (.md): The file body is the ticket body field. Metadata is stored as frontmatter:
---delimiter → YAML frontmatter+++delimiter → TOML frontmatter
Example (YAML frontmatter):
---
id: a3f9c2
title: Fix login bug
priority: 8
status: in_progress
ticket_type: bug
dependencies: [b7d41e]
---
Long-form body text goes here. Supports full markdown.
TOML format (.toml): All fields in a flat TOML file; body is a multi-line string.
Binary JSON (.jsonb): Compact binary-encoded JSON (e.g. via CBOR or BSON; crate TBD at implementation time).
Conversion via nbd update --ftype: Reads the ticket in its current format, writes it in the new format, and deletes the old file. The ID and all field values are preserved.
Crates to evaluate: serde_yaml, toml, ciborium (CBOR) or bson.