jot-gh
A GitHub-only port of the jot CLI loop: capture brain-dumps into well-formed issues, decide what to work on next, and keep the backlog tidy — all through gh calls. No tracker abstraction (GitHub only), no licensing, no persistent SQLite. Auth is whatever gh auth status already provides.
When to invoke
Explicit (always invoke):
/jot-gh <command> — slash form
jot: <command> or jot-gh: <command> — prefix form
Auto-invoke (use judgment, prefer explicit when unsure):
- "capture this: ..." / "capture: ..." / "log this: ..."
- "what should I work on (next)?" / "what's next?"
- "what's on my plate today?" / "what am I doing today?"
- "tidy the backlog" / "find duplicate issues" / "find stale issues"
- "(re)assign all bugs to @"
- "make all issues P0/P1/P2/P3"
- "what does the backlog say about ?"
Do NOT invoke for:
- General GitHub questions unrelated to issue triage ("what's the API for X?", "how do I fork?")
- Pull request review or code review (that belongs to other skills)
- Anything that does not touch the issue lifecycle (issues, labels, assignees, comments)
Prerequisites
Before any command, verify gh is authed:
gh auth status
If the exit code is non-zero, stop and tell the user:
Run gh auth login first. jot-gh uses your gh credentials directly and does not store its own auth.
Also ensure jq is available for parsing gh --json output (command -v jq — error clearly if missing).
Repo resolution
Most commands operate on one repo at a time. To pick:
- If the user passed
--repo OWNER/REPO, use that.
- Otherwise, run
gh repo view --json nameWithOwner -q .nameWithOwner from the current directory — if it returns a repo, use it.
- Otherwise, ask the user which repo via
AskUserQuestion. Offer the 3 most-recent repos from gh repo list --json nameWithOwner --limit 10 -q '.[].nameWithOwner' as options.
Multi-repo flows (next by default, ask if no repo context): use gh search issues with --owner @me or assignee filters. Each issue carries its repo in the result; treat writes per-repo. Do not run tidy, assign, or reprioritize cross-repo — those are single-repo only.
Memory file: .github/jot.md
If .github/jot.md exists in the target repo, load it before any LLM-y step (extraction, ranking, dedup decision). It holds repo-specific rules: glossary, priority defaults, label conventions, "all bugs filed by @alice are P1", "anything tagged regression is P0", etc.
Read it via:
gh api "repos/OWNER/REPO/contents/.github/jot.md" --jq .content 2>/dev/null | base64 -d
Cap at ~4KB. If larger, take the first 4KB and tell the user the file was truncated.
Apply memory rules when:
- Extracting structured fields in
capture (priority, labels)
- Ranking issues in
next
- Deciding dedup similarity in
capture and tidy
If memory rules conflict with the user's explicit request in this turn, the user wins.
Cache: ~/.cache/jot-gh/
Cache recent issue digests at ~/.cache/jot-gh/<owner>__<repo>.json so next/tidy don't re-fetch 500 issues per invocation.
- TTL: 10 minutes. Compare file
mtime to now; if older, refetch.
- Bypass: any command with
--no-cache.
- Miss path:
gh issue list --repo OWNER/REPO --state open --limit 200 --json number,title,labels,assignees,updatedAt,body,url → write to cache file.
- Cache is not load-bearing. Safe to delete at any time. Never treat a cached value as authoritative for writes — re-fetch before
gh issue edit.
Action log: ~/.cache/jot-gh/actions.jsonl
Every successful write appends one JSON line:
{"ts":"2026-05-20T10:00:00Z","cmd":"capture","repo":"andresd/jot","action":"created","number":42,"url":"https://github.com/andresd/jot/issues/42","inverse":{"op":"close","number":42}}
- Keep only the last 100 lines (trim head on each append).
- Used exclusively by
undo.
- Skip the log for previews, dry-runs, and read-only commands.
Commands
capture <text>
Natural-language brain-dump → well-formed issue, with dedup check first.
Flow:
- Resolve repo. Load
.github/jot.md if present.
- Pull existing labels once:
gh label list --repo OWNER/REPO --json name --limit 100.
- Extract 3–5 keywords from
<text> (noun phrases, key verbs).
- Dedup search:
gh search issues --repo OWNER/REPO --state open --json number,title,body,url --limit 10 -- "<keywords joined with OR>"
- Score top 5 candidates for semantic similarity vs.
<text>. If best score reads as "likely duplicate" (same bug/feature, not just same topic), surface that.
- Extract structured fields, constrained by the existing-labels list and memory rules:
- title: concise, imperative for actions ("Fix login redirect loop"), declarative for observations ("Search returns 500 on empty query").
- body: original text, then a blank line, then
_Captured via jot-gh on YYYY-MM-DD._
- labels: pick zero-or-more from the existing labels. Always include exactly one of
P0|P1|P2|P3 (default P2 unless memory rules say otherwise). Include exactly one of bug|feature|chore|docs if clear from text.
- assignee: only if
<text> explicitly names a person via @handle.
- Preview via
AskUserQuestion:
Create issue (default)
Comment on dup #N (only if a dup was found)
Edit fields first
Cancel
- On
Create issue:gh issue create --repo OWNER/REPO --title "..." --body "..." --label "P2,bug" --assignee @me
Append to action log with inverse: {op: "close", number: <new_number>}.
- On
Comment on dup #N:gh issue comment <N> --repo OWNER/REPO --body "<captured text>\n\n_Linked via jot-gh dedup._"
Log with inverse: {op: "delete_comment", number: <N>, comment_id: <id>}.
- On
Edit fields first: ask which field (title/body/labels/assignee), update, re-prompt.
Do not invent labels that don't already exist on the repo. If the repo is missing P0..P3, tell the user and offer to create them (one-shot: gh label create P0 --color B60205 etc.).
next [--for @user] [--scope <filter>] [-n N] [--commit] [--repo OWNER/REPO]
Top-N ranked open issues with a one-line rationale per item. Defaults: --for @me, n=5, multi-repo.
Flow:
- List candidates:
# multi-repo (default)
gh search issues --assignee @me --state open --json repository,number,title,labels,updatedAt,body,url --limit 100
# single-repo when --repo passed
gh issue list --repo OWNER/REPO --assignee @me --state open --limit 100 --json number,title,labels,updatedAt,body,url
- For each distinct repo in the results, load
.github/jot.md (cache the load per turn).
- Rank by, in order:
- Priority label:
P0 > P1 > P2 > P3 > (none)
jot-today label present (boost)
- Age (newer wins for
P0/P1, older wins for P2/P3)
- Memory-rule hints
- Output a numbered list. Each line:
[OWNER/REPO#N] prefix, title, priority badge, one-sentence rationale, then URL on the next line.
- If
--commit: confirm via a single AskUserQuestion (Commit these N to today / Cancel), then for each chosen item:gh issue edit <N> --repo OWNER/REPO --add-label jot-today
Log each. inverse: {op: "remove_label", label: "jot-today", number: <N>}.
tidy [--repo OWNER/REPO]
Interactive backlog hygiene. Single-repo only (multi-repo tidy is too noisy).
Flow:
- Resolve repo.
- Fetch all open issues:
gh issue list --repo OWNER/REPO --state open --limit 500 --json number,title,labels,assignees,updatedAt,body,url.
- Run four passes:
- dup — cluster by title+body similarity. Report any cluster of 2+, recommending the oldest as canonical and the rest to close-with-link.
- stale —
updatedAt > 30 days ago and not in an open milestone. Recommend close with comment, or label stale.
- stale-assignment — has assignee, no comment or edit in last 14 days. Recommend unassign and ping.
- missing-priority — no
P0|P1|P2|P3 label. Recommend P2 as default (or memory-rule default).
- Output a single numbered list (text mode — not
AskUserQuestion; tidy routinely produces 20+ findings):1. [dup] #42 looks like dup of #38 — close #42 with link?
2. [stale] #15 — no activity since 2026-04-12 (38d). Close with comment?
3. [stale-asn] #71 — @bob assigned 22d ago, no movement. Unassign?
4. [priority] #88 — missing P-label. Add P2?
...
- Prompt the user in plain text:
Apply which? Reply with numbers (e.g. 1,3,5), all, or cancel.
- Apply each accepted finding via the appropriate
gh issue edit / gh issue close / gh issue comment. Log every write.
ask <question> [--repo OWNER/REPO]
NL Q&A over the backlog with #N citations. Read-only.
Flow:
- Resolve repo (or stay multi-repo if none given).
- Search both open and closed:
gh search issues --repo OWNER/REPO --state all --json number,title,body,state,labels,closedAt,url --limit 30 -- "<keywords>"
- Answer in 2–4 sentences. Cite each referenced issue as
[#42](URL) (markdown link).
- No writes. No action log.
assign <NL intent> [--repo OWNER/REPO]
Bulk reassignment by natural language. Single-repo only.
Example intents:
- "all open auth bugs to @alice"
- "@bob's open issues to @carol"
- "everything labeled regression to @me"
Flow:
- Parse the intent into
{ label?, assignee_old?, assignee_new, state? }. If parsing is uncertain, ask via AskUserQuestion with the best-guess filter.
- List candidates:
gh issue list --repo OWNER/REPO --state open --label "<label>" --assignee "<old_or_*>" --limit 200 --json number,title,assignees,url
- Show the numbered candidate list (text mode).
- Prompt:
Reassign these to @<new>? Reply with numbers, all, or cancel. Numbers SKIPPED will keep their current assignee.
- For each chosen item, use TOCTOU best-effort:
- Re-read the issue (fresh, no cache).
- If
<new> is already assigned, skip with a note.
- Else:
gh issue edit <N> --repo OWNER/REPO --add-assignee <new> (and --remove-assignee <old> if specified).
- On HTTP 422/409 (concurrent edit), warn and continue.
- Log each.
inverse: {op: "set_assignees", number: <N>, assignees: [<prev list>]}.
reprioritize <NL intent> [--repo OWNER/REPO]
Bulk priority change by natural language. Single-repo only.
Example intents:
- "all launch-blocker label → P0"
- "all P3 bugs older than 90 days → close as stale" (this overlaps with
tidy; route to tidy if the intent is hygiene, not promotion)
- "@alice's open issues to P1"
Same flow as assign, but operating on P0|P1|P2|P3 labels:
- For each accepted item:
gh issue edit <N> --repo OWNER/REPO --remove-label "<old P>" --add-label "<new P>".
- Log with
inverse: {op: "swap_label", number: <N>, from: <new>, to: <old>}.
status [--team] [--repo OWNER/REPO]
Default (no --team): list issues with jot-today label assigned to @me, across repos. Show priority and repo.
gh search issues --assignee @me --label jot-today --state open --json repository,number,title,labels,url
--team: list jot-today issues in the target repo across all assignees, grouped by assignee.
gh issue list --repo OWNER/REPO --label jot-today --state open --limit 100 --json number,title,assignees,labels,url
No writes.
clear-plan [--repo OWNER/REPO]
Remove jot-today from all issues currently labeled with it for @me.
Flow:
- List:
gh search issues --assignee @me --label jot-today --state open --json repository,number,url
- Show count and titles. Confirm via
AskUserQuestion: Clear N items / Cancel.
- For each:
gh issue edit <N> --repo OWNER/REPO --remove-label jot-today. Log.
undo
Reverse the most recent action within 24 hours.
Flow:
- Read
~/.cache/jot-gh/actions.jsonl (tail).
- Find the most recent entry where
ts > now - 24h. If none, tell the user no recent action is reversible.
- Show the action and its planned inverse via
AskUserQuestion: Undo / Cancel.
- Re-read the affected issue's current state (no cache). If state has changed in a way that makes the inverse meaningless (e.g. issue already closed, label already gone), abort with a clear explanation rather than forcing it.
- Apply the inverse:
created #N → gh issue close <N> --comment "Reverted by jot-gh undo"
closed #N → gh issue reopen <N>
add_label X #N → gh issue edit <N> --remove-label X
remove_label X #N → gh issue edit <N> --add-label X
swap_label from=A to=B #N → gh issue edit <N> --add-label A --remove-label B
add_assignee U #N → gh issue edit <N> --remove-assignee U
set_assignees [list] #N → gh issue edit <N> --add-assignee <list...> after clearing current
commented #N (cmt_id) → gh api -X DELETE "repos/OWNER/REPO/issues/comments/<id>"
- On success, remove the entry from the log. Do not chain undos — one
undo reverses one action.
help
Print one line per command above. No gh calls. No log.
Output conventions
- Cross-repo listings: always prefix items with
[OWNER/REPO#N]. Single-repo listings: prefix with #N.
- Every write reports the resulting URL on the next line (fetch via
gh issue view <n> --repo OWNER/REPO --json url -q .url if not already known).
- No emoji in skill output unless the user uses emoji first.
gh errors surface verbatim — do not swallow them. If gh exits non-zero on a write, stop the batch and report which item failed.
Confirmation patterns
- Single write (
capture create, single-item assign, undo, --commit): AskUserQuestion with Confirm / Edit / Cancel.
- Multi-write (
tidy, bulk assign, bulk reprioritize, clear-plan): numbered text list, free-text reply (1,3,5 / all / cancel). AskUserQuestion only fits 4 options, so it does not scale here.
- Read-only (
status, ask, next without --commit): no confirmation.
Rate limits
gh handles auth and rate-limit retry on its own. Avoid hand-built pagination loops — use --limit and let gh paginate internally. If a write batch encounters a rate-limit error, stop, report progress so far, and tell the user to retry after the reset window shown in the error.
Anti-patterns
- Don't call the REST API via
curl / gh api unless gh issue / gh search / gh label / gh repo cannot express the operation. gh handles auth, retries, and pagination.
- Don't batch writes without showing a preview first, even when the user said "go ahead" earlier in the turn — previews are cheap insurance against an LLM misparse.
- Don't invent labels that aren't already on the repo. If a needed label is missing, ask before creating it.
- Don't write to closed issues unless the user explicitly named the issue.
- Don't store any auth, tokens, or secrets.
gh is the source of truth.
- Don't treat the cache as authoritative for writes — always re-fetch the issue before
gh issue edit.
- Don't run
tidy, assign, or reprioritize cross-repo. They are single-repo by design.
- Don't chain undos. One
undo reverses one action; if the user wants more, they invoke undo again.
1---2name: jot-gh3description: Capture, triage, and plan work on GitHub issues from natural language, across one or many repos. Auto-triggers on phrases like "capture this:", "what should I work on next", "what's on my plate today", "tidy the backlog", "find duplicate issues", "all <X> bugs to @<user>", and explicit `/jot-gh <cmd>`, `jot:`, or `jot-gh:` prefixes. Use when the user wants to turn brain-dump text into a well-formed GitHub issue, decide what to work on next, run dedup/stale passes, or bulk-edit issues by natural-language intent. Requires the `gh` CLI authenticated via `gh auth login`.4---56# jot-gh78A GitHub-only port of the `jot` CLI loop: capture brain-dumps into well-formed issues, decide what to work on next, and keep the backlog tidy — all through `gh` calls. No tracker abstraction (GitHub only), no licensing, no persistent SQLite. Auth is whatever `gh auth status` already provides.910## When to invoke1112**Explicit (always invoke):**13- `/jot-gh <command>` — slash form14- `jot: <command>` or `jot-gh: <command>` — prefix form1516**Auto-invoke (use judgment, prefer explicit when unsure):**17- "capture this: ..." / "capture: ..." / "log this: ..."18- "what should I work on (next)?" / "what's next?"19- "what's on my plate today?" / "what am I doing today?"20- "tidy the backlog" / "find duplicate issues" / "find stale issues"21- "(re)assign all <X> bugs to @<user>"22- "make all <X> issues P0/P1/P2/P3"23- "what does the backlog say about <topic>?"2425**Do NOT invoke for:**26- General GitHub questions unrelated to issue triage ("what's the API for X?", "how do I fork?")27- Pull request review or code review (that belongs to other skills)28- Anything that does not touch the issue lifecycle (issues, labels, assignees, comments)2930## Prerequisites3132Before any command, verify `gh` is authed:3334```bash35gh auth status36```3738If the exit code is non-zero, stop and tell the user:3940> Run `gh auth login` first. jot-gh uses your `gh` credentials directly and does not store its own auth.4142Also ensure `jq` is available for parsing `gh --json` output (`command -v jq` — error clearly if missing).4344## Repo resolution4546Most commands operate on **one repo at a time**. To pick:47481. If the user passed `--repo OWNER/REPO`, use that.492. Otherwise, run `gh repo view --json nameWithOwner -q .nameWithOwner` from the current directory — if it returns a repo, use it.503. Otherwise, ask the user which repo via `AskUserQuestion`. Offer the 3 most-recent repos from `gh repo list --json nameWithOwner --limit 10 -q '.[].nameWithOwner'` as options.5152**Multi-repo flows** (`next` by default, `ask` if no repo context): use `gh search issues` with `--owner @me` or assignee filters. Each issue carries its repo in the result; treat writes per-repo. Do not run `tidy`, `assign`, or `reprioritize` cross-repo — those are single-repo only.5354## Memory file: `.github/jot.md`5556If `.github/jot.md` exists in the target repo, load it before any LLM-y step (extraction, ranking, dedup decision). It holds repo-specific rules: glossary, priority defaults, label conventions, "all bugs filed by @alice are P1", "anything tagged regression is P0", etc.5758Read it via:5960```bash61gh api "repos/OWNER/REPO/contents/.github/jot.md" --jq .content 2>/dev/null | base64 -d62```6364Cap at ~4KB. If larger, take the first 4KB and tell the user the file was truncated.6566Apply memory rules when:67- Extracting structured fields in `capture` (priority, labels)68- Ranking issues in `next`69- Deciding dedup similarity in `capture` and `tidy`7071If memory rules conflict with the user's explicit request in this turn, the user wins.7273## Cache: `~/.cache/jot-gh/`7475Cache recent issue digests at `~/.cache/jot-gh/<owner>__<repo>.json` so `next`/`tidy` don't re-fetch 500 issues per invocation.7677- **TTL**: 10 minutes. Compare file `mtime` to now; if older, refetch.78- **Bypass**: any command with `--no-cache`.79- **Miss path**: `gh issue list --repo OWNER/REPO --state open --limit 200 --json number,title,labels,assignees,updatedAt,body,url` → write to cache file.80- Cache is **not load-bearing**. Safe to delete at any time. Never treat a cached value as authoritative for writes — re-fetch before `gh issue edit`.8182## Action log: `~/.cache/jot-gh/actions.jsonl`8384Every successful write appends one JSON line:8586```json87{"ts":"2026-05-20T10:00:00Z","cmd":"capture","repo":"andresd/jot","action":"created","number":42,"url":"https://github.com/andresd/jot/issues/42","inverse":{"op":"close","number":42}}88```8990- Keep only the last 100 lines (trim head on each append).91- Used exclusively by `undo`.92- Skip the log for previews, dry-runs, and read-only commands.9394---9596## Commands9798### `capture <text>`99100Natural-language brain-dump → well-formed issue, with dedup check first.101102**Flow:**1031041. Resolve repo. Load `.github/jot.md` if present.1052. Pull existing labels once: `gh label list --repo OWNER/REPO --json name --limit 100`.1063. Extract 3–5 keywords from `<text>` (noun phrases, key verbs).1074. Dedup search:108 ```bash109 gh search issues --repo OWNER/REPO --state open --json number,title,body,url --limit 10 -- "<keywords joined with OR>"110 ```1115. Score top 5 candidates for semantic similarity vs. `<text>`. If best score reads as "likely duplicate" (same bug/feature, not just same topic), surface that.1126. Extract structured fields, constrained by the existing-labels list and memory rules:113 - **title**: concise, imperative for actions ("Fix login redirect loop"), declarative for observations ("Search returns 500 on empty query").114 - **body**: original text, then a blank line, then `_Captured via jot-gh on YYYY-MM-DD._`115 - **labels**: pick zero-or-more from the existing labels. **Always** include exactly one of `P0|P1|P2|P3` (default `P2` unless memory rules say otherwise). Include exactly one of `bug|feature|chore|docs` if clear from text.116 - **assignee**: only if `<text>` explicitly names a person via `@handle`.1177. Preview via `AskUserQuestion`:118 - `Create issue` (default)119 - `Comment on dup #N` (only if a dup was found)120 - `Edit fields first`121 - `Cancel`1228. On `Create issue`:123 ```bash124 gh issue create --repo OWNER/REPO --title "..." --body "..." --label "P2,bug" --assignee @me125 ```126 Append to action log with `inverse: {op: "close", number: <new_number>}`.1279. On `Comment on dup #N`:128 ```bash129 gh issue comment <N> --repo OWNER/REPO --body "<captured text>\n\n_Linked via jot-gh dedup._"130 ```131 Log with `inverse: {op: "delete_comment", number: <N>, comment_id: <id>}`.13210. On `Edit fields first`: ask which field (title/body/labels/assignee), update, re-prompt.133134**Do not invent labels** that don't already exist on the repo. If the repo is missing `P0..P3`, tell the user and offer to create them (one-shot: `gh label create P0 --color B60205` etc.).135136### `next [--for @user] [--scope <filter>] [-n N] [--commit] [--repo OWNER/REPO]`137138Top-N ranked open issues with a one-line rationale per item. Defaults: `--for @me`, `n=5`, multi-repo.139140**Flow:**1411421. List candidates:143 ```bash144 # multi-repo (default)145 gh search issues --assignee @me --state open --json repository,number,title,labels,updatedAt,body,url --limit 100146 # single-repo when --repo passed147 gh issue list --repo OWNER/REPO --assignee @me --state open --limit 100 --json number,title,labels,updatedAt,body,url148 ```1492. For each distinct repo in the results, load `.github/jot.md` (cache the load per turn).1503. Rank by, in order:151 - Priority label: `P0 > P1 > P2 > P3 > (none)`152 - `jot-today` label present (boost)153 - Age (newer wins for `P0/P1`, older wins for `P2/P3`)154 - Memory-rule hints1554. Output a numbered list. Each line: `[OWNER/REPO#N]` prefix, title, priority badge, one-sentence rationale, then URL on the next line.1565. If `--commit`: confirm via a single `AskUserQuestion` (`Commit these N to today / Cancel`), then for each chosen item:157 ```bash158 gh issue edit <N> --repo OWNER/REPO --add-label jot-today159 ```160 Log each. `inverse: {op: "remove_label", label: "jot-today", number: <N>}`.161162### `tidy [--repo OWNER/REPO]`163164Interactive backlog hygiene. **Single-repo only** (multi-repo tidy is too noisy).165166**Flow:**1671681. Resolve repo.1692. Fetch all open issues: `gh issue list --repo OWNER/REPO --state open --limit 500 --json number,title,labels,assignees,updatedAt,body,url`.1703. Run four passes:171 - **dup** — cluster by title+body similarity. Report any cluster of 2+, recommending the oldest as canonical and the rest to close-with-link.172 - **stale** — `updatedAt > 30 days ago` and not in an open milestone. Recommend close with comment, or label `stale`.173 - **stale-assignment** — has assignee, no comment or edit in last 14 days. Recommend unassign and ping.174 - **missing-priority** — no `P0|P1|P2|P3` label. Recommend `P2` as default (or memory-rule default).1754. Output a single numbered list (text mode — not `AskUserQuestion`; tidy routinely produces 20+ findings):176 ```177 1. [dup] #42 looks like dup of #38 — close #42 with link?178 2. [stale] #15 — no activity since 2026-04-12 (38d). Close with comment?179 3. [stale-asn] #71 — @bob assigned 22d ago, no movement. Unassign?180 4. [priority] #88 — missing P-label. Add P2?181 ...182 ```1835. Prompt the user in plain text:184 > Apply which? Reply with numbers (e.g. `1,3,5`), `all`, or `cancel`.1856. Apply each accepted finding via the appropriate `gh issue edit` / `gh issue close` / `gh issue comment`. Log every write.186187### `ask <question> [--repo OWNER/REPO]`188189NL Q&A over the backlog with `#N` citations. Read-only.190191**Flow:**1921931. Resolve repo (or stay multi-repo if none given).1942. Search both open and closed:195 ```bash196 gh search issues --repo OWNER/REPO --state all --json number,title,body,state,labels,closedAt,url --limit 30 -- "<keywords>"197 ```1983. Answer in 2–4 sentences. Cite each referenced issue as `[#42](URL)` (markdown link).1994. No writes. No action log.200201### `assign <NL intent> [--repo OWNER/REPO]`202203Bulk reassignment by natural language. **Single-repo only.**204205Example intents:206- "all open auth bugs to @alice"207- "@bob's open issues to @carol"208- "everything labeled regression to @me"209210**Flow:**2112121. Parse the intent into `{ label?, assignee_old?, assignee_new, state? }`. If parsing is uncertain, ask via `AskUserQuestion` with the best-guess filter.2132. List candidates:214 ```bash215 gh issue list --repo OWNER/REPO --state open --label "<label>" --assignee "<old_or_*>" --limit 200 --json number,title,assignees,url216 ```2173. Show the numbered candidate list (text mode).2184. Prompt:219 > Reassign these to @\<new\>? Reply with numbers, `all`, or `cancel`. Numbers SKIPPED will keep their current assignee.2205. For each chosen item, use TOCTOU best-effort:221 - Re-read the issue (fresh, no cache).222 - If `<new>` is already assigned, skip with a note.223 - Else: `gh issue edit <N> --repo OWNER/REPO --add-assignee <new>` (and `--remove-assignee <old>` if specified).224 - On HTTP 422/409 (concurrent edit), warn and continue.2256. Log each. `inverse: {op: "set_assignees", number: <N>, assignees: [<prev list>]}`.226227### `reprioritize <NL intent> [--repo OWNER/REPO]`228229Bulk priority change by natural language. **Single-repo only.**230231Example intents:232- "all launch-blocker label → P0"233- "all P3 bugs older than 90 days → close as stale" (this overlaps with `tidy`; route to `tidy` if the intent is hygiene, not promotion)234- "@alice's open issues to P1"235236Same flow as `assign`, but operating on `P0|P1|P2|P3` labels:237238- For each accepted item: `gh issue edit <N> --repo OWNER/REPO --remove-label "<old P>" --add-label "<new P>"`.239- Log with `inverse: {op: "swap_label", number: <N>, from: <new>, to: <old>}`.240241### `status [--team] [--repo OWNER/REPO]`242243Default (no `--team`): list issues with `jot-today` label assigned to `@me`, across repos. Show priority and repo.244245```bash246gh search issues --assignee @me --label jot-today --state open --json repository,number,title,labels,url247```248249`--team`: list `jot-today` issues in the target repo across all assignees, grouped by assignee.250251```bash252gh issue list --repo OWNER/REPO --label jot-today --state open --limit 100 --json number,title,assignees,labels,url253```254255No writes.256257### `clear-plan [--repo OWNER/REPO]`258259Remove `jot-today` from all issues currently labeled with it for `@me`.260261**Flow:**2622631. List:264 ```bash265 gh search issues --assignee @me --label jot-today --state open --json repository,number,url266 ```2672. Show count and titles. Confirm via `AskUserQuestion`: `Clear N items / Cancel`.2683. For each: `gh issue edit <N> --repo OWNER/REPO --remove-label jot-today`. Log.269270### `undo`271272Reverse the most recent action within 24 hours.273274**Flow:**2752761. Read `~/.cache/jot-gh/actions.jsonl` (tail).2772. Find the most recent entry where `ts > now - 24h`. If none, tell the user no recent action is reversible.2783. Show the action and its planned inverse via `AskUserQuestion`: `Undo / Cancel`.2794. Re-read the affected issue's current state (no cache). If state has changed in a way that makes the inverse meaningless (e.g. issue already closed, label already gone), abort with a clear explanation rather than forcing it.2805. Apply the inverse:281 - `created #N` → `gh issue close <N> --comment "Reverted by jot-gh undo"`282 - `closed #N` → `gh issue reopen <N>`283 - `add_label X #N` → `gh issue edit <N> --remove-label X`284 - `remove_label X #N` → `gh issue edit <N> --add-label X`285 - `swap_label from=A to=B #N` → `gh issue edit <N> --add-label A --remove-label B`286 - `add_assignee U #N` → `gh issue edit <N> --remove-assignee U`287 - `set_assignees [list] #N` → `gh issue edit <N> --add-assignee <list...>` after clearing current288 - `commented #N (cmt_id)` → `gh api -X DELETE "repos/OWNER/REPO/issues/comments/<id>"`2896. On success, remove the entry from the log. Do not chain undos — one `undo` reverses one action.290291### `help`292293Print one line per command above. No `gh` calls. No log.294295---296297## Output conventions298299- Cross-repo listings: always prefix items with `[OWNER/REPO#N]`. Single-repo listings: prefix with `#N`.300- Every write reports the resulting URL on the next line (fetch via `gh issue view <n> --repo OWNER/REPO --json url -q .url` if not already known).301- No emoji in skill output unless the user uses emoji first.302- `gh` errors surface verbatim — do not swallow them. If `gh` exits non-zero on a write, stop the batch and report which item failed.303304## Confirmation patterns305306- **Single write** (`capture` create, single-item `assign`, `undo`, `--commit`): `AskUserQuestion` with Confirm / Edit / Cancel.307- **Multi-write** (`tidy`, bulk `assign`, bulk `reprioritize`, `clear-plan`): numbered text list, free-text reply (`1,3,5` / `all` / `cancel`). `AskUserQuestion` only fits 4 options, so it does not scale here.308- **Read-only** (`status`, `ask`, `next` without `--commit`): no confirmation.309310## Rate limits311312`gh` handles auth and rate-limit retry on its own. Avoid hand-built pagination loops — use `--limit` and let `gh` paginate internally. If a write batch encounters a rate-limit error, stop, report progress so far, and tell the user to retry after the reset window shown in the error.313314## Anti-patterns315316- Don't call the REST API via `curl` / `gh api` unless `gh issue` / `gh search` / `gh label` / `gh repo` cannot express the operation. `gh` handles auth, retries, and pagination.317- Don't batch writes without showing a preview first, even when the user said "go ahead" earlier in the turn — previews are cheap insurance against an LLM misparse.318- Don't invent labels that aren't already on the repo. If a needed label is missing, ask before creating it.319- Don't write to closed issues unless the user explicitly named the issue.320- Don't store any auth, tokens, or secrets. `gh` is the source of truth.321- Don't treat the cache as authoritative for writes — always re-fetch the issue before `gh issue edit`.322- Don't run `tidy`, `assign`, or `reprioritize` cross-repo. They are single-repo by design.323- Don't chain undos. One `undo` reverses one action; if the user wants more, they invoke `undo` again.