You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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.toml with 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.rsasync-std entry point, placeholder fn 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 check passes

Phase 2: Data Model (ticket.rs)

Define the core types.

  • Define Status enum: Todo, InProgress, Done (default: Todo)
    • Derive: Serialize, Deserialize, Debug, Clone, PartialEq
    • Serialize to/from lowercase strings ("todo", "in_progress", "done")
  • Define TicketType enum: Project, Feature, Task, Bug (default: Task)
    • Derive same traits
    • Serialize to/from lowercase strings
  • Define Ticket struct:
    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
  • 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::RandomState or 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: returns root/.nbd/tickets/
  • Implement ensure_tickets_dir(root: &Path) -> Result<()>: creates .nbd/tickets/ if missing (used only by create)
  • 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 *.json from 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 display
    ID:           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 table
    ID       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 clap derive:
    • 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 as create
  • 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 command
  • nbd ready — list tickets with no blockers
  • nbd archive — mark tickets as Closed
  • nbd update git-diff style +/- output
  • Partial ID matching (nbd read a3f finding a3f9c2)
  • SQLite cache for performance
  • Nix flake for depending on nbd in 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.