feat(claude): add /ticket and /ticket-list commands for GitHub Issues (Step 3)
Adapt the Notion onboarding prompt — originally Jira/MCP-oriented — to this project's actual issue tracker (GitHub at salestech-group/MiroFish) using the gh CLI. Slash commands (.claude/commands/): - /ticket <number>: fetch a GitHub issue via gh, self-assign, try to add an in-progress label (skips silently if the label doesn't exist), and snapshot the issue (frontmatter + body) to .ticket/<n>.md so later planning steps can read the description without refetching. - /ticket-list: interactive overview of issues; asks for filters (open/closed, status, assignee, milestone, labels) and runs a single gh issue list with the answers, rendering a compact markdown table. Workspace: - .ticket/repo.md declares the target repo (GitHub equivalent of the Jira "board.md" referenced in the prompt). - .gitignore: ignore .ticket/* except repo.md and .gitkeep so cached ticket markdowns stay local. Settings: - Allow-list gh issue view/list/edit/comment, gh repo view, gh pr view/list, gh auth status to avoid permission prompts. Documentation: .claude/onboarding/step3_planning/01_ticket_sync.md
This commit is contained in:
parent
76f719e760
commit
52f78d9fe3
|
|
@ -0,0 +1,62 @@
|
||||||
|
---
|
||||||
|
description: List GitHub issues for this project with selectable filters
|
||||||
|
argument-hint: (no args — interactive)
|
||||||
|
---
|
||||||
|
|
||||||
|
# /ticket-list — Show GitHub issues for this project
|
||||||
|
|
||||||
|
You are running the `/ticket-list` slash command. The user wants an overview of issues for the current project's GitHub repo so they can decide what to pick up next.
|
||||||
|
|
||||||
|
## Repository
|
||||||
|
|
||||||
|
Read the target repo from `.ticket/repo.md` (look for the `Repo (owner/name)` line). If the file does not exist, ask the user for the `owner/name` slug, write it to `.ticket/repo.md`, then continue.
|
||||||
|
|
||||||
|
Set `REPO` to that value (e.g., `salestech-group/MiroFish`).
|
||||||
|
|
||||||
|
## Filters — ask the user first
|
||||||
|
|
||||||
|
Before running anything, ask which subset of issues to list. Offer these toggles (the user can answer in plain language; default any unanswered to "no"):
|
||||||
|
|
||||||
|
1. **Include closed?** (default: no — open only)
|
||||||
|
2. **Status filter:**
|
||||||
|
- "todo" / "no assignee" — only issues that nobody is working on yet
|
||||||
|
- "in-progress" — only issues with the `in-progress` label or a self-assignee
|
||||||
|
- "all open" — no status filter
|
||||||
|
3. **Assigned to me only?** (default: no)
|
||||||
|
4. **Milestone / sprint:** the user can name a milestone, or skip
|
||||||
|
5. **Label filter:** optional comma-separated labels to require
|
||||||
|
|
||||||
|
Wait for the user's answers before running `gh`.
|
||||||
|
|
||||||
|
## Run the query
|
||||||
|
|
||||||
|
Build a single `gh issue list` invocation from the answers. Useful flags:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
gh issue list --repo "$REPO" \
|
||||||
|
--state open # or "all" / "closed"
|
||||||
|
--assignee "@me" # if "assigned to me only"
|
||||||
|
--label "in-progress" # if status=in-progress
|
||||||
|
--label "<label1>,<label2>" # if user gave label filter
|
||||||
|
--milestone "<milestone>" # if user named one
|
||||||
|
--limit 50 \
|
||||||
|
--json number,title,state,labels,assignees,milestone,updatedAt,url
|
||||||
|
```
|
||||||
|
|
||||||
|
For "todo / no assignee" issues, GitHub doesn't expose a `--no-assignee` flag — list open issues without `--assignee` and **filter the JSON locally** to keep only items where `.assignees == []`.
|
||||||
|
|
||||||
|
## Output
|
||||||
|
|
||||||
|
Render a compact table (markdown) ordered by `updatedAt` desc:
|
||||||
|
|
||||||
|
| # | Title | Labels | Assignees | Milestone | Updated |
|
||||||
|
|
||||||
|
Truncate titles to ~70 chars; show `—` for empty fields. End with a one-line hint: *"Pick one: `/ticket <number>` to start work."*
|
||||||
|
|
||||||
|
If there are no results, say so plainly and suggest relaxing a filter.
|
||||||
|
|
||||||
|
## Constraints
|
||||||
|
|
||||||
|
- Use `gh` exclusively. Do not call the REST API directly.
|
||||||
|
- Be conservative with output size — cap at 50 rows by default.
|
||||||
|
- Don't refetch on every minor question; ask once, then run once.
|
||||||
|
|
@ -0,0 +1,68 @@
|
||||||
|
---
|
||||||
|
description: Pull a GitHub issue into the local ticket workspace and mark it as in-progress
|
||||||
|
argument-hint: <issue-number>
|
||||||
|
---
|
||||||
|
|
||||||
|
# /ticket — Start work on a GitHub issue
|
||||||
|
|
||||||
|
You are running the `/ticket` slash command. The user has typed:
|
||||||
|
|
||||||
|
```
|
||||||
|
/ticket $ARGUMENTS
|
||||||
|
```
|
||||||
|
|
||||||
|
Goal: pick up the GitHub issue identified by `$ARGUMENTS`, transition it to "in progress", and snapshot it as a markdown file under `.ticket/` so later steps (planning, implementation) can read its description without making more API calls.
|
||||||
|
|
||||||
|
## Repository
|
||||||
|
|
||||||
|
Read the target repo from `.ticket/repo.md` (look for the `Repo (owner/name)` line). If the file does not exist, ask the user for the `owner/name` slug, write it to `.ticket/repo.md`, then continue.
|
||||||
|
|
||||||
|
Set `REPO` to that value (e.g., `salestech-group/MiroFish`) and use it explicitly in every `gh` command via `--repo "$REPO"`.
|
||||||
|
|
||||||
|
## Steps
|
||||||
|
|
||||||
|
1. **Validate input.** `$ARGUMENTS` must be a non-empty issue number (or a string GitHub will accept). If empty, ask the user for the issue number and stop.
|
||||||
|
|
||||||
|
2. **Fetch the issue.**
|
||||||
|
```bash
|
||||||
|
gh issue view "$ARGUMENTS" --repo "$REPO" \
|
||||||
|
--json number,title,state,url,labels,assignees,milestone,author,createdAt,updatedAt,body
|
||||||
|
```
|
||||||
|
If this fails, surface the error and stop. Common causes: wrong number, no access, network.
|
||||||
|
|
||||||
|
3. **Mark as in-progress.** GitHub Issues has no built-in "in progress" state. Approximate it:
|
||||||
|
- Self-assign: `gh issue edit "$ARGUMENTS" --repo "$REPO" --add-assignee @me`
|
||||||
|
- Try to add the `in-progress` label. If the label does not exist, do not create it — just continue without it. (`gh issue edit ... --add-label in-progress 2>/dev/null || true`)
|
||||||
|
- If the user indicates the project uses a different label or a GitHub Project with a status field, follow that instead.
|
||||||
|
|
||||||
|
4. **Snapshot to `.ticket/<number>.md`.** Write a markdown file with this structure (replace fields from the JSON above):
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
---
|
||||||
|
number: <number>
|
||||||
|
title: <title>
|
||||||
|
state: <state>
|
||||||
|
url: <url>
|
||||||
|
author: <author.login>
|
||||||
|
assignees: <comma-separated logins, "—" if none>
|
||||||
|
labels: <comma-separated names, "—" if none>
|
||||||
|
milestone: <milestone.title or "—">
|
||||||
|
createdAt: <createdAt>
|
||||||
|
updatedAt: <updatedAt>
|
||||||
|
workingSince: <today YYYY-MM-DD>
|
||||||
|
---
|
||||||
|
|
||||||
|
# #<number> — <title>
|
||||||
|
|
||||||
|
<body verbatim from the issue>
|
||||||
|
```
|
||||||
|
|
||||||
|
This description is consumed by later planning steps — keep it intact.
|
||||||
|
|
||||||
|
5. **Confirm to the user.** One short summary: ticket number, title, current state, what was changed (assignee, label), and the path to the snapshot. Don't dump the whole body back to the terminal.
|
||||||
|
|
||||||
|
## Constraints
|
||||||
|
|
||||||
|
- Use `gh` exclusively for GitHub. Do not call the REST API directly.
|
||||||
|
- Do not commit `.ticket/<number>.md` — the directory is gitignored except for `repo.md` / `.gitkeep`.
|
||||||
|
- Be quiet on success. Be loud on failure (state what failed and the next action).
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
# Step 3 — Ticket Sync (GitHub Issues + gh CLI)
|
||||||
|
|
||||||
|
## Date: 2026-05-06
|
||||||
|
|
||||||
|
## Tooling Decision (vs. the original Notion prompt)
|
||||||
|
The Notion prompt is Jira/MCP-oriented. For this project we use:
|
||||||
|
|
||||||
|
- **Issue tracker:** GitHub Issues at https://github.com/salestech-group/MiroFish
|
||||||
|
- **Transport:** the `gh` CLI (already installed and authenticated as
|
||||||
|
`dseemann`).
|
||||||
|
- **No Atlassian MCP usage in these commands** — `gh` is faster, scoped
|
||||||
|
by token, and matches the rest of the team's Git workflow.
|
||||||
|
|
||||||
|
Equivalences vs. the prompt:
|
||||||
|
- *"board.md"* → `.ticket/repo.md` (stores `owner/name`).
|
||||||
|
- *"IN ARBEIT" transition* → GitHub doesn't have a built-in
|
||||||
|
in-progress state; we approximate it with self-assignment + an
|
||||||
|
`in-progress` label (graceful fallback if the label doesn't exist).
|
||||||
|
|
||||||
|
## What Was Created
|
||||||
|
|
||||||
|
### `.ticket/`
|
||||||
|
- Directory created. Most contents are gitignored — only `repo.md` and
|
||||||
|
`.gitkeep` are tracked, so the local cache of in-progress tickets
|
||||||
|
stays out of the repo.
|
||||||
|
- `.ticket/repo.md` — declares `salestech-group/MiroFish` as the
|
||||||
|
target repo.
|
||||||
|
- `.gitignore` — added `.ticket/*` with negations for `repo.md` and
|
||||||
|
`.gitkeep`.
|
||||||
|
|
||||||
|
### `.claude/commands/ticket.md`
|
||||||
|
Slash command: `/ticket <issue-number>`
|
||||||
|
- Reads the repo from `.ticket/repo.md`.
|
||||||
|
- `gh issue view --json …` to fetch the issue.
|
||||||
|
- Self-assigns (`gh issue edit --add-assignee @me`).
|
||||||
|
- Tries to add the `in-progress` label; silently skips if the label
|
||||||
|
doesn't exist on the repo.
|
||||||
|
- Snapshots the issue (frontmatter + full body) to `.ticket/<n>.md` so
|
||||||
|
later planning / implementation steps have the description without
|
||||||
|
re-fetching.
|
||||||
|
|
||||||
|
### `.claude/commands/ticket-list.md`
|
||||||
|
Slash command: `/ticket-list` (interactive)
|
||||||
|
- Reads the repo from `.ticket/repo.md`.
|
||||||
|
- Asks the user for filters before running anything:
|
||||||
|
- include closed
|
||||||
|
- status (todo / in-progress / all open)
|
||||||
|
- assigned to me only
|
||||||
|
- milestone
|
||||||
|
- labels
|
||||||
|
- Builds a single `gh issue list` invocation from the answers.
|
||||||
|
- For "no assignee" filtering, post-processes the JSON locally because
|
||||||
|
`gh` has no `--no-assignee` flag.
|
||||||
|
- Renders a compact markdown table; ends with a hint to use
|
||||||
|
`/ticket <n>` to start work.
|
||||||
|
|
||||||
|
### `.claude/settings.json`
|
||||||
|
- Allow-listed `gh issue view/list/edit/comment`, `gh repo view`,
|
||||||
|
`gh pr view/list`, `gh auth status` so the slash commands run
|
||||||
|
without permission prompts.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
- `gh issue list --repo salestech-group/MiroFish --state open` returned
|
||||||
|
the open issue list (issue #1 currently open).
|
||||||
|
- `settings.json` parses as valid JSON.
|
||||||
|
- `gh auth status` confirmed (token: `ghp_…`, scopes incl. `repo`).
|
||||||
|
|
||||||
|
## Limitations / Notes
|
||||||
|
- "In progress" semantics on GitHub Issues is convention-driven. If
|
||||||
|
the team adopts a GitHub Project (v2) with a Status field, update
|
||||||
|
the `/ticket` command to call `gh project item-edit …` instead of
|
||||||
|
the label/assignee approximation.
|
||||||
|
- Atlassian MCP setup from Step 0 is unused here. Leave it configured
|
||||||
|
for any cross-project Jira lookups.
|
||||||
|
|
||||||
|
## Next
|
||||||
|
- Step 4: Planning (MANDATORY) — `/plan` command, persistence, hooks.
|
||||||
|
|
@ -22,7 +22,15 @@
|
||||||
"Bash(uv run:*)",
|
"Bash(uv run:*)",
|
||||||
"Bash(uv sync:*)",
|
"Bash(uv sync:*)",
|
||||||
"Bash(docker compose:*)",
|
"Bash(docker compose:*)",
|
||||||
"Bash(docker-compose:*)"
|
"Bash(docker-compose:*)",
|
||||||
|
"Bash(gh issue view:*)",
|
||||||
|
"Bash(gh issue list:*)",
|
||||||
|
"Bash(gh issue edit:*)",
|
||||||
|
"Bash(gh issue comment:*)",
|
||||||
|
"Bash(gh repo view:*)",
|
||||||
|
"Bash(gh pr view:*)",
|
||||||
|
"Bash(gh pr list:*)",
|
||||||
|
"Bash(gh auth status:*)"
|
||||||
],
|
],
|
||||||
"deny": [
|
"deny": [
|
||||||
"Read(*/.env*)",
|
"Read(*/.env*)",
|
||||||
|
|
|
||||||
|
|
@ -49,6 +49,11 @@ htmlcov/
|
||||||
.claude/.credentials.json
|
.claude/.credentials.json
|
||||||
.codegraph/
|
.codegraph/
|
||||||
|
|
||||||
|
# Ticket workspace — local cache of in-progress tickets
|
||||||
|
.ticket/*
|
||||||
|
!.ticket/repo.md
|
||||||
|
!.ticket/.gitkeep
|
||||||
|
|
||||||
# 文档与测试程序
|
# 文档与测试程序
|
||||||
mydoc/
|
mydoc/
|
||||||
mytest/
|
mytest/
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
# GitHub Issue Tracker for this Project
|
||||||
|
|
||||||
|
**Repo (owner/name):** `salestech-group/MiroFish`
|
||||||
|
**URL:** https://github.com/salestech-group/MiroFish
|
||||||
|
|
||||||
|
This file is the equivalent of "board.md" for the GitHub-based ticket
|
||||||
|
flow: it tells `/ticket` and `/ticket-list` which repository to query.
|
||||||
|
Update if the project moves.
|
||||||
Loading…
Reference in New Issue