Skip to content

Commit c8afb88

Browse files
Merge pull request #480 from BootNodeDev/release/v2.5.0
release: v2.5.0
2 parents 06550e6 + f7d0de0 commit c8afb88

142 files changed

Lines changed: 8991 additions & 5151 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/skills/create-pr/SKILL.md

Lines changed: 203 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,203 @@
1+
---
2+
name: sdlc:create-pr
3+
description: Use when creating a pull request -- reads the PR template, auto-fills from git context and linked issue, confirms with the user, then creates via gh CLI.
4+
---
5+
6+
# /sdlc:create-pr
7+
8+
Create a well-structured GitHub pull request by reading the repo's PR template and filling it from context.
9+
10+
**Core principle:** Templates own the format. Context owns the content. User owns the final word.
11+
12+
## Template Location
13+
14+
Read `.github/PULL_REQUEST_TEMPLATE.md` relative to the project root on every invocation. This path is fixed -- do not search for it.
15+
16+
## Core Pattern
17+
18+
1. **Gather** -- Collect all inputs silently. Ask only when auto-derivation fails.
19+
2. **Draft** -- Fill every template section from the gathered context.
20+
3. **Confirm** -- Show the full draft. Wait for explicit approval. Iterate.
21+
4. **Create** -- Run `gh pr create` with `--body-file`. Report the URL.
22+
23+
## Step 1: Gather
24+
25+
**Auto-derive (no user interaction):**
26+
27+
- Read `.github/PULL_REQUEST_TEMPLATE.md`
28+
- Run `git diff <base>...HEAD` -- the branch diff
29+
- Run `git log <base>..HEAD --oneline` -- the commit history
30+
- Extract issue number from branch name (pattern: `type/NNN-description`, e.g., `feat/17-add-skill``#17`)
31+
- If issue number found, run `gh issue view NNN --json title,body,labels` and extract acceptance criteria from the body
32+
33+
**Ask when needed (use multiple-choice where possible):**
34+
35+
| Question | When | Options |
36+
|----------|------|---------|
37+
| "Which issue does this PR close?" | Branch name has no issue number | List of recent open issues (via `gh issue list --state open --limit 5 --json number,title`) + "None" + Other |
38+
| "PR type?" | Always; pre-select "Ready for review" | "Ready for review" / "Draft" |
39+
| "Base branch?" | Always; **must be asked even when auto-detected** -- the auto-detected value is the pre-selected default, not a reason to skip | Branch we branched from (auto-detected via `git merge-base` against known remote branches; pre-selected as default) / `develop` (if present in remote) / `main` / Other |
40+
| "Who should review this PR?" | Always; multi-select | All reviewers returned by script (see below), in order, plus "Other" as the last option |
41+
| "Who should this PR be assigned to?" | Always; pre-select "Me" | "Me" (resolved via `gh api user --jq '.login'`) / "Nobody" (default if "Me" feels presumptuous) / Other |
42+
| "Which checklist items have you completed?" | Always; multi-select; zero selections is valid (means none completed yet) | "Self-reviewed my own diff" / "Tests added or updated" / "Docs updated (if applicable)" / "No unrelated changes bundled in" |
43+
| "Will you add screenshots to this PR?" | Always | "Yes, I'll add them after creation" / "No" (default) |
44+
45+
### Helper scripts
46+
47+
**IMPORTANT:** Do NOT run `bash .claude/skills/create-pr/*.sh` directly -- that path only works for project-local installs. Always use the commands below, which resolve the script location first.
48+
49+
Auto-detect base branch:
50+
51+
```bash
52+
if [[ -f .claude/skills/create-pr/get-base-branch.sh ]]; then bash .claude/skills/create-pr/get-base-branch.sh; elif [[ -f "$HOME/.claude/skills/create-pr/get-base-branch.sh" ]]; then bash "$HOME/.claude/skills/create-pr/get-base-branch.sh"; fi
53+
```
54+
55+
It outputs the branch name (e.g., `main`, `develop`) whose merge-base with HEAD is most recent -- i.e., the branch we most likely forked from. Present it as the pre-selected default in the base branch question.
56+
57+
Fetch recent reviewers:
58+
59+
```bash
60+
if [[ -f .claude/skills/create-pr/get-reviewers.sh ]]; then bash .claude/skills/create-pr/get-reviewers.sh; elif [[ -f "$HOME/.claude/skills/create-pr/get-reviewers.sh" ]]; then bash "$HOME/.claude/skills/create-pr/get-reviewers.sh"; fi
61+
```
62+
63+
It outputs up to 4 reviewer logins, one per line, ordered most-recent-first (excludes the current user; falls back to alphabetical collaborators for new repos). Show every login the script returns as an option, in the exact order returned. Add "Other" as the last option. Do not add a "Skip" or "None" option -- if the user wants no reviewers, they select only "Other" and leave it empty.
64+
65+
Do not add labels to the PR. Labels are managed separately.
66+
67+
## Step 2: Draft
68+
69+
### PR Title
70+
71+
Conventional commit format: `type(scope): subject` or `type: subject`.
72+
73+
Allowed types: `feat`, `fix`, `docs`, `test`, `ci`, `refactor`, `perf`, `chore`, `revert`, `wip`, `build`, `style`, `release`.
74+
75+
<!-- Standard Conventional Commits prefixes only, matching the types documented in CLAUDE.md. Projects adopting this starter kit can extend this list to suit their conventions. -->
76+
77+
- Derive from branch name and commit history
78+
- Scope is optional
79+
- Subject: lowercase, imperative mood, no trailing period
80+
81+
### PR Body
82+
83+
Fill every section in template order. Strip all HTML comments (`<!-- ... -->`).
84+
85+
#### Summary
86+
87+
First line: `Closes #`
88+
89+
If no linked issue, first line: `No related issue. <one sentence motivation>`
90+
91+
Followed by 1-3 sentences synthesizing commits and issue description, focused on *why* not *what*. If no issue is linked, derive from commits and branch name only.
92+
93+
#### Changes
94+
95+
Bullet list. Each bullet = one discrete change from the diff or commit messages.
96+
97+
#### Acceptance criteria
98+
99+
- **Issue has AC:** Mirror as checkboxes. Check off items the diff demonstrates are fulfilled.
100+
- **Issue has no AC, or no issue:** Suggest AC based on the changes made. Present as unchecked checkboxes.
101+
- **AC diverged from issue:** Note it explicitly. Example: "Note: criterion X was moved to #M" or "Added: Y discovered during implementation."
102+
103+
#### Test plan
104+
105+
Two subsections, always present:
106+
107+
##### Automated tests
108+
List test files added/modified in the diff and the command to run them. If none: `No automated tests added.`
109+
110+
##### Manual verification
111+
If the change has user-facing or integration behavior, list manual steps. If purely internal: `No manual steps required.`
112+
113+
#### Breaking changes
114+
115+
If breaking changes detected (API changes, removed exports, schema changes): describe what breaks and migration steps.
116+
117+
If none: `None.`
118+
119+
#### Checklist
120+
121+
Render all four items based on the user's selections from the Gather step:
122+
123+
- Items selected by the user → `- [x] <item>`
124+
- Items not selected → `- [ ] <item>`
125+
126+
The four items, in order:
127+
128+
1. Self-reviewed my own diff
129+
2. Tests added or updated
130+
3. Docs updated (if applicable)
131+
4. No unrelated changes bundled in
132+
133+
#### Screenshots
134+
135+
Based on the Step 1 answer:
136+
137+
- "Yes": `To be added after PR creation.` (remind user to attach via GitHub UI)
138+
- "No" (default): `None.`
139+
140+
**The section is always present.**
141+
142+
## Step 3: Confirm
143+
144+
Show to the user:
145+
- PR title
146+
- Complete body (all sections, no HTML comments)
147+
- Target base branch
148+
- The exact `gh pr create` command that will run
149+
150+
Wait for explicit approval. Accept edits to any section. Loop until approved.
151+
152+
## Step 4: Create
153+
154+
```bash
155+
BODY_FILE=$(mktemp /tmp/gh_pr_body_XXXXXX)
156+
157+
# Replace the placeholder below with the actual drafted PR body:
158+
cat > "$BODY_FILE" << 'EOF'
159+
{{PR_BODY}}
160+
EOF
161+
162+
gh pr create \
163+
--title "<title>" \
164+
--base "<base-branch>" \
165+
--body-file "$BODY_FILE" \
166+
[--reviewer <handle> ...] \
167+
[--assignee <handle>]
168+
```
169+
170+
Add `--draft` if user selected "Draft" in Step 1. Add one `--reviewer <handle>` flag per reviewer selected in Step 1; if "Other" was selected, use the handle the user provided. Add `--assignee <handle>` using the resolved login if "Me" or "Other" was selected; omit if "Nobody".
171+
172+
After reporting the PR URL: if the user selected "Yes" for screenshots in Step 1, remind them to attach screenshots via the GitHub UI.
173+
174+
## Edge Cases
175+
176+
### Branch is behind base
177+
Present options:
178+
1. **Continue as-is** -- create the PR and note it's behind
179+
2. **Rebase onto base** -- run `git rebase <base>`; if conflicts, help resolve
180+
3. **Merge base in** -- run `git merge <base>`; if conflicts, help resolve
181+
4. **Abort** -- stop; do not create the PR
182+
183+
### No commits ahead of base
184+
Stop. "No commits ahead of `<base>`. Nothing to create a PR from."
185+
186+
## Common Mistakes
187+
188+
- **Reconstructing the template from memory** -- read `.github/PULL_REQUEST_TEMPLATE.md` every time.
189+
- **Generating an ad-hoc format** -- every section from the template must appear, in template order.
190+
- **Creating before confirmation** -- never run `gh pr create` without explicit user approval.
191+
- **Leaving HTML comments** -- strip all `<!-- ... -->` from the output.
192+
- **Silently omitting `Closes #`** -- if no issue, say so explicitly on the first line.
193+
- **Deleting empty sections** -- Breaking changes and Screenshots are always present; use `None.`
194+
- **Ignoring AC divergence** -- note explicitly when PR criteria differ from the issue's.
195+
- **Skipping the base-branch question** -- always present it. Auto-detection provides the default, not the answer.
196+
197+
## Installation
198+
199+
This skill includes helper scripts alongside `SKILL.md`. When installing or updating, copy (or symlink) the **entire `create-pr/` directory** -- not just `SKILL.md`. All files in this directory are required:
200+
201+
- `SKILL.md` -- skill definition
202+
- `get-base-branch.sh` -- auto-detects the base branch
203+
- `get-reviewers.sh` -- fetches recent reviewer logins
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# Find the base branch: the most likely target for a pull request.
5+
#
6+
# Strategy: pick the remote branch whose merge-base with HEAD is most recent
7+
# (i.e., the branch we most likely forked from). This works even when the
8+
# base branch has advanced past the fork point.
9+
#
10+
# 1. Check well-known stable branches first (main, master, develop, staging).
11+
# Among those that exist, pick the one with the closest merge-base to HEAD.
12+
# 2. If none match, fall back to any remote branch with the closest merge-base.
13+
14+
current=$(git rev-parse --abbrev-ref HEAD)
15+
16+
# Priority 1: well-known base branches -- pick closest merge-base
17+
best_branch=""
18+
best_ts=0
19+
20+
for candidate in main master develop staging; do
21+
ref="origin/$candidate"
22+
git rev-parse --verify "$ref" >/dev/null 2>&1 || continue
23+
[[ "$candidate" == "$current" ]] && continue
24+
mb=$(git merge-base HEAD "$ref" 2>/dev/null) || continue
25+
ts=$(git log -1 --format=%ct "$mb" 2>/dev/null) || continue
26+
[[ -n "$ts" ]] || continue
27+
if (( ts > best_ts )); then
28+
best_ts=$ts
29+
best_branch=$candidate
30+
fi
31+
done
32+
33+
if [[ -n "$best_branch" ]]; then
34+
echo "$best_branch"
35+
exit 0
36+
fi
37+
38+
# Priority 2: any other remote branch, pick closest merge-base
39+
best_branch=""
40+
best_ts=0
41+
42+
while IFS= read -r ref; do
43+
branch="${ref#origin/}"
44+
[[ "$branch" == "HEAD" ]] && continue
45+
[[ "$branch" == "$current" ]] && continue
46+
mb=$(git merge-base HEAD "$ref" 2>/dev/null) || continue
47+
ts=$(git log -1 --format=%ct "$mb" 2>/dev/null) || continue
48+
[[ -n "$ts" ]] || continue
49+
if (( ts > best_ts )); then
50+
best_ts=$ts
51+
best_branch=$branch
52+
fi
53+
done < <(git for-each-ref --format='%(refname:short)' refs/remotes/origin/)
54+
55+
if [[ -z "$best_branch" ]]; then
56+
echo "No base branch found" >&2
57+
exit 1
58+
fi
59+
60+
echo "$best_branch"
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
me=$(gh api user --jq '.login' 2>/dev/null) || true
5+
6+
# Attempt: recent PR reviewers sorted by most-recent-first
7+
reviewers=$(
8+
gh pr list --state all --limit 20 --json reviews \
9+
--jq "
10+
[ .[].reviews[]
11+
| { login: .author.login, ts: .submittedAt }
12+
]
13+
| sort_by(.ts) | reverse
14+
| map(.login)
15+
| map(select(. != \"$me\"))
16+
| reduce .[] as \$x (
17+
{ seen: {}, out: [] };
18+
if .seen[\$x] then . else { seen: (.seen | .[\$x] = true), out: (.out + [\$x]) } end
19+
)
20+
| .out[:4]
21+
| .[]
22+
" 2>/dev/null || true
23+
)
24+
25+
if [[ -n "$reviewers" ]]; then
26+
echo "$reviewers"
27+
exit 0
28+
fi
29+
30+
# Fallback: collaborators (alphabetical, excluding self)
31+
repo=$(gh repo view --json nameWithOwner -q .nameWithOwner 2>/dev/null) || exit 0
32+
gh api "/repos/$repo/collaborators" \
33+
--jq "[ .[].login | select(. != \"$me\") ] | sort | .[:4] | .[]" \
34+
2>/dev/null || true

.claude/skills/issue/SKILL.md

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
---
2+
name: sdlc:issue
3+
description: Use when creating a GitHub issue from a brief -- bug, feature, epic, or spike -- against the repo's GitHub issue templates via gh CLI.
4+
---
5+
6+
# /sdlc:issue
7+
8+
Create a well-structured GitHub issue using the repo's own templates and `gh` CLI.
9+
10+
**Core principle:** Templates own the format. Skill owns the behavior.
11+
12+
## Core Pattern
13+
14+
1. **Classify** -- Determine type from brief: bug / feature / epic / spike. If unclear, ask once.
15+
2. **Read** -- Load `.github/ISSUE_TEMPLATE/<type>.yml`. Extract all fields, required vs optional, and label. Do this every time -- never reconstruct from memory.
16+
3. **Interview** -- Ask only for missing required fields. If the brief already covers a field, don't re-ask. Scale ceremony to issue weight.
17+
4. **Draft** -- Build title and body. Sections follow template field order using `label` as heading. Omit empty optional fields.
18+
5. **Confirm** -- Show full draft including labels. Wait for explicit approval. Iterate until approved.
19+
6. **Create** -- Write body to temp file, run `gh issue create` with all labels, report issue URL.
20+
21+
## Title Format
22+
23+
Issue titles must be **natural language, sentence case** (code terms and command names retain their canonical casing) -- no conventional commit prefixes, no scope tags.
24+
25+
Conventional commit format (`type(scope): subject`) is for **commits and PR titles only**. It is not appropriate for issue titles, which appear in GitHub's issue list and must be scannable at a glance.
26+
27+
**Good:**
28+
- `Issue skill defaults to conventional commit format for titles`
29+
- `mktemp fails with .md suffix`
30+
- `Add natural language title guidance to issue skill`
31+
32+
**Bad:**
33+
- `fix(skills): mktemp fails with .md suffix`
34+
- `fix: issue skill defaults to conventional commit format`
35+
- `feat(issue): add title guidance`
36+
37+
Rule: if a reader has to mentally strip a prefix to understand the title, the title is wrong.
38+
39+
## Template Map
40+
41+
| Type | File | Type label | Additional labels |
42+
|---------|-----------------|---------------|---------------------------|
43+
| Bug | `1-bug.yml` | `bug` | `priority: <level>` |
44+
| Feature | `2-feature.yml` | `enhancement` | `priority: <level>` |
45+
| Epic | `3-epic.yml` | `epic` | `priority: <level>` |
46+
| Spike | `4-spike.yml` | `spike` | -- |
47+
48+
## Labels
49+
50+
Priority is applied as a label, not a form dropdown. See the Label Conventions section in `CLAUDE.md` for the full table and descriptions.
51+
52+
- Bugs, features, and epics each get a `priority: <level>` label.
53+
- Spikes don't carry priority.
54+
- If the brief doesn't specify a level, ask once using a numbered list -- never default silently:
55+
56+
1. Critical
57+
2. High
58+
3. Medium
59+
4. Low
60+
61+
## gh Command
62+
63+
```bash
64+
BODY_FILE=$(mktemp /tmp/gh_issue_body_XXXXXX)
65+
66+
cat > "$BODY_FILE" << 'EOF'
67+
<body>
68+
EOF
69+
70+
gh issue create \
71+
--title "<title>" \
72+
--label "<type-label>" \
73+
# omit the next label for spikes
74+
--label "<priority-label>" \
75+
--body-file "$BODY_FILE"
76+
```
77+
78+
Multiple `--label` flags can be chained. The type label is always present. The priority label is added for bugs, features, and epics -- omit it for spikes.
79+
80+
Optional flags: `--assignee "<username>"`, `--milestone "<name>"`, `--project "<name>"`
81+
82+
## Common Mistakes
83+
84+
- **Skipping the template read** -- Field names and order come from the YAML, not assumptions. Read it every time.
85+
- **Pre-emptively asking for optional fields** -- Required fields are the floor. Let the user volunteer the rest.
86+
- **Creating before confirmation** -- Never run `gh` without explicit approval. Always show the full draft first.
87+
- **Omitting priority labels** -- Form dropdowns do not survive `gh` CLI creation. Always apply these as labels.

0 commit comments

Comments
 (0)