--- name: tuicr description: "Read comments and human-owned review marks in tuicr code-review sessions, open review windows in tmux, and run a live feedback loop. Includes a daemon that wakes an agent when comments are pending or a review closes, plus contracts for answering every comment before acting and interpreting deterministic approval without setting marks. Use when opening a review for a human, reading or replying to review comments, checking review completion, starting or stopping a live watch, or when woken by a `tuicr wake` or `tuicr close` prompt." --- # tuicr review workflow `tuicr` is the human's review surface. The TUI is where they read the diff and write comments; the `tuicr review` CLI is how an agent discovers sessions, reads comments, and posts replies. A watch daemon closes the loop by waking the agent when a comment is pending, so the human never has to copy anything out of the TUI. Scripts live in this skill's `lib/`. Resolve the dir as `~/.agents/skills/tuicr/lib`. They need `tuicr`, `git`, `tmux` and `python3` (3.8+, stdlib only), and run on Linux, macOS and WSL — `tmux` has no native Windows port, so on Windows use WSL. See [`lib/README.md`](lib/README.md) for each script's contract and exit codes. ## Non-negotiables 1. **Close the loop in tuicr.** Whenever you act on a comment, post a threaded `--type reply` *before* you report the work done, anchored to that comment's **exact** `path`, `start_line`, `end_line` and `side` — threading is positional, so an approximate anchor does not thread. A chat summary is not a substitute — the human triages in tuicr. See [Replying](#replying-to-existing-comments). 2. **Open a review without being asked.** Whenever you produce changes a human needs to review, proactively open a tuicr window on them and say it is ready. The only exception is an explicit "no review window for this one". 3. **Refresh branch refs first.** Before opening any branch-based review, fetch and resolve against fresh remote refs via [`lib/refresh_review_refs.py`](#opening-a-review). Never review a stale local base. Fixed commit-SHA ranges need no refresh. 4. **The watch loop is local-only.** A woken agent may read comments, make **unstaged** edits, and post **local draft** replies. It must never stage, commit, push, or sync to a remote forge. The human's review gate depends on this. Committing happens **outside** the loop, after the human has approved and the review is over — never in response to a wake. ## Pick the workflow **A — Human-led review of your changes.** They inspect the patch and write comments. You open or find the session and read their comments; you do not review your own patch or write comments in their voice. **B — Agent review of a patch** (any "code review this", "review with model X and Y" request). Spawn a window for the target revset and post every finding as an inline comment — anchored in tuicr, not printed to chat. Run reviewer sub-agents in parallel when several models are named, verify each finding against the real source before posting (models fabricate line numbers and symbols), and set `--username` to the model id that produced it. Ask only when the *session* is ambiguous (several plausible repos, or an active session on a different revset). If the *workflow* is ambiguous, ask which one. ## Opening a review Resolve the revset first. If it contains branch refs, refresh them: ```bash eval "$(~/.agents/skills/tuicr/lib/refresh_review_refs.py \ --repo [--base ] [--head ])" # use ${REVIEW_REVSET} ``` The base defaults to the remote's own default branch (`origin/HEAD`), so no branch name is hardcoded. The helper fetches, resolves names to fresh `origin/` refs, fast-forwards a **clean** behind `HEAD`, and refuses a dirty or diverged one. Pass `--no-ff` to review the local `HEAD` as-is instead of moving it. If a commit in the range is already merged into the base, use `^..` so the diff is non-empty. Then check for an existing session and open a window if there is none: ```bash tuicr review list --repo # look for "active": true ~/.agents/skills/tuicr/lib/tuicr_up.py # working-tree review window ``` `tuicr_up.py` refreshes refs, opens a tmux window, and starts the review watch daemon for the current Copilot CLI session; if tuicr is already reviewing **that same checkout**, it reuses the active session and still starts the watcher. For full control, open directly: `tmux new-window -d -n review -c "tuicr -w"` (uncommitted changes) or `"tuicr -r ''"` (a commit range). Requires `$TMUX`; if there is no multiplexer, tell the human you are waiting for them to start `tuicr`, then attach via `tuicr review list`. The CLI works outside tmux, so never require a multiplexer just to read an existing session. **Reuse the persisted session for that checkout and review target.** Review marks are human-owned state: unchanged files retain their marks, while tuicr automatically clears marks for changed files and adds new files unreviewed. Never move, replace, delete, or edit a persisted session file to refresh its file list or force a new review. Only do so when the human explicitly requests session-state maintenance. ## Reading comments There is no push stream from the TUI. Read on demand. **Use `review.py`, not raw `tuicr`.** It is the validated front door for the four operations you perform, and it removes the mistakes that are easy to make from a shell and hard to diagnose: ```bash ~/.agents/skills/tuicr/lib/review.py --repo comments [--unanswered] ``` - `--repo` is a **subcommand** flag on the real CLI, so `tuicr review --repo ` fails with `unexpected argument '--repo' found` and a usage block that never names the actual problem — the missing subcommand. `review.py` requires the subcommand up front and says so. - `--session` defaults to the repo's active session, and `--unanswered` applies the pending-comment rule (non-`reply`, no later reply at the same anchor) for you rather than leaving you to reproduce it. The raw form below is the reference for what the wrapper sends, and is still what you need for anything outside those three operations (`--type reply`, for instance): ```bash tuicr review comments --repo --session ``` Each comment carries `id`, `location`, `path`, `start_line`, `end_line`, `side`, `comment_type`, `lifecycle_state`, `created_at`, `content`. > tuicr has **no parent/thread field** — threading is positional. A reply is > associated with its parent by sharing the **same anchor** (`path`, > `start_line`, `end_line`, `side`) and a later `created_at`. This is why reply > anchoring must match exactly. Treat types as: `issue` blocking, `suggestion` consider-or-justify, `note` answer, `praise` no action. If the result is empty, confirm the session choice. Re-read before claiming completion — the human may have kept commenting while you worked. ## Wake contract When a `tuicr wake : N pending comment(s) in review session …` prompt arrives, the daemon has confirmed that comments are awaiting a response. The prompt is intentionally minimal — **this section is the behaviour**, so it is identical no matter which agent is woken. Before replying to anything, refresh your understanding of the file at the current checkout state. Re-read the latest file contents for the anchored path before deciding how to answer, so your reasoning reflects the newest version of the code even if the comment was written against an older snapshot. Never derive reply anchors from the current file text; use the comment id to copy the original anchor verbatim. `N` is the **total** outstanding count, not just what changed since the last wake, so it may exceed the number the human wrote most recently — earlier comments still awaiting a reply are included. Answer all of them. 1. **Read** `~/.agents/skills/tuicr/lib/review.py --repo comments --unanswered`. That flag applies the pending rule for you: the non-`reply` comments with no later reply at the same anchor. Note each comment's `id` — it is what you reply to. 2. **Classify, then reply — before any work.** Every pending comment gets a threaded reply at the original's **exact** anchor, which `review.py reply --to ` copies for you (see [Replying](#replying-to-existing-comments)); that reply is what closes it, and a comment left unanswered — including one whose reply missed the anchor — is re-delivered. What the reply *says* depends on what the comment **is**. `comment_type` is only a hint — it is user configuration (one human's `note` is another's `question`), so read the intent. - **Asks for a change** — a problem, a suggestion, a nit. Reply with one lowercase verdict and a brief justification: - `agree — ; queued: ` - `disagree — ; no work started` - `needs clarification — ; no work started` A bare verdict is not enough, and an `agree` must accurately describe the work you are about to start. - **Asks a question** — the human wants to understand something. **Just answer it, in plain prose.** No verdict word, no template, no `queued:`. Answer directly, then stop: a question is not a change request and starts no work. - **Just remarks** — praise, an FYI, "this is intentional". One short acknowledgement. Do not manufacture a verdict for it. A comment can be two of these at once ("why 5.0? make it configurable"): answer the question plainly *and* give a verdict on the ask — never force the answer into verdict shape. If answering a question exposes a real defect, say so in the answer and queue it as you would an `agree`. 3. **Then act, through your own ownership model.** The contract fixes *when* and *what you promise*, never *who edits*. If you are a solo coding agent, make the edits yourself. If you orchestrate sub-agents, route the brief to the owning agent and do not edit code yourself. If a workflow skill defines ownership (worktrees, test/impl partitioning, a todo ledger), that skill decides the routing — this contract does not override it. 4. **`disagree`, `needs clarification`, an answered question, and an acknowledged remark all start no work.** Wait for the human's follow-up, which arrives as a new comment and a new wake. 5. **Be idempotent.** A wake may be redelivered. If a comment already has your reply or queued work, do not duplicate it. 6. **Report the outcome** in the same thread before calling the comment done, unless your first reply already states it (an answered question usually needs nothing further). 7. **Stop at the local boundary.** Unstaged edits and local draft replies only. The human reviews the diff and the threads afterwards. > **No status updates in tuicr.** The only comments you post are `--type reply` > responses anchored to a human comment. A non-`reply` comment adds review > noise *and* wakes you again through the watcher — a self-wake > loop. Report progress in your own window, never in the review. ## Close contract When a `tuicr close : ... verdict= ... report=` prompt arrives, read the JSON report at `report` and act on its deterministic result. - `approved` means tuicr exited cleanly, `HEAD` did not move, the current change set is non-empty, every changed file is present and human-marked reviewed at its current content, and every comment has an exact-anchor reply. - `incomplete` means the review closed cleanly but one or more checks failed. Report the exact `reasons`, unreviewed/absent/stale files, and unanswered comments. - `aborted` means the window was killed, crashed, or otherwise did not record a clean exit. It is never approval. Review marks are human-owned. Read and verify them; never create, change, or simulate them. Preserve the persisted session so unchanged marks survive future review cycles; let tuicr invalidate marks when content changes. Approval ends this skill's responsibility: do not stage, commit, push, or infer what approval enables. The invoking custom agent or human-owned workflow decides the next action. ## Live watch Starts a non-LLM daemon that polls the session's persisted comments and wakes your CLI session when one is pending. Full detail: [`reference/watch.md`](reference/watch.md). ```bash ~/.agents/skills/tuicr/lib/watch_up.py \ --repo --session \ --cli-session "" \ --name "