Gh
Skill andresd/jot-skills/gh
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`.From its SKILL.md
npx -y skills add andresd/jot-skills --skill ghAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
- runs commandsInstructs the agent to run 8 commands, including `gh auth status` and 7 more.
SKILL.md
15.8 KB, ~4.3k tokens by cl100k_base, as published. Nobody here has run it
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 formjot: <command>orjot-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 <X> bugs to @<user>"
- "make all <X> issues P0/P1/P2/P3"
- "what does the backlog say about <topic>?"
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 loginfirst. jot-gh uses yourghcredentials 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 .nameWithOwnerfrom the current directory — if it returns a repo, use it. - Otherwise, ask the user which repo via
AskUserQuestion. Offer the 3 most-recent repos fromgh 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
captureandtidy
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
mtimeto 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.mdif 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(defaultP2unless memory rules say otherwise). Include exactly one ofbug|feature|chore|docsif 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 firstCancel
- On
Create issue:
Append to action log withgh issue create --repo OWNER/REPO --title "..." --body "..." --label "P2,bug" --assignee @meinverse: {op: "close", number: <new_number>}. - On
Comment on dup #N:
Log withgh issue comment <N> --repo OWNER/REPO --body "<captured text>\n\n_Linked via jot-gh dedup._"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-todaylabel present (boost)- Age (newer wins for
P0/P1, older wins forP2/P3) - Memory-rule hints
- Priority label:
- 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 singleAskUserQuestion(Commit these N to today / Cancel), then for each chosen item:
Log each.gh issue edit <N> --repo OWNER/REPO --add-label jot-todayinverse: {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 agoand not in an open milestone. Recommend close with comment, or labelstale. - stale-assignment — has assignee, no comment or edit in last 14 days. Recommend unassign and ping.
- missing-priority — no
P0|P1|P2|P3label. RecommendP2as 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, orcancel. - 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 viaAskUserQuestionwith 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, orcancel. 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 totidyif 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 Xremove_label X #N→gh issue edit <N> --add-label Xswap_label from=A to=B #N→gh issue edit <N> --add-label A --remove-label Badd_assignee U #N→gh issue edit <N> --remove-assignee Uset_assignees [list] #N→gh issue edit <N> --add-assignee <list...>after clearing currentcommented #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
undoreverses 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 .urlif not already known). - No emoji in skill output unless the user uses emoji first.
gherrors surface verbatim — do not swallow them. Ifghexits non-zero on a write, stop the batch and report which item failed.
Confirmation patterns
- Single write (
capturecreate, single-itemassign,undo,--commit):AskUserQuestionwith Confirm / Edit / Cancel. - Multi-write (
tidy, bulkassign, bulkreprioritize,clear-plan): numbered text list, free-text reply (1,3,5/all/cancel).AskUserQuestiononly fits 4 options, so it does not scale here. - Read-only (
status,ask,nextwithout--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 apiunlessgh issue/gh search/gh label/gh repocannot express the operation.ghhandles 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.
ghis 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, orreprioritizecross-repo. They are single-repo by design. - Don't chain undos. One
undoreverses one action; if the user wants more, they invokeundoagain.
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.