Migrate async runtime from async-std to tokio (required by libsql),
then add a mtime-based libsql cache at .nbd/cache.db that accelerates
nbd list, nbd ready, and nbd next by avoiding redundant file reads.
- Cargo.toml: replace async-std with tokio + libsql = "0.6"
- src/store.rs: async_std → tokio fs API; add open_cache() and
list_tickets_cached() with fallback to list_tickets on error
- src/main.rs: tokio::main, tokio::fs::remove_file; wire cmd_list,
cmd_ready, cmd_next to list_tickets_cached
- src/tests.rs: async_std::test → tokio::test, async_std::fs → tokio::fs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Tickets can now be stored in four formats, selected with --ftype:
json (.json) — pretty-printed JSON, default, unchanged
md (.md) — Markdown body with TOML frontmatter
toml (.toml) — full TOML
jsonb (.jsonb) — CBOR binary via ciborium
Changes:
- store.rs: FileFormat enum, detect_format(), find_ticket_path(),
per-format serialize/deserialize helpers; read_ticket/list_tickets/
resolve_id/migrate_tickets all scan all known extensions
- main.rs: --ftype on create (default "json") and update (optional,
converts format and removes old file); archive/update preserve
existing format when --ftype is absent
- tests.rs: update write_ticket/ticket_path call sites; add TOML,
Markdown, and CBOR roundtrip unit tests
- integration.rs: 8 new format tests covering create, list, update
conversion, format preservation, body roundtrip, unknown-ftype error
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add repeatable --filter KEY=VALUE option to nbd list, nbd ready, and
nbd migrate. Filters are parsed into a TicketFilter and applied in each
command handler. Different keys are ANDed; same key with multiple values
is ORed; values support glob wildcards.
- store: add skipped field to MigrateReport; migrate_tickets accepts
a &TicketFilter and skips non-matching tickets
- display: format_migrate_report shows optional Skipped line;
format_migrate_report_json includes skipped key
- filter: suppress dead_code on is_empty/has_status_filter (public API
reserved for future done-exclusion feature)
- tests: update MigrateReport literals and migrate_tickets call sites;
add unit tests for skipped formatting; add 14 integration tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add `resolve_id` to `store.rs` that resolves a full ticket ID from an
exact match or a unique prefix (like git short-SHA resolution). Use it
in `cmd_read`, `cmd_update`, and `validate_deps` so all three accept
short prefixes. Ambiguous prefixes produce an error listing all matches.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds `nbd migrate` to bring ticket files on disk into conformance with
the current serde schema. Re-serialises every *.json file through the
current Ticket model — removing stale fields (e.g. old \"id\" key),
adding new fields with their defaults, and normalising formatting.
- store: MigrateReport struct and migrate_tickets() function
- display: format/print_migrate_report and _json variants
- main: Migrate command with --dry-run flag and cmd_migrate handler
- 6 unit tests (rewrites old format, already-current, dry-run, invalid JSON, empty store, no tickets dir)
- 4 integration tests (rewrite, dry-run, parse error tolerance, --json output)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The ticket id is now stored only in the filename stem (.nbd/tickets/{id}.json).
`id` is annotated with `#[serde(skip)]` so it is never written to disk,
eliminating the consistency hazard of id-in-body vs. filename disagreement.
- ticket.rs: add `#[serde(skip)]` to `Ticket::id`
- store.rs: `read_ticket` and `list_tickets` inject id from filename stem
- display.rs: `ticket_to_json_value` re-inserts id for CLI `--json` output
- tests.rs: new unit tests for omission, injection, and old-format compat
- integration.rs: assert written files lack "id"; assert read --json has id
Backwards-compatible: old files with "id" in JSON body still parse correctly
(serde ignores the unknown field), so existing stores work without migration.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Wire up clap subcommands to storage and display layers:
- Cli struct with global --json flag and Create/Read/List/Update subcommands
- cmd_create: generates ID, validates priority and deps, writes and prints ticket
- cmd_read: looks up ticket by ID and prints it
- cmd_list: lists all tickets sorted by priority
- cmd_update: reads existing ticket, merges only provided flags, writes and prints
- parse_status, parse_ticket_type, parse_deps, validate_deps helpers
- 8 integration tests using process::Command against a tempdir
- Fix clippy: map_or(false, …) → is_some_and(…) in store.rs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add async-std file I/O and directory traversal in store.rs:
- find_nbd_root / find_nbd_root_from: walk up from cwd to locate .nbd/
- tickets_dir, ticket_path: pure path helpers
- ensure_tickets_dir: create .nbd/tickets/ on first use
- write_ticket / read_ticket: JSON serialisation round-trip
- list_tickets: read all *.json files, sort by priority descending
Add 8 unit tests covering write/read round-trip, missing-ticket
error, priority-sorted list, empty directory, grandparent traversal,
traversal failure, and path helpers.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>