feat: Tier 1 bundle - all functional-completeness gaps from TODO #28
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/tier-1-bundle"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Tier 1 bundle: every functional-completeness gap from
TODO.mdshipped as a single PR, per your direction. After this lands the CLI covers ~95% of day-to-day YouTrack workflows.10 verbs added + 1 extended (
yt list).New verbs
yt issue update <id>with--summary,--description,--state,--priority,--type,--assignee,--json. Top-level fields go throughPOST /api/issues/<id>; custom fields ride the YouTrack command language so we don't enumerate every$type.yt issue delete <id> [-y]with interactive confirmation.yt issue tag {add,remove} <id> <tag>.yt issue link <id> --type <type> <target>andyt issue unlink ....yt issue attachment {list,add,get,remove}. Multipart upload viareqwest::multipart; download follows the relativeurlfield and writes to<output>or./<name>. Sizes formatted human-friendly.yt user {me,list,show}.meis a single GET against/api/users/me;listtakes--query+--top;showlooks up by login.yt project {list,show,states}.states <SHORT>walks the project's custom-field bundle to surface valid State values, which preventsset-state Foofrom silently 400-ing.yt work list <id>(history with the internal id) andyt work delete <id> <work-id> [-y].yt work check --date YYYY-MM-DDextends the today-only check to any UTC date.Extended
yt list-q / --query <raw>passes a raw YouTrack search query and bypasses the structured filters. Clap'sconflicts_with_allenforces the exclusion at parse time.--top <N>and--skip <N>for explicit pagination.--allloops 100-at-a-time via a newfetch_issues_allhelper.Plumbing
Clientgrowsdelete_ignore,get_bytes, andpost_multipart_json(the latter takes a prebuiltreqwest::multipart::Form).User,WorkItemFull(WorkItemdoesn't carry the id; needed forwork delete),Attachment,BundleValue.delete_issue,update_issue,fetch_issues_page/fetch_issues_all,get_user_me/list_users/get_user,get_project/list_project_states,list_work_items_full/delete_work_item,list_attachments/upload_attachment/download_attachment/delete_attachment,apply_command_one.Cargo.toml:reqwestadds themultipart+streamfeatures.Out of scope (intentionally)
--dry-runis partially shipped (the updater) and will be generalized in Tier 2.Verification
cargo fmt --checkclean.cargo clippy --all-targets -- -D warningsclean.cargo test --all-targets: 182 tests pass (4 new). Wiremock tests cover the api-helper HTTP shapes; the table-formatting + size-humanization tests are pure functions. The command-language mutations (update/tag/link/unlink) reuse the already-testedapply_commandplumbing.Test plan
yt issue update ABC-1 --summary "new"writes the new summary;--state "In Progress"flips state.yt issue delete ABC-99prompts;-yskips.yt issue tag add ABC-1 wip, thenyt issue tag remove ABC-1 wip.yt issue link ABC-1 --type "relates" ABC-2; same withunlink.yt issue attachment add ABC-1 ./file.png, thenlist, thenget ABC-1 file.png.yt user me,yt user list --query jdoe,yt user show jdoe.yt project list,yt project states ABC(use the output as the set of validset-statevalues).yt work list ABC-1,yt work delete ABC-1 <work-id>.yt work check --date 2026-05-13.yt list -q "for:me #Unresolved"runs against the raw query; structured filters are not applied.yt list --all --json | jq lengthmatches the full backlog count.Sequence
Part 2 of 4 from today's batch. Part 1 (TODO additions) is PR #27. Tier 2 and Tier 3 PRs are next.
Ships every Tier 1 verb identified in the post-audit backlog as a single bundled PR per direction. After this lands, the CLI covers ~95% of day-to-day YouTrack workflows: every gap that was a "user noticed something is missing" is closed. ## New verbs - `yt issue update <id> [--summary] [--description] [--state] [--priority] [--type] [--assignee] [--json]`. Top-level fields (summary, description) go through `POST /api/issues/<id>`; custom fields use the YouTrack command language via `apply_command` so we don't have to enumerate every `$type`. - `yt issue delete <id> [-y]`. `DELETE /api/issues/<id>`. Interactive confirmation unless `-y`. - `yt issue tag {add,remove} <id> <tag>`. Wraps the command-language `tag <name>` / `remove tag <name>`. - `yt issue link <id> --type <type> <target>` and `yt issue unlink <id> --type <type> <target>`. Same shape; the unlink path prefixes the command with `remove`. - `yt issue attachment {list,add,get,remove}`. Multipart upload via `reqwest::multipart` (gated behind the new `multipart` + `stream` features on reqwest); download follows the relative `url` field YouTrack returns and writes to a configurable destination; remove uses `DELETE`. Sizes are formatted human-friendly (`512 B`, `2.00 KB`, `2.38 MB`). - `yt user me / list / show <login>`. `me` is one GET against `/api/users/me`; `list` takes `--query` (substring; YouTrack server-side) and `--top` (default 50); `show` looks up by login. - `yt project list / show <short> / states <short>`. `list` reuses the private `list_projects` helper that `issue create` already had; `states` walks the project's custom-field bundle to surface the valid State values - the same lookup that prevents `issue set-state Foo` from silently 400-ing. - `yt work list <id>` returns the full work-item history with the internal id (needed to delete). `yt work delete <id> <work-id> [-y]` removes one. - `yt work check --date YYYY-MM-DD` extends the existing today-only check to any UTC date. ## Extended `yt list` - `-q / --query <raw>` passes a raw YouTrack search query through and bypasses the structured `--sprint / --assignee / --type` filters; clap's `conflicts_with_all` enforces the exclusion. - `--top <N>` and `--skip <N>` for explicit pagination. Default behavior (neither) preserves the existing single-page request shape. - `--all` loops 100-at-a-time via the new `fetch_issues_all` helper until a short page comes back. ## Plumbing - `Client` grows `delete_ignore`, `get_bytes`, and `post_multipart_json`. `delete_ignore` mirrors `post_ignore` (errors on non-2xx, drains body); `get_bytes` returns raw bytes for attachment downloads (sends `Accept: */*` instead of `application/json`); `post_multipart_json` accepts a prebuilt `reqwest::multipart::Form`. - `yt::models` adds `User`, `WorkItemFull` (carries the id field that `WorkItem` from inspect doesn't), `Attachment`, `BundleValue` (one entry in a project custom-field bundle). - `yt::api` adds: `delete_issue`, `update_issue`, `fetch_issues_page` + `fetch_issues_all`, `get_user_me / list_users / get_user`, `get_project / list_project_states`, `list_work_items_full / delete_work_item`, `list_attachments / upload_attachment / download_attachment / delete_attachment`, `apply_command_one` (single-issue shorthand for `apply_command`). - `Cargo.toml`: reqwest grows the `multipart` + `stream` features (needed for attachment upload). ## Verification - `cargo fmt --check` clean. - `cargo clippy --all-targets -- -D warnings` clean. - `cargo test --all-targets`: 182 tests pass (4 new). The api-helper tests rely on wiremock for HTTP shape; the table-formatting + size-humanization tests are pure-function. Mutation paths through the commands API (`update`, `tag`, `link/unlink`) reuse existing `apply_command` plumbing which is already covered. ## Test plan - [ ] `yt issue update ABC-1 --summary "new title"` writes the new summary; `--state "In Progress"` flips the state via the command API. - [ ] `yt issue delete ABC-99` prompts; `-y` skips the prompt. - [ ] `yt issue tag add ABC-1 wip`, then `yt issue tag remove ABC-1 wip`. - [ ] `yt issue link ABC-1 --type "relates" ABC-2`; `yt issue unlink ABC-1 --type "relates" ABC-2`. - [ ] `yt issue attachment add ABC-1 ./screenshot.png`, then `yt issue attachment list ABC-1`, then `yt issue attachment get ABC-1 screenshot.png`. - [ ] `yt user me`, `yt user list --query jdoe`, `yt user show jdoe`. - [ ] `yt project list`, `yt project states ABC` (catches mis-named states before `set-state` does). - [ ] `yt work list ABC-1`, `yt work delete ABC-1 <work-id>`. - [ ] `yt work check --date 2026-05-13`. - [ ] `yt list --query "for:me #Unresolved"` returns whatever YouTrack matches against that raw query, ignoring the structured filters. - [ ] `yt list --all --json | jq length` returns the full backlog (loops pages of 100 until short).