Skip to content

Commit c5a42cf

Browse files
committed
refactor(add-keyboard): rewrite command to use gh CLI with monorepo and archive handling
1 parent 8faa474 commit c5a42cf

1 file changed

Lines changed: 92 additions & 27 deletions

File tree

.claude/commands/add-keyboard.md

Lines changed: 92 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,44 +15,107 @@ GitHub URL: $ARGUMENTS
1515

1616
## Instructions
1717

18-
### Step 1: Validate URL
18+
### Step 1: Validate and Normalize URL
1919

20-
Verify the URL is a valid GitHub repository URL (github.com/owner/repo format).
21-
If invalid, inform the user and stop.
20+
Normalize the URL before doing anything else:
21+
22+
- Strip trailing slashes
23+
- Remove `www.`
24+
- Strip any path suffix beyond `owner/repo` (e.g. `/tree/main`, `/blob/...`, `#readme`)
25+
- Result must match `https://github.com/{owner}/{repo}`
26+
27+
Extract `owner` and `repo`. If the URL is not a valid GitHub repo URL, inform the user and stop.
2228

2329
### Step 2: Check for Duplicates
2430

25-
Use Grep to search for the URL in `src/content/keyboards/**/*.md`.
31+
Use Grep to search for the normalized URL in `src/content/keyboards/**/*.md`.
2632
If the keyboard already exists, inform the user and ask how to proceed.
2733

28-
### Step 3: Fetch Repository Data
34+
### Step 3: Fetch Repository Data via gh CLI
35+
36+
Run these two calls in parallel with Bash:
37+
38+
**3a. Repository metadata** (includes archive/activity status):
2939

30-
Use WebFetch once to retrieve all needed information from `$ARGUMENTS`:
40+
```bash
41+
gh repo view owner/repo --json name,description,repositoryTopics,url,defaultBranchRef,isArchived,pushedAt
42+
```
43+
44+
**3b. Root README content:**
45+
46+
```bash
47+
gh api repos/owner/repo/readme --jq '.content' | base64 -d
48+
```
3149

32-
**Extract from the GitHub repository page and README**:
50+
If `gh` is not available or the calls fail (e.g., private repo without access), fall back to `WebFetch` on the GitHub URL.
51+
52+
**Archive/activity check**: If `isArchived` is `true`, or `pushedAt` is more than 3 years ago, warn the user:
53+
> "⚠ This repo appears inactive (last push: {date} / archived). Continue anyway?"
54+
Use AskUserQuestion with Yes/Cancel options before proceeding.
55+
56+
**Monorepo handling**: If the root README is sparse (under 200 characters or contains no keyboard-related keywords), check for a nested README by searching common subdirectory patterns:
57+
58+
```bash
59+
gh api repos/owner/repo/contents | jq '[.[] | select(.type=="dir") | .name]'
60+
```
61+
62+
If a relevant subdirectory exists (e.g., matches the repo name, or contains `keyboard`/`pcb`/`case`), fetch its README too:
63+
64+
```bash
65+
gh api repos/owner/repo/contents/{subdir}/README.md --jq '.content' | base64 -d
66+
```
67+
68+
Use whichever README has richer content.
69+
70+
**Extract from the results**:
3371

3472
- Repository name (clean it up: replace hyphens/underscores with spaces, title case)
35-
- Repository description from the About section
36-
- GitHub topics/tags
37-
- First significant image URL from README (look for `![` markdown or `<img` tags showing the keyboard)
73+
- Repository description from the `description` field
74+
- Topics from the `repositoryTopics` array (each topic has a `topic.name`)
75+
- Up to 3 candidate image URLs from the README (see image selection below)
3876
- Raw mentions of: QMK, ZMK, Vial, KiCad, PCB, STL, 3MF, case files, key counts (40%, 60%, 65%, 75%, TKL, 3x5, 4x6), layouts (Alice, Arisu), switch types (Choc, MX, low profile, hotswap), controllers (Pro Micro, RP2040, STM32, Elite-C, nice!nano, ATmega32U4, nRF52840, integrated controller), features (wireless, bluetooth, USB-C, rotary encoder, RGB, underglow, OLED, screen, e-ink, Cirque, Azoteq, trackball, tenting, TRRS, through hole, handwiring, reversible, 3D printed)
3977

40-
Return this information in a structured format.
78+
**Image selection**: Scan the README for all `![...](...) ` markdown images and `<img src="...">` tags. Collect up to 3 candidates, filtering out badges (shields.io, badge URLs, tiny images) and favoring `.jpg`, `.png`, `.gif`, `.webp`. Resolve relative paths to raw GitHub URLs using the default branch:
79+
`https://raw.githubusercontent.com/owner/repo/{branch}/{path}`
80+
81+
### Step 4: Select Image
82+
83+
If more than one image candidate was found, present them to the user:
84+
85+
```yaml
86+
Question: "Which image best shows the keyboard?"
87+
Header: "Select image"
88+
Options:
89+
- 1: [URL of candidate 1]
90+
- 2: [URL of candidate 2]
91+
- 3: [URL of candidate 3] # if available
92+
- None: Skip image
93+
```
94+
95+
If only one candidate was found, use it without asking. If none were found, omit the `image` field.
4196

42-
### Step 4: Generate Tags
97+
### Step 5: Generate Tags
4398

44-
Generate tags from the fetched data by normalizing raw mentions to canonical tags.
99+
Scan existing keyboard entries to see what tags are already in use, so new tags stay consistent:
100+
101+
```bash
102+
grep -h "^tags:" src/content/keyboards/**/*.md | sort | uniq -c | sort -rn | head -40
103+
```
104+
105+
Generate tags from the fetched data by normalizing raw mentions to canonical tags. Cross-reference against the existing tag list — prefer an existing tag spelling over inventing a new variant.
106+
107+
Also include any relevant GitHub topics from Step 3a.
45108

46109
**Tag normalization rules**:
47110

48-
- `qmk`, `zmk`, `vial` - use as-is (lowercase)
49-
- `pcb` - if KiCad or PCB mentioned
50-
- `case` - if STL, 3MF, or case files mentioned
51-
- `40%`, `60%`, `65%`, `75%`, `tkl`, `3x5`, `4x6` - use as-is (lowercase)
52-
- `alice` - use for both Alice and Arisu layouts
53-
- `choc`, `mx`, `low profile`, `hotswap` - use as-is (lowercase)
54-
- `pro micro`, `rp2040`, `stm32`, `elite-c`, `nice!nano`, `atmega32u4`, `nrf52840` - use as-is (lowercase)
55-
- `wireless`, `bluetooth`, `usb-c`, `rotary encoder`, `per-key rgb`, `underglow`, `tenting puck`, `trrs`, `through hole`, `handwiring`, `reversible`, `3d printed`, `trackball` - use as-is (lowercase)
111+
- `qmk`, `zmk`, `vial` use as-is (lowercase)
112+
- `pcb` if KiCad or PCB mentioned
113+
- `case` if STL, 3MF, or case files mentioned
114+
- `40%`, `60%`, `65%`, `75%`, `tkl`, `3x5`, `4x6` use as-is (lowercase)
115+
- `alice` use for both Alice and Arisu layouts
116+
- `choc`, `mx`, `low profile`, `hotswap` use as-is (lowercase)
117+
- `pro micro`, `rp2040`, `stm32`, `elite-c`, `nice!nano`, `atmega32u4`, `nrf52840` use as-is (lowercase)
118+
- `wireless`, `bluetooth`, `usb-c`, `rotary encoder`, `per-key rgb`, `underglow`, `tenting puck`, `trrs`, `through hole`, `handwiring`, `reversible`, `3d printed`, `trackball` use as-is (lowercase)
56119

57120
**Aliases** (convert raw mentions to these canonical tags):
58121

@@ -63,7 +126,7 @@ Generate tags from the fetched data by normalizing raw mentions to canonical tag
63126

64127
Format as comma-separated string (e.g., "split, choc, pcb, case, display").
65128

66-
### Step 5: Determine Category
129+
### Step 6: Determine Category
67130

68131
Use AskUserQuestion to let the user select the category:
69132

@@ -77,17 +140,17 @@ Options:
77140
- other: Numpad, macropad, or unique design
78141
```
79142

80-
### Step 6: Generate Description
143+
### Step 7: Generate Description
81144

82145
Create a concise description (max 500 characters) from:
83146

84147
- Repository description
85148
- First paragraph of README
86149
- Key features detected
87150

88-
The description should explain what makes this keyboard interesting or unique.
151+
The description should explain what makes this keyboard interesting or unique. If none of the above yield meaningful content, omit the `description` field rather than writing a generic filler sentence.
89152

90-
### Step 7: Preview Entry
153+
### Step 8: Preview Entry
91154

92155
Show the user a concise preview:
93156

@@ -111,7 +174,7 @@ Options:
111174

112175
If "Let me edit" is selected, ask which field to modify and get the new value.
113176

114-
### Step 8: Create the File
177+
### Step 9: Create the File
115178

116179
Generate a filename slug from the keyboard name (lowercase, hyphens, no special chars).
117180

@@ -128,7 +191,9 @@ image: "[image URL]"
128191
---
129192
```
130193

131-
### Step 9: Confirm Success
194+
Omit `description` and/or `image` if they have no value.
195+
196+
### Step 10: Confirm Success
132197

133198
Tell the user:
134199

0 commit comments

Comments
 (0)