Skip to content

Commit 31828e8

Browse files
cs-rajclaude
andcommitted
feat: add back-merge, release, and resolve-snyk developer skills
Add three internal developer skills to the CLI repo for use by the devex team and contentstack agents. These skills are distinct from the customer-facing skills in contentstack-agent-skills — they cover developer workflows specific to this repository. Skills added: - back-merge: automates merging main into release branches - release: orchestrates the full release pipeline (Google Sheets tracking, PR checks, build/deploy coordination) - resolve-snyk: runs Snyk audits and applies dependency upgrades to fix vulnerabilities across CLI packages No secrets are committed — credentials files are .gitignored and all talisman flags are whitelisted false positives (code variables, example/template files). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent cf8b920 commit 31828e8

17 files changed

Lines changed: 3348 additions & 0 deletions

.talismanrc

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,42 @@ fileignoreconfig:
55
version: '1.0'
66
- filename: .github/workflows/release-production-pipeline.yml
77
checksum: dd858a2c2a3297c5651c2843ddae73ad0776a0386329200d01b051ddc489871e
8+
- filename: skills/release/references/sheets-api.md
9+
checksum: e0e05865f67f07365e6e17e6b6d2d1a944c70ba4ee7fd8be555dbaf5a0e5ae6a
10+
version: '1.0'
11+
- filename: skills/release/scripts/build-deploy-plan.mjs
12+
checksum: 59426bc226d31b39f00d5e95031cb47acef0c5835d02766b3bccb610ef15c4c1
13+
version: '1.0'
14+
- filename: skills/back-merge/SKILL.md
15+
checksum: fb9a17abf0d532ac5ff1580051408bc6d00b8ea0d9b7c02ac5a2128fa5efc717
16+
version: '1.0'
17+
- filename: skills/release/scripts/refresh-google-token.sh
18+
checksum: 32321da36f711ee265bb084e0f6e174fc6fe07b4037f5cfbb4ae4fff3383304e
19+
version: '1.0'
20+
- filename: skills/release/references/google-credentials.example.json
21+
checksum: aabfa2f9d65835a43b7541dfe379f0b968f733d93be2eeac82b9b618c9cae7e2
22+
version: '1.0'
23+
- filename: skills/release/scripts/fetch-release-data.mjs
24+
checksum: ce8bb03c1e374abf2dd6c6aaf6da37c66e7d5322a8bbdff3934ddf64f89c17ed
25+
version: '1.0'
26+
- filename: skills/release/SKILL.md
27+
checksum: 48be9892b46329734ebd953249d70532f2badc3399a49352c5a9aa0b51b29369
28+
version: '1.0'
29+
- filename: skills/release/scripts/check-prs.mjs
30+
checksum: 951efbb87e8ff3594e4117b50b970143c5485a02648df6daca55ee0662819d67
31+
version: '1.0'
32+
- filename: skills/resolve-snyk/SKILL.md
33+
checksum: c85525c91ff4da6b79e9446bc032641689403688cbb4409e193fb4005f75ff6d
34+
version: '1.0'
35+
- filename: skills/back-merge/scripts/back-merge.sh
36+
checksum: 4adac8f3c3348aa5aaf6bdb4d92a99189c120a4687c5788b2783a9f96b1906e5
37+
version: '1.0'
38+
- filename: skills/release/references/config.example.json
39+
checksum: 67d56e7573403378fdb6b9500e70effd74111963ae0f72fde8183c8b2786e67f
40+
version: '1.0'
41+
- filename: skills/release/scripts/copy-template-sheet.sh
42+
checksum: fac7d2eddab079ec010a31bdfedd6d3d4a53e4e2bfa0a7848100f185d6ac8a04
43+
version: '1.0'
44+
- filename: skills/release/references/.gitignore
45+
checksum: 7ef9f9de95ad10f7d7ab5a4ee1477bdc013ebe46bb227741b062a3b809d4566b
46+
version: '1.0'

skills/back-merge/SKILL.md

Lines changed: 254 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,254 @@
1+
---
2+
name: back-merge
3+
description: >
4+
Creates or checks back-merge PRs (main/master → development) across repos after a release.
5+
Use whenever the user wants to check or create back-merge PRs, verify post-release branch sync,
6+
or audit which repos are missing a back-merge.
7+
Triggers on: /back-merge, "back merge", "backmerge", "check back merges", "create back-merge PRs",
8+
"are back merges done", "post-release back merge".
9+
---
10+
11+
# Back-Merge Skill
12+
13+
Creates or checks PRs that merge `main/master → development` across repos after a release.
14+
Supports two input modes and two run modes.
15+
16+
---
17+
18+
## Usage
19+
20+
When this skill activates, greet the user with this help block before doing anything else:
21+
22+
```
23+
👋 /back-merge — Back-merge PR checker & creator
24+
25+
How to use:
26+
/back-merge → I'll ask how to specify repos
27+
/back-merge "<fixVersion>" → pull repos from a Jira release
28+
/back-merge <github-url1> <github-url2> ... → use a direct repo list
29+
/back-merge "<fixVersion>" <github-url1> ... → combine both sources
30+
31+
Modes (specify after your input, or I'll ask):
32+
check — read-only audit, shows what needs back-merging (default, recommended)
33+
create — raises PRs for all repos that need them (asks for confirmation first)
34+
35+
Examples:
36+
/back-merge "DX | 16-08-2026 | Release" check
37+
/back-merge https://github.com/org/repo1 https://github.com/org/repo2
38+
/back-merge "PROJ | 16-08-2026 | Release" create
39+
40+
Requirements:
41+
gh CLI → brew install gh && gh auth login
42+
Jira MCP (only needed for fixVersion mode) — must be connected in Claude Code
43+
```
44+
45+
Only show this block once at the start. Then proceed to collect inputs.
46+
47+
---
48+
49+
## Inputs
50+
51+
| Input | Format | Example |
52+
|-------|--------|---------|
53+
| `fixVersion` | Jira fixVersion string | `PROJ \| 16-08-2026 \| Release` |
54+
| `repo_list` | Space or comma-separated GitHub URLs | `https://github.com/org/repo1 https://github.com/org/repo2` |
55+
| `mode` | `check` or `create` | `check` |
56+
57+
**If neither fixVersion nor repo_list is provided** — use AskUserQuestion:
58+
59+
> **Question:** "How would you like to specify the repos to back-merge?"
60+
> - `fixVersion` — pull repos from a Jira release (e.g. `PROJ | 16-08-2026 | Release`)
61+
> - `Repo list` — paste GitHub repo URLs directly
62+
> - `Both` — combine repos from a fixVersion AND a manual list
63+
>
64+
> *(User can select "Other" to type a custom value)*
65+
66+
Do not proceed without knowing the scope.
67+
68+
**If mode is not provided** — use AskUserQuestion:
69+
70+
> **Question:** "Which mode do you want to run?"
71+
> - `Check` — read-only, shows what needs back-merging without creating any PRs *(Recommended)*
72+
> - `Create PRs` — creates back-merge PRs for all repos that need them
73+
>
74+
> *(User can select "Other" to specify a custom behaviour)*
75+
76+
---
77+
78+
## Step 1 — Resolve Repo List
79+
80+
### 1a — From fixVersion
81+
82+
**Extract the project key** from the fixVersion string — it is the first segment before the first `|`:
83+
- `"DX | 16-08-2026 | Release"` → project key = `DX`
84+
- `"PROJ | 16-08-2026 | Release"` → project key = `PROJ`
85+
86+
Query Jira using the extracted project key:
87+
88+
```
89+
project = {project_key} AND fixVersion = "{fixVersion}" ORDER BY created ASC
90+
```
91+
92+
Scan every ticket's `comment.comments[].body` AND the master release ticket's `description` for GitHub PR URLs:
93+
94+
```
95+
pattern: https://github\.com/[^/]+/[^/]+/pull/\d+
96+
```
97+
98+
For each unique PR URL, extract `owner` and `repo` from the URL path. Deduplicate by `owner/repo`. This is your repo list.
99+
100+
If no PR URLs are found → warn the user and stop:
101+
> `⚠️ No GitHub PRs found for fixVersion "{fixVersion}". Cannot determine repo scope.`
102+
103+
### 1b — From repo_list
104+
105+
Parse each GitHub URL to extract `owner/repo`:
106+
- `https://github.com/org/repo-name``org/repo-name`
107+
108+
Deduplicate silently. If a URL can't be parsed → skip it and flag:
109+
> `⚠️ Could not parse repo from URL: {url} — skipped.`
110+
111+
### Step 1 output — display resolved repos before proceeding
112+
113+
After resolving the repo list, always show this table:
114+
115+
```
116+
📋 Repos resolved (N total)
117+
118+
# | Repo | Source
119+
--|---------------------------|--------
120+
1 | org/repo-one | fixVersion
121+
2 | org/repo-two | manual list
122+
3 | org/repo-three | fixVersion
123+
```
124+
125+
Do not proceed to Step 2 until this table is shown.
126+
127+
---
128+
129+
## Step 2 & 3 — Run the bundled script
130+
131+
Use `scripts/back-merge.sh` — do not regenerate this logic inline.
132+
133+
```bash
134+
SKILL_DIR="$(dirname "$(realpath "$0")")/.." # or: ~/.claude/skills/back-merge
135+
136+
# check mode
137+
bash "$SKILL_DIR/scripts/back-merge.sh" check \
138+
owner/repo1 owner/repo2 ...
139+
140+
# create mode — pass the full fixVersion string as the second argument
141+
bash "$SKILL_DIR/scripts/back-merge.sh" create "{fixVersion}" \
142+
owner/repo1 owner/repo2 ...
143+
144+
# create mode — no fixVersion (repo list only)
145+
bash "$SKILL_DIR/scripts/back-merge.sh" create "" \
146+
owner/repo1 owner/repo2 ...
147+
```
148+
149+
**Before running in create mode** — show the resolved repos table from Step 1 if not already visible, then use AskUserQuestion:
150+
151+
> **Question:** "Ready to create back-merge PRs for the N repos listed above. Proceed?"
152+
> - `Yes, create PRs` — proceed with PR creation
153+
> - `No, abort` — stop without making any changes
154+
> - `Check only` — switch to check mode instead (read-only, no PRs created)
155+
>
156+
> *(User can select "Other" to specify which repos to skip or override)*
157+
158+
Do not create any PRs until the user confirms.
159+
160+
The script outputs one pipe-delimited line per repo:
161+
162+
```
163+
owner/repo|STATUS_CODE|branch_used|details
164+
```
165+
166+
| STATUS_CODE | Meaning |
167+
|-------------|---------|
168+
| `IN_SYNC` | branches are in sync |
169+
| `NEEDS_MERGE` | back-merge needed (`details` = "base is N commits ahead (status)") |
170+
| `PR_OPEN` | open PR already exists (`details` = "PR#N url") |
171+
| `CREATED` | PR created (`details` = PR URL) |
172+
| `NO_DEV` | no development branch |
173+
| `NO_BASE` | no main or master branch |
174+
| `ACCESS_ERROR` | API error / no access |
175+
| `ERROR` | PR creation failed (`details` = error message) |
176+
177+
Parse the output and render the table below.
178+
179+
### Check mode output table
180+
181+
```
182+
Repo | Status | Details
183+
org/repo-one | ✅ In sync | —
184+
org/repo-two | ⚠️ Back-merge needed | main is 4 commits ahead
185+
org/repo-three | 🔄 PR open — pending merge | PR#456 <url>
186+
org/repo-four | ❌ No dev branch | —
187+
org/repo-five | ❌ Access error | —
188+
```
189+
190+
Do not create any PRs in check mode.
191+
192+
### Create mode output table
193+
194+
```
195+
Repo | Result | PR
196+
org/repo-one | ✅ Created | PR#789 <url>
197+
org/repo-two | ⏭️ Skipped — PR open | PR#456 <url>
198+
org/repo-three | ⏭️ Skipped — in sync | —
199+
org/repo-four | ⏭️ Skipped — no dev branch | —
200+
```
201+
202+
**`ERROR` rows**: report the details verbatim — do not retry silently. If details contains "No commits between", treat it as `IN_SYNC`.
203+
204+
---
205+
206+
## Corner Cases Handled
207+
208+
| # | Scenario | Behaviour |
209+
|---|----------|-----------|
210+
| 1 | Repo has no `development` branch | Flagged `❌ No dev branch`, skipped |
211+
| 2 | Repo has no `main` or `master` | Flagged `❌ No main/master branch`, skipped |
212+
| 3 | main and development are identical (in sync) | Flagged `✅ In sync`, no PR created |
213+
| 4 | Back-merge PR already open | Reported as `🔄 pending merge`, not duplicated |
214+
| 5 | Back-merge PR was closed (not merged) | New PR created — closed ≠ done |
215+
| 6 | Branches diverged (both have unique commits) | PR created anyway — GitHub surfaces conflicts |
216+
| 7 | `gh` cannot access repo (private / permissions) | Flagged `❌ Access error`, skipped |
217+
| 8 | Duplicate repos in manual list | Deduplicated silently before processing |
218+
| 9 | Unparseable URL in manual list | Flagged and skipped, rest continues |
219+
| 10 | fixVersion has no PRs / no repos found | Warn user and stop — no scope to act on |
220+
| 11 | PR creation fails with "no commits between" | Treated as `✅ In sync` — already resolved |
221+
| 12 | Mode not specified | Ask via AskUserQuestion (see Inputs section) |
222+
| 13 | Repo list + fixVersion both provided | Merge both lists, deduplicate, proceed |
223+
224+
---
225+
226+
## Final Summary Output
227+
228+
After all repos are processed:
229+
230+
```
231+
✅ Back-merge run complete
232+
233+
Mode: check | create
234+
Input: fixVersion "{fixVersion}" | {N} repos from list
235+
236+
📊 Summary:
237+
⚠️ Needs back-merge: <N repos — listed>
238+
🔄 PR open (pending merge): <N repos — listed with PR links>
239+
✅ In sync: <N repos>
240+
❌ Skipped (no dev branch): <N repos>
241+
❌ Skipped (access error): <N repos>
242+
243+
[create mode only]
244+
✅ PRs created: <N> — <links>
245+
```
246+
247+
---
248+
249+
## Error Handling
250+
251+
- **`gh` not authenticated**: Run `gh auth status`; ask user to run `gh auth login` if needed
252+
- **Jira MCP not available**: Cannot resolve fixVersion — ask user to switch to repo_list input mode
253+
- **All repos in sync**: Report clearly — no action needed
254+
- **Partial failures**: Always continue processing remaining repos; report failures at the end
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
#!/usr/bin/env bash
2+
# back-merge.sh — check or create back-merge PRs (master/main → development)
3+
#
4+
# Usage:
5+
# back-merge.sh check <owner/repo> [<owner/repo> ...]
6+
# back-merge.sh create <fix_version> <owner/repo> [<owner/repo> ...]
7+
#
8+
# create mode PR title: "<fix_version> | Back-merge" (pass "" for plain "Back-merge")
9+
10+
MODE="$1"; shift
11+
FIX_VERSION=""
12+
if [ "$MODE" = "create" ]; then
13+
FIX_VERSION="$1"; shift
14+
fi
15+
16+
REPOS=("$@")
17+
18+
PR_TITLE="${FIX_VERSION:+$FIX_VERSION | }Back-merge"
19+
20+
for REPO in "${REPOS[@]}"; do
21+
# 2a — dev branch
22+
DEV=$(gh api "repos/${REPO}/branches/development" 2>/dev/null | jq -r '.name // empty')
23+
if [ -z "$DEV" ]; then
24+
echo "${REPO}|NO_DEV|—|—"
25+
continue
26+
fi
27+
28+
# 2b — main or master
29+
MAIN=$(gh api "repos/${REPO}/branches/main" 2>/dev/null | jq -r '.name // empty')
30+
MASTER=$(gh api "repos/${REPO}/branches/master" 2>/dev/null | jq -r '.name // empty')
31+
if [ -n "$MAIN" ]; then
32+
BASE="main"
33+
elif [ -n "$MASTER" ]; then
34+
BASE="master"
35+
else
36+
echo "${REPO}|NO_BASE|—|—"
37+
continue
38+
fi
39+
40+
# 2c — existing open back-merge PR
41+
OPEN_PR=$(gh pr list --repo "$REPO" --head "$BASE" --base development --state open \
42+
--json number,url --jq 'if length > 0 then .[0] | "\(.number)|\(.url)" else "" end' 2>/dev/null)
43+
if [ -n "$OPEN_PR" ]; then
44+
PR_NUM=$(echo "$OPEN_PR" | cut -d'|' -f1)
45+
PR_URL=$(echo "$OPEN_PR" | cut -d'|' -f2)
46+
echo "${REPO}|PR_OPEN|${BASE}|PR#${PR_NUM} ${PR_URL}"
47+
continue
48+
fi
49+
50+
# 2d — compare
51+
COMPARE=$(gh api "repos/${REPO}/compare/development...${BASE}" \
52+
--jq '{ahead_by:.ahead_by,status:.status}' 2>/dev/null)
53+
AHEAD=$(echo "$COMPARE" | jq -r '.ahead_by // "error"')
54+
STATUS=$(echo "$COMPARE" | jq -r '.status // "error"')
55+
56+
if [ "$AHEAD" = "error" ] || [ "$STATUS" = "404" ] || [ "$STATUS" = "error" ]; then
57+
echo "${REPO}|ACCESS_ERROR|${BASE}|—"
58+
continue
59+
fi
60+
61+
if [ "$AHEAD" -gt 0 ] 2>/dev/null; then
62+
if [ "$MODE" = "check" ]; then
63+
echo "${REPO}|NEEDS_MERGE|${BASE}|${BASE} is ${AHEAD} commits ahead (${STATUS})"
64+
else
65+
# create mode — check for closed PR (just note it, don't block)
66+
CLOSED=$(gh pr list --repo "$REPO" --head "$BASE" --base development --state closed \
67+
--json number,url --jq '.[0].number' 2>/dev/null)
68+
CLOSED_NOTE="${CLOSED:+previous closed PR#$CLOSED}"
69+
70+
RESULT=$(gh pr create \
71+
--repo "$REPO" \
72+
--base development \
73+
--head "$BASE" \
74+
--title "$PR_TITLE" \
75+
--body "Back-merge of \`${BASE}\` into \`development\` following ${FIX_VERSION:-release}." 2>&1)
76+
77+
if echo "$RESULT" | grep -q "^https://"; then
78+
echo "${REPO}|CREATED|${BASE}|${RESULT}${CLOSED_NOTE:+ ($CLOSED_NOTE)}"
79+
elif echo "$RESULT" | grep -qi "no commits between"; then
80+
echo "${REPO}|IN_SYNC|${BASE}|—"
81+
else
82+
echo "${REPO}|ERROR|${BASE}|${RESULT}"
83+
fi
84+
fi
85+
else
86+
echo "${REPO}|IN_SYNC|${BASE}|—"
87+
fi
88+
done

0 commit comments

Comments
 (0)