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:
Dominik Seemann 2026-05-06 17:55:42 +02:00
parent 76f719e760
commit 52f78d9fe3
7 changed files with 229 additions and 1 deletions

View File

@ -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.

View File

@ -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).

View File

@ -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.

View File

@ -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*)",

5
.gitignore vendored
View File

@ -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
.ticket/.gitkeep Normal file
View File

8
.ticket/repo.md Normal file
View File

@ -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.