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.

2.9 KiB

+++ title = "Add 'nbd graph' CLI subcommand" priority = 5 status = "done" ticket_type = "feature" dependencies = ["9c9ebe", "e14172"] +++ Wire up the graph subcommand in src/main.rs to expose the ASCII dependency graph and JSON output.

Motivation

With src/graph.rs and the rendering functions in display.rs implemented, this ticket connects them to the CLI so users (and agents) can invoke nbd graph.

CLI interface

nbd graph                   # full dependency forest (all tickets)
nbd graph <id>              # subtree rooted at the given ticket ID (or unique prefix)
nbd graph --json            # machine-readable adjacency list (all tickets)
nbd graph <id> --json       # machine-readable subtree
nbd graph --filter type=bug # forest of only bug tickets and their deps

Clap definition (add to Commands enum in main.rs)

/// Draw an ASCII dependency graph of all tickets, or a subtree rooted at a
/// specific ticket.
///
/// Without an ID, renders every ticket as a dependency forest (roots first,
/// dependencies indented below). With an ID, renders only the subtree reachable
/// from that ticket via its dependencies.
Graph {
    /// Optional ticket ID or unique prefix to use as the graph root.
    ///
    /// When omitted, the full dependency forest is rendered.
    id: Option<String>,

    /// Filter tickets by field before building the graph: repeatable `key=value` pairs.
    ///
    /// Applied to the full ticket list before graph construction.
    /// Keys: `status`, `type`, `priority`, `title`. Values support globs.
    #[arg(long = "filter", value_name = "KEY=VALUE")]
    filter: Vec<String>,
}

Handler: cmd_graph

async fn cmd_graph(id: Option<String>, filter_args: Vec<String>, json: bool) -> store::Result<()>

Steps:

  1. find_nbd_root() and list_tickets(&root).await?.
  2. Apply parse_filters(&filter_args)? to narrow the ticket list.
  3. let graph = TicketGraph::build(&tickets);
  4. If id is Some: a. Resolve id via resolve_id(&root, &id).await?. b. If json: print graph.to_subtree_json_value(&id) (or similar — see graph.rs ticket for the JSON shape). c. Else: display::print_subtree(&graph, &id).
  5. If id is None: a. If json: print graph.to_json_value(). b. Else: display::print_graph(&graph).

JSON output shapes

Full graph (nbd graph --json):

{
  "nodes": [
    {"id": "a3f9c2", "title": "Fix login bug", "status": "todo", "priority": 8, "dependencies": ["b7d41e"]},
    ...
  ],
  "edges": [
    {"from": "a3f9c2", "to": "b7d41e"},
    ...
  ]
}

Subtree (nbd graph <id> --json): Same shape as full graph but only including nodes and edges reachable from <id>.

Files touched

  • src/main.rsCommands::Graph variant, cmd_graph, dispatch in dispatch()

Depends on

  • 9c9ebe — graph computation module
  • e14172 — ASCII rendering functions