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 sync:*)",
|
||||
"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": [
|
||||
"Read(*/.env*)",
|
||||
|
|
|
|||
|
|
@ -49,6 +49,11 @@ htmlcov/
|
|||
.claude/.credentials.json
|
||||
.codegraph/
|
||||
|
||||
# Ticket workspace — local cache of in-progress tickets
|
||||
.ticket/*
|
||||
!.ticket/repo.md
|
||||
!.ticket/.gitkeep
|
||||
|
||||
# 文档与测试程序
|
||||
mydoc/
|
||||
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