chore: plan hltb CLI (HowLongToBeat search tool)
Milestone vibed-f05l and its seven subtasks laying out a new Rust CLI binary that queries howlongtobeat.com and prints completion times. Covers project init, request/response types, CLI arg parsing, the HTTP search call, output formatting, and a validation/smoke-test pass. No implementation yet — planning only. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>main
parent
2a2c607e70
commit
e42c3c731c
@ -0,0 +1,24 @@
|
||||
---
|
||||
# vibed-0t6e
|
||||
title: Add API request types
|
||||
status: todo
|
||||
type: task
|
||||
created_at: 2026-04-11T22:34:39Z
|
||||
updated_at: 2026-04-11T22:34:39Z
|
||||
parent: vibed-f05l
|
||||
blocked_by:
|
||||
- vibed-ca4q
|
||||
---
|
||||
|
||||
Implement the full `SearchRequest` struct tree for the HLTB POST body.
|
||||
|
||||
## Todo
|
||||
- [ ] `SearchRequest` with searchType, searchTerms, searchPage, size, searchOptions, useCache
|
||||
- [ ] `SearchOptions` with games, users, lists, filter, sort, randomizer
|
||||
- [ ] `GamesOptions` with userId, platform, sortCategory, rangeCategory, rangeTime, gameplay, rangeYear, modifier
|
||||
- [ ] `RangeTime { min: Option<u32>, max: Option<u32> }`
|
||||
- [ ] `Gameplay` with perspective, flow, genre, subGenre
|
||||
- [ ] `RangeYear { min: &str, max: &str }`
|
||||
- [ ] `UsersOptions` and `ListsOptions` with sortCategory
|
||||
- [ ] All structs derive `serde::Serialize` with correct `#[serde(rename = ...)]` annotations
|
||||
- [ ] `cargo check` passes
|
||||
@ -0,0 +1,21 @@
|
||||
---
|
||||
# vibed-54u4
|
||||
title: Add CLI argument parsing
|
||||
status: todo
|
||||
type: task
|
||||
created_at: 2026-04-11T22:34:31Z
|
||||
updated_at: 2026-04-11T22:34:31Z
|
||||
parent: vibed-f05l
|
||||
blocked_by:
|
||||
- vibed-ca4q
|
||||
---
|
||||
|
||||
Implement the `Args` struct using `clap` derive API.
|
||||
|
||||
## Todo
|
||||
- [ ] Add `use clap::Parser;` and derive `Args` struct
|
||||
- [ ] Variadic positional `title: Vec<String>` with `num_args = 1..`
|
||||
- [ ] `--json` bool flag (output raw JSON)
|
||||
- [ ] `--all` bool flag (show all results, not just top match)
|
||||
- [ ] Call `Args::parse()` in `main` and join title words for display
|
||||
- [ ] `cargo check` passes
|
||||
@ -0,0 +1,25 @@
|
||||
---
|
||||
# vibed-90il
|
||||
title: Validation & smoke test
|
||||
status: todo
|
||||
type: task
|
||||
created_at: 2026-04-11T22:35:32Z
|
||||
updated_at: 2026-04-11T22:35:32Z
|
||||
parent: vibed-f05l
|
||||
blocked_by:
|
||||
- vibed-iq16
|
||||
- vibed-mlgv
|
||||
---
|
||||
|
||||
Run all validation commands and live smoke tests against the real API.
|
||||
|
||||
## Todo
|
||||
- [ ] `cargo fmt --check` passes
|
||||
- [ ] `cargo check` passes
|
||||
- [ ] `cargo clippy -- -D warnings` passes
|
||||
- [ ] `cargo build --release` succeeds
|
||||
- [ ] `./target/release/hltb halo` returns results
|
||||
- [ ] `./target/release/hltb the witcher 3` works without quotes
|
||||
- [ ] `./target/release/hltb --all zelda` shows numbered list
|
||||
- [ ] `./target/release/hltb --json hollow knight` outputs valid JSON
|
||||
- [ ] `./target/release/hltb xyzzy_no_game_zzzq` prints "No results found."
|
||||
@ -0,0 +1,16 @@
|
||||
---
|
||||
# vibed-ca4q
|
||||
title: Initialize Rust project
|
||||
status: todo
|
||||
type: task
|
||||
created_at: 2026-04-11T22:34:25Z
|
||||
updated_at: 2026-04-11T22:34:25Z
|
||||
parent: vibed-f05l
|
||||
---
|
||||
|
||||
Create `Cargo.toml` and `src/main.rs` skeleton for the `hltb` binary.
|
||||
|
||||
## Todo
|
||||
- [ ] Write `Cargo.toml` with package metadata, [[bin]], dependencies (clap, reqwest, serde, serde_json, tokio), and release profile
|
||||
- [ ] Write minimal `src/main.rs` with a stub `main()`
|
||||
- [ ] Run `cargo check` and confirm it passes
|
||||
@ -0,0 +1,10 @@
|
||||
---
|
||||
# vibed-f05l
|
||||
title: hltb CLI — HowLongToBeat search tool
|
||||
status: todo
|
||||
type: milestone
|
||||
created_at: 2026-04-11T22:33:37Z
|
||||
updated_at: 2026-04-11T22:33:37Z
|
||||
---
|
||||
|
||||
A Rust CLI binary that searches HowLongToBeat.com and prints completion times (Main Story / Main + Extras / Completionist) for any game. Single binary named `hltb`, uses reqwest + clap + serde + tokio.
|
||||
@ -0,0 +1,22 @@
|
||||
---
|
||||
# vibed-iq16
|
||||
title: Implement HTTP search function
|
||||
status: todo
|
||||
type: task
|
||||
created_at: 2026-04-11T22:35:13Z
|
||||
updated_at: 2026-04-11T22:35:13Z
|
||||
parent: vibed-f05l
|
||||
blocked_by:
|
||||
- vibed-0t6e
|
||||
- vibed-mn9a
|
||||
---
|
||||
|
||||
Implement `async fn search(query: &str) -> Result<SearchResponse, AppError>` using reqwest.
|
||||
|
||||
## Todo
|
||||
- [ ] Define `AppError { Network(reqwest::Error), Parse(serde_json::Error) }` with Display + From impls
|
||||
- [ ] Implement `search()`: split query on whitespace for searchTerms, build full SearchRequest body
|
||||
- [ ] POST to `https://howlongtobeat.com/api/search` with headers: User-Agent (Firefox), Referer, Content-Type
|
||||
- [ ] Use `.json(&body)` for request and `.json::<SearchResponse>()` for response
|
||||
- [ ] Wire into `main`: call search, match Ok/Err, print error to stderr and exit(1) on failure
|
||||
- [ ] `cargo check` passes
|
||||
@ -0,0 +1,24 @@
|
||||
---
|
||||
# vibed-mlgv
|
||||
title: Implement display formatting
|
||||
status: todo
|
||||
type: task
|
||||
created_at: 2026-04-11T22:35:25Z
|
||||
updated_at: 2026-04-11T22:35:25Z
|
||||
parent: vibed-f05l
|
||||
blocked_by:
|
||||
- vibed-mn9a
|
||||
- vibed-54u4
|
||||
---
|
||||
|
||||
Implement output formatting functions and the main display dispatcher.
|
||||
|
||||
## Todo
|
||||
- [ ] `fn format_duration(seconds: u32) -> String`: return "N/A" for 0, else "Xh YYm"
|
||||
- [ ] `fn print_game(game: &GameResult, prefix: Option<&str>)`: print name then 3 time lines with fixed-width labels
|
||||
- [ ] `fn display(resp: SearchResponse, args: &Args)`: check for zero results, dispatch to --json / --all / default paths
|
||||
- [ ] Default path: show only `data[0]`
|
||||
- [ ] `--all` path: numbered list of all results
|
||||
- [ ] `--json` path: `serde_json::to_string_pretty` of `data[0]` (or full vec with --all)
|
||||
- [ ] Zero results: print "No results found." and return
|
||||
- [ ] `cargo check` passes
|
||||
@ -0,0 +1,19 @@
|
||||
---
|
||||
# vibed-mn9a
|
||||
title: Add API response types
|
||||
status: todo
|
||||
type: task
|
||||
created_at: 2026-04-11T22:35:02Z
|
||||
updated_at: 2026-04-11T22:35:02Z
|
||||
parent: vibed-f05l
|
||||
blocked_by:
|
||||
- vibed-ca4q
|
||||
---
|
||||
|
||||
Implement `SearchResponse` and `GameResult` deserialization types.
|
||||
|
||||
## Todo
|
||||
- [ ] `SearchResponse { count: u32, data: Vec<GameResult> }` with `Deserialize`
|
||||
- [ ] `GameResult` with game_id, game_name, comp_main, comp_plus, comp_100, profile_steam, similarity — derive both `Deserialize` and `Serialize` (needed for --json output)
|
||||
- [ ] Note: comp_main/plus/100 are in **seconds**; 0 means N/A
|
||||
- [ ] `cargo check` passes
|
||||
Loading…
Reference in New Issue