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.

90 lines
2.9 KiB
Markdown

+++
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
```sh
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)
```rust
/// 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`
```rust
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`):**
```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.rs``Commands::Graph` variant, `cmd_graph`, dispatch in `dispatch()`
## Depends on
- `9c9ebe` — graph computation module
- `e14172` — ASCII rendering functions