From inki
Rewrites PR titles or descriptions on strapi/documentation repos to match project git-rules. Used via `/pr-fix` with explicit action and optional PR identifiers or auto-detection of recent PRs.
How this skill is triggered — by the user, by Claude, or both
Slash command
/inki:pr-fix <title|description|body> [--yes|-y] [--include-old] [PR# or URL] [PR# or URL] ...<title|description|body> [--yes|-y] [--include-old] [PR# or URL] [PR# or URL] ...The summary Claude sees in its skill listing — used to decide when to auto-load this skill
**Scope:** designed for strapi/documentation (and its forks).
Scope: designed for strapi/documentation (and its forks).
$ARGUMENTS is <action> [flags] [PR identifiers...].
| Action | What it rewrites |
|---|---|
title | The PR title |
description | The PR body/description |
body | Alias of description (matches gh pr edit --body vocabulary) |
If no action is given, or if the first token is not one of the above, report the error and stop.
--yes or -y → AUTO=true (non-interactive: skip confirmation prompts)--include-old → INCLUDE_OLD=true (only meaningful when no PR identifiers are given; includes open PRs older than 30 days)Zero or more, each as a bare number (2143), a hashed number (#2143), or a full URL (https://github.com/strapi/documentation/pull/2143, with optional /files or /changes suffix).
If no PR identifiers are given:
gh pr list --repo strapi/documentation --state open \
--search "created:>=$(date -v-30d +%Y-%m-%d 2>/dev/null || date -d '30 days ago' +%Y-%m-%d)" \
--limit 100 --json number,title,body,author,createdAt
--include-old is set, drop the --search filter to list all open PRs.The 30-day cutoff uses createdAt (not updatedAt) because bot activity bumps updatedAt on otherwise stale PRs.
ACTION (must be title, description, or body). Normalize body → description internally.--yes/-y → AUTO=true. Remove from list.--include-old → INCLUDE_OLD=true. Remove from list.2143 → 2143#2143 → 2143https://github.com/strapi/documentation/pull/2143 → 2143https://github.com/strapi/documentation/pull/2143/files → 2143
Regex [0-9]+$ after stripping trailing path segments and a leading #.command -v gh >/dev/null || { echo "gh CLI required"; exit 1; }
Depending on ACTION:
title: read ../_shared/pr-title-rules.md and ../../references/git-rules.md (section "Pull Request Titles").description (and body): read ../_shared/pr-description-rules.md and ../../references/git-rules.md (the description section). Layer the strict strapi/documentation overrides on top:
##, no ###)For each PR number, fetch metadata:
gh pr view <num> --repo strapi/documentation --json number,title,body,author
Bucket each PR:
feat:/chore:/fix:/docs: (or any word(scope):) prefix, lowercase first word, vague wording (1-2 generic words like "Update docs"), missing verb or specific noun, ticket ID at start, emoji at start, length over 80 characters.
Missing "This PR ..." opener, contains ##/### heading, contains "Test plan" section, contains - [ ] checklist, contains boilerplate sections, empty body, body is just "Updated docs" type vagueness, missing the trailing "Direct preview link 👉 here" line (see Step 5 for the construction logic).
AUTO=trueCase A — PR identifiers were given explicitly
Edit immediately without further prompting:
ACTION=title: gh pr edit <num> --repo strapi/documentation --title "<proposed>"ACTION=description: write the proposed body to a tmpfile, then gh pr edit <num> --repo strapi/documentation --body-file <tmpfile>Announce each change inline:
PR #<num>: edited — "<proposed title>" (for title)
PR #<num>: edited (description rewritten) (for description)
Case B — No PR identifiers given (targets all recent open PRs)
A batch confirmation is required as a safety bracket. Display the full list of proposed edits first:
Review batch (--yes without PR IDs targets all recent open PRs):
| PR# | Author | Current | Proposed | Reason | (for title)
| PR# | Author | Reason | Preview of new description | (for description)
| ... | ... | ... | ... | ... |
Compliant (no changes needed): N PRs
Type `y` to apply all, `n` to cancel, or `s <PR#> <PR#> ...` to skip specific PRs and apply the rest.
Branch on the user's single response:
y → apply all listed edits.n → cancel entirely, edit nothing, exit.s <PR#> <PR#> ... → remove those PRs from the batch, then apply the remaining edits.AUTO=falseFor each non-compliant PR, display the proposal and wait for input.
For ACTION=title:
PR #<num> — author: @<login>
Current: <current title>
Proposed: <proposed title>
Reason: <short explanation>
[y]es / [n]o / [e]dit <new title> / [s]kip
Branch on response:
y → gh pr edit <num> --repo strapi/documentation --title "<proposed>"n or s → record as skippede <new title> → edit with the user-provided titleFor ACTION=description:
PR #<num> — author: @<login>
Title: <current title (for context, not edited here)>
--- Current description ---
<current body>
--- Proposed description ---
<proposed body>
Reason: <short explanation>
[y]es / [n]o / [e]dit / [s]kip
Branch:
y → write proposed body to tmpfile, gh pr edit <num> --repo strapi/documentation --body-file <tmpfile>n or s → record as skippede → open temp file with proposal pre-filled, let user edit, re-display, ask y/nThe proposed description MUST end with a Vercel preview link line. If the original description has one as its last line, preserve it. If it does not, build one and append it.
The link has two parts: the deployment host (https://documentation-git-<...>-strapijs.vercel.app) and the page path.
Do NOT naively build the host from the branch name. Vercel truncates long branch slugs to a ~63-character DNS label and appends a short hash (e.g. branch cms/move-mcp-plugin-api-to-plugins-dev deploys to documentation-git-cms-move-mcp-plugin-api-to-pl-704ff2-strapijs.vercel.app, NOT documentation-git-cms-move-mcp-plugin-api-to-plugins-dev-strapijs.vercel.app). A slug-built host for a long branch will fail to resolve.
Primary method, extract the real host from the Vercel bot comment:
gh pr view <num> --repo strapi/documentation --json comments \
-q '.comments[] | select(.author.login | test("vercel"; "i")) | .body' \
| grep -oE 'https://documentation-git-[a-z0-9-]+\.vercel\.app' | sort -u | head -1
This returns the exact deployed host, hash and all.
Fallback, build the slug from the branch name (only when no Vercel comment exists yet, e.g. a brand-new PR whose deployment has not been posted):
gh pr view <num> --repo strapi/documentation --json headRefName)./ with -: cms/mcp-server → cms-mcp-server.https://documentation-git-<branch-slug>-strapijs.vercel.app.gh pr view <num> --repo strapi/documentation --json files --jq '.files[].path'..md/.mdx files under docusaurus/docs/.cms/features/users-permissions.md over its sub-pages).docusaurus/docs/ prefix and the .md/.mdx extension: docusaurus/docs/cms/features/users-permissions.md → /cms/features/users-permissions..md/.mdx files are touched (rare for a docs PR), fall back to the root path (empty string).Assemble the line (<host> from step 1/2, <page-path> with its leading /):
Direct preview link 👉 [here](<host><page-path>)
Verify the URL resolves before proposing it:
curl -s -o /dev/null -w '%{http_code}' -L --max-time 25 "<url>"
A non-200 (or 000 host-resolution failure) means the host is wrong; re-derive from the Vercel comment (step 1) rather than the slug. Surface the status in the Reason line.
Append the line as the last line of the proposed description, separated from the body by a blank line.
cms/move-mcp-plugin-api-to-plugins-dev: the Vercel comment yields host https://documentation-git-cms-move-mcp-plugin-api-to-pl-704ff2-strapijs.vercel.app; added page cms/plugins-development/extend-mcp-server → https://documentation-git-cms-move-mcp-plugin-api-to-pl-704ff2-strapijs.vercel.app/cms/plugins-development/extend-mcp-serverrepo/contact-support-mention (slug fallback safe) + page cms/intro → https://documentation-git-repo-contact-support-mention-strapijs.vercel.app/cms/introReason so the user can opt out.strapijs.vercel.app pattern still works (Vercel deploys all branches under the strapijs subdomain), so no special handling needed.After all PRs are processed:
| PR# | Action | New title/description |
|-----|---------|------------------------|
| ... | edited | ... |
| ... | skipped | (unchanged) |
Include PRs classified as compliant in a separate count below the table (e.g., Compliant (no changes needed): 5 PRs).
AUTO=false), never edit without explicit confirmation.AUTO=true), still respect the compliant filter — never rewrite content that already passes the rules.title action only modifies the title. description/body action only modifies the body. Cross-pollination is not allowed.Interactive title rewrite on a single PR:
/inki:pr-fix title 3204
Interactive description rewrite from a URL:
/inki:pr-fix description https://github.com/strapi/documentation/pull/3204
Body alias (same behavior as description):
/inki:pr-fix body 3204
Auto title rewrite on multiple PRs:
/inki:pr-fix title --yes 3204 3202 #3199
Auto title rewrite on all recent open PRs (with batch review):
/inki:pr-fix title --yes
Auto title rewrite on all open PRs including stale ones:
/inki:pr-fix title --yes --include-old
npx claudepluginhub strapi/documentation --plugin inkiCreates, refreshes, and rewrites PR titles and descriptions following Sentry conventions. Requires GitHub CLI.
Creates, updates descriptions, and adds comments to GitHub pull requests using file-based drafts for safety, emphasizing material impact and reviewer-friendly communication.
Generates comprehensive PR descriptions, automates review processes, and ensures PRs follow best practices for clarity, size, and reviewability.