Add /issue-to-spec and /sync-issue Claude Code skills
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
101
.claude/commands/issue-to-spec.md
Normal file
101
.claude/commands/issue-to-spec.md
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
---
|
||||||
|
description: Fetch a Gitea issue and feed it into /speckit.specify as the feature description.
|
||||||
|
handoffs:
|
||||||
|
- label: Start speccing from this issue
|
||||||
|
agent: speckit.specify
|
||||||
|
prompt: "Create a spec from this issue"
|
||||||
|
send: true
|
||||||
|
---
|
||||||
|
|
||||||
|
## User Input
|
||||||
|
|
||||||
|
```text
|
||||||
|
$ARGUMENTS
|
||||||
|
```
|
||||||
|
|
||||||
|
You **MUST** provide an issue number as the argument (e.g. `/issue-to-spec 42`). If `$ARGUMENTS` is empty or not a valid number, ask the user for the issue number.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
1. Verify the `GITEA_TOKEN_ISSUES` environment variable is set:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
test -n "$GITEA_TOKEN_ISSUES" && echo "TOKEN_OK" || echo "TOKEN_MISSING"
|
||||||
|
```
|
||||||
|
|
||||||
|
If missing, tell the user to set it:
|
||||||
|
```
|
||||||
|
export GITEA_TOKEN_ISSUES="your-gitea-personal-access-token"
|
||||||
|
```
|
||||||
|
Then abort.
|
||||||
|
|
||||||
|
2. Parse the git remote to extract the Gitea API base URL, owner, and repo:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git config --get remote.origin.url
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected format: `ssh://git@<host>:<port>/<owner>/<repo>.git` or `https://<host>/<owner>/<repo>.git`
|
||||||
|
|
||||||
|
Extract:
|
||||||
|
- `GITEA_HOST` — the hostname
|
||||||
|
- `OWNER` — the repo owner/org
|
||||||
|
- `REPO` — the repo name (strip `.git` suffix)
|
||||||
|
- `API_BASE` — `https://<GITEA_HOST>/api/v1`
|
||||||
|
|
||||||
|
## Execution
|
||||||
|
|
||||||
|
### Step 1 — Fetch the issue
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -sf -H "Authorization: token $GITEA_TOKEN_ISSUES" "$API_BASE/repos/$OWNER/$REPO/issues/<NUMBER>"
|
||||||
|
```
|
||||||
|
|
||||||
|
Extract from the JSON response:
|
||||||
|
- `title` — the issue title
|
||||||
|
- `body` — the issue body (markdown)
|
||||||
|
- `labels` — array of label names (if any)
|
||||||
|
|
||||||
|
If the API call fails or returns no issue, abort with a clear error.
|
||||||
|
|
||||||
|
### Step 2 — Fetch issue comments (if any)
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -sf -H "Authorization: token $GITEA_TOKEN_ISSUES" "$API_BASE/repos/$OWNER/$REPO/issues/<NUMBER>/comments"
|
||||||
|
```
|
||||||
|
|
||||||
|
If comments exist, append them to the context (they may contain clarifications or additional requirements discussed after the issue was created).
|
||||||
|
|
||||||
|
### Step 3 — Compose the feature description
|
||||||
|
|
||||||
|
Format the issue content into a feature description suitable for `/speckit.specify`:
|
||||||
|
|
||||||
|
```
|
||||||
|
<Issue Title>
|
||||||
|
|
||||||
|
<Issue Body>
|
||||||
|
|
||||||
|
<If comments exist:>
|
||||||
|
---
|
||||||
|
Additional context from issue comments:
|
||||||
|
<Comment 1 by @author>: <comment body>
|
||||||
|
<Comment 2 by @author>: <comment body>
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 4 — Report and hand off
|
||||||
|
|
||||||
|
Display a summary:
|
||||||
|
- Issue number and title
|
||||||
|
- Number of comments included
|
||||||
|
- The composed feature description
|
||||||
|
|
||||||
|
Then hand off to `/speckit.specify` with the composed feature description as input. The handoff button in the UI will allow the user to proceed.
|
||||||
|
|
||||||
|
## Behavior Rules
|
||||||
|
|
||||||
|
- Never modify the issue on Gitea — this is a read-only operation.
|
||||||
|
- Include comment authors in the context so `/speckit.specify` can attribute requirements.
|
||||||
|
- If the issue body is empty, warn the user but still proceed with just the title.
|
||||||
|
- Strip HTML tags from the body/comments if present (Gitea sometimes includes rendered HTML).
|
||||||
|
- Use `curl` for all API calls — do not rely on `gh` CLI.
|
||||||
153
.claude/commands/sync-issue.md
Normal file
153
.claude/commands/sync-issue.md
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
---
|
||||||
|
description: Update a Gitea issue with business-level acceptance criteria extracted from the feature spec's user stories.
|
||||||
|
---
|
||||||
|
|
||||||
|
## User Input
|
||||||
|
|
||||||
|
```text
|
||||||
|
$ARGUMENTS
|
||||||
|
```
|
||||||
|
|
||||||
|
You **MUST** provide an issue number as the argument (e.g. `/sync-issue 42`). If `$ARGUMENTS` is empty or not a valid number, ask the user for the issue number.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
1. Verify the `GITEA_TOKEN_ISSUES` environment variable is set:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
test -n "$GITEA_TOKEN_ISSUES" && echo "TOKEN_OK" || echo "TOKEN_MISSING"
|
||||||
|
```
|
||||||
|
|
||||||
|
If missing, tell the user to set it:
|
||||||
|
```
|
||||||
|
export GITEA_TOKEN_ISSUES="your-gitea-personal-access-token"
|
||||||
|
```
|
||||||
|
Then abort.
|
||||||
|
|
||||||
|
2. Parse the git remote to extract the Gitea API base URL, owner, and repo:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git config --get remote.origin.url
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected format: `ssh://git@<host>:<port>/<owner>/<repo>.git` or `https://<host>/<owner>/<repo>.git`
|
||||||
|
|
||||||
|
Extract:
|
||||||
|
- `GITEA_HOST` — the hostname
|
||||||
|
- `OWNER` — the repo owner/org
|
||||||
|
- `REPO` — the repo name (strip `.git` suffix)
|
||||||
|
- `API_BASE` — `https://<GITEA_HOST>/api/v1`
|
||||||
|
|
||||||
|
3. Locate the spec file. Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
.specify/scripts/bash/check-prerequisites.sh --json --paths-only
|
||||||
|
```
|
||||||
|
|
||||||
|
Parse `FEATURE_SPEC` from the output. If it fails, ask the user to ensure they're on a feature branch with a spec. For single quotes in args, use escape syntax: e.g `'I'\''m Groot'` (or double-quote if possible: `"I'm Groot"`).
|
||||||
|
|
||||||
|
## Execution
|
||||||
|
|
||||||
|
### Step 1 — Read the spec
|
||||||
|
|
||||||
|
Load the spec file at `FEATURE_SPEC`. Parse the **User Scenarios & Testing** section, specifically:
|
||||||
|
|
||||||
|
- Each `### User Story N - [Title]` block
|
||||||
|
- The **Acceptance Scenarios** numbered list within each story (Given/When/Then format)
|
||||||
|
- The **Edge Cases** section
|
||||||
|
|
||||||
|
### Step 2 — Condense into business-level acceptance criteria
|
||||||
|
|
||||||
|
For each user story, extract the acceptance scenarios and rewrite them as concise, business-level checkbox items. Group by user story title.
|
||||||
|
|
||||||
|
**Transformation rules:**
|
||||||
|
- Strip Given/When/Then syntax — write as plain outcomes
|
||||||
|
- Remove implementation details (API names, database references, component names, file paths, config values, tool names)
|
||||||
|
- Focus on what the user **can do** or **can see**
|
||||||
|
- Keep each item to one line
|
||||||
|
- Preserve the grouping by user story for readability
|
||||||
|
|
||||||
|
**Example transformation:**
|
||||||
|
|
||||||
|
Input (from spec):
|
||||||
|
```
|
||||||
|
**Given** no sources are cached, **When** the user clicks the import button in the top bar,
|
||||||
|
**Then** the stat block side panel opens showing a descriptive explanation, an editable
|
||||||
|
pre-filled base URL, and a "Load All" button.
|
||||||
|
```
|
||||||
|
|
||||||
|
Output (for issue):
|
||||||
|
```
|
||||||
|
- [ ] Clicking the import button opens a panel with a description, editable URL, and "Load All" button
|
||||||
|
```
|
||||||
|
|
||||||
|
Also include edge cases as a separate group if they describe user-facing behavior.
|
||||||
|
|
||||||
|
### Step 3 — Fetch the existing issue
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -sf -H "Authorization: token $GITEA_TOKEN_ISSUES" "$API_BASE/repos/$OWNER/$REPO/issues/<NUMBER>"
|
||||||
|
```
|
||||||
|
|
||||||
|
Extract the current `body` from the response.
|
||||||
|
|
||||||
|
### Step 4 — Update the issue body
|
||||||
|
|
||||||
|
Merge the acceptance criteria into the existing issue body:
|
||||||
|
|
||||||
|
- If the body already has an `## Acceptance Criteria` section, **replace** its contents (everything between `## Acceptance Criteria` and the next `##` heading or end of body).
|
||||||
|
- If the body does not have an `## Acceptance Criteria` section, insert it after the `## Summary` section (or at the end if no Summary exists).
|
||||||
|
|
||||||
|
Preserve all other sections of the issue body unchanged.
|
||||||
|
|
||||||
|
The acceptance criteria section should look like:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## Acceptance Criteria
|
||||||
|
|
||||||
|
### <User Story 1 Title>
|
||||||
|
- [ ] <criterion from acceptance scenario>
|
||||||
|
- [ ] <criterion from acceptance scenario>
|
||||||
|
|
||||||
|
### <User Story 2 Title>
|
||||||
|
- [ ] <criterion from acceptance scenario>
|
||||||
|
|
||||||
|
### Edge Cases
|
||||||
|
- [ ] <edge case behavior>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Step 5 — Preview and confirm
|
||||||
|
|
||||||
|
Show the user:
|
||||||
|
- The full updated issue body
|
||||||
|
- A diff summary of what changed (sections added/replaced)
|
||||||
|
|
||||||
|
Ask for confirmation before updating.
|
||||||
|
|
||||||
|
### Step 6 — Push the update
|
||||||
|
|
||||||
|
On confirmation:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
curl -sf -X PATCH \
|
||||||
|
-H "Authorization: token $GITEA_TOKEN_ISSUES" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
"$API_BASE/repos/$OWNER/$REPO/issues/<NUMBER>" \
|
||||||
|
-d @- <<'PAYLOAD'
|
||||||
|
{
|
||||||
|
"body": "<updated body>"
|
||||||
|
}
|
||||||
|
PAYLOAD
|
||||||
|
```
|
||||||
|
|
||||||
|
Report success with the issue URL.
|
||||||
|
|
||||||
|
## Behavior Rules
|
||||||
|
|
||||||
|
- Never modify the issue title, labels, milestone, or assignees — only the body.
|
||||||
|
- Always preview before updating — never push without user confirmation.
|
||||||
|
- If the spec has no user stories or acceptance scenarios, abort with a clear message suggesting the user run `/speckit.specify` first.
|
||||||
|
- Acceptance criteria must be business-level. If you find yourself writing implementation details, rewrite at a higher level of abstraction.
|
||||||
|
- Use `curl` for all API calls — do not rely on `gh` CLI.
|
||||||
|
- Always use HEREDOC for the JSON payload to handle special characters in the body.
|
||||||
|
- Escape double quotes and newlines properly in the JSON body.
|
||||||
Reference in New Issue
Block a user