Contributor-First PR Merge
HKUDS/OpenHarness
Merges external GitHub pull requests while keeping the original author credited, and fixes conflicts after the merge instead of rewriting the contribution.
Cuts a ToolJet release branch, bumps the version and moves feature PRs onto it, or adds more PRs to a release that already exists.
$ npx skills add ToolJet/ToolJet --skill cut-release -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install ToolJet/ToolJet cut-release --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/ToolJet/ToolJet.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.agents/skills/cut-release .claude/skills/cut-release && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "cut-release" agent skill from https://github.com/ToolJet/ToolJet/tree/main/.agents/skills/cut-release into .claude/skills/cut-release/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "cut-release", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/ToolJet/ToolJet/tree/main/.agents/skills/cut-releaseType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add ToolJet/ToolJet --skill cut-release -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install ToolJet/ToolJet cut-release --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/ToolJet/ToolJet.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.agents/skills/cut-release .agents/skills/cut-release && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "cut-release" agent skill from https://github.com/ToolJet/ToolJet/tree/main/.agents/skills/cut-release into .agents/skills/cut-release/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "cut-release", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add ToolJet/ToolJet --skill cut-release -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install ToolJet/ToolJet cut-release --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/ToolJet/ToolJet.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.agents/skills/cut-release .cursor/skills/cut-release && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "cut-release" agent skill from https://github.com/ToolJet/ToolJet/tree/main/.agents/skills/cut-release into .cursor/skills/cut-release/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "cut-release", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/ToolJet/ToolJet.git --path .agents/skills/cut-release--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add ToolJet/ToolJet --skill cut-release -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install ToolJet/ToolJet cut-release --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/ToolJet/ToolJet.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.agents/skills/cut-release .gemini/skills/cut-release && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "cut-release" agent skill from https://github.com/ToolJet/ToolJet/tree/main/.agents/skills/cut-release into .gemini/skills/cut-release/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "cut-release", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install ToolJet/ToolJet cut-releaseInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add ToolJet/ToolJet --skill cut-release -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/ToolJet/ToolJet.git skills-src && mkdir -p .github/skills && cp -r skills-src/.agents/skills/cut-release .github/skills/cut-release && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "cut-release" agent skill from https://github.com/ToolJet/ToolJet/tree/main/.agents/skills/cut-release into .github/skills/cut-release/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "cut-release", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add ToolJet/ToolJet --skill cut-release -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install ToolJet/ToolJet cut-release --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/ToolJet/ToolJet.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.agents/skills/cut-release .opencode/skills/cut-release && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "cut-release" agent skill from https://github.com/ToolJet/ToolJet/tree/main/.agents/skills/cut-release into .opencode/skills/cut-release/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "cut-release", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
cut-releaseCuts a ToolJet release branch, bumps the version and moves feature PRs onto it, or adds more PRs to a release that already exists.
An interactive workflow built on the `gh` CLI for ToolJet maintainers. You supply an edition, `lts` or `beta`, and the pull requests to move, which can be root PRs or PRs in the `ee-server` and `ee-frontend` submodule repositories. LTS releases are cut from `lts-3.16` and beta releases from `main`, and a PR on the wrong line is a hard error that is caught before anything is created.
For a new release it creates the release branch across the root repository and its submodules, bumps the version with the repository's script, changes each PR's base to that branch, merges the branch into each PR branch, and comments the roster on the release base PR, flagging conflicts and mentioning authors. Run again on an existing release branch, it skips the cut and bump and only adds the new PRs. Shell notes say to avoid loops in the zsh-based Bash tool.
5 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 1786038. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
gitghbashFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md. Its commands use git and gh, which can reach the network depending on how they are called.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
ToolJet Release Cutter loads about 5.6k tokens when it runs. Until then it costs about 182 tokens; SKILL.md has 2,322 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from ToolJet/ToolJet at commit 1786038, republished under its AGPL-3.0 licence (© ToolJet). 2,322 words, ~5,606 tokens.
.claude/skills/cut-release/SKILL.md (or your agent's skills folder).Cut a release branch, bump the version, and retarget a set of feature PRs onto it.
This skill is the interactive gh-based successor to release-scripts/create-release.sh (which is milestone + raw GitHub API driven). It reuses release-scripts/bump-minor-version.sh for the version bump and the root → server/ee → frontend/ee fan-out pattern from the merge / create-pr skills.
User input: $ARGUMENTS
Parse $ARGUMENTS. Two inputs are required — ask for whichever is missing before doing anything:
Edition — lts or beta.
lts → cut from lts-3.16beta → cut from mainPR list — one or more PRs to retarget, as numbers or URLs. Each is either:
ToolJet/ToolJet) — a "base-branch PR", orToolJet/ee-server or ToolJet/ee-frontend).Classify each by its URL. If only a bare number is given, ask which repo it belongs to.
Target release branch (optional) — e.g. release/v3.20.237-lts. Pass this to add PRs to an existing release instead of cutting a new one. When omitted, the branch name is computed (Phase 1); if that branch already exists remotely, the skill switches to append mode automatically.
Fresh-cut vs. append mode. If the target release branch does not yet exist → fresh cut (Phase 1 creates it and bumps the version). If it already exists → append mode: skip the version bump and branch creation, reuse the release base PRs, and just retarget/merge the new PRs onto it (Phases 2–5). The skill is re-runnable — running it again with more PRs simply adds them.
Edition and PR line must match. A beta release only accepts PRs on the beta line (base
main/develop); an lts release only accepts PRs on the lts line (baselts-3.16). A cross-line PR is a hard error — see "Validate edition vs. PR base" in Phase 2. This is checked before anything is created.
Requires the gh CLI, authenticated against ToolJet/ToolJet, ToolJet/ee-server, and ToolJet/ee-frontend.
IMPORTANT: The Bash tool runs in zsh via
eval. Two constraints carried over from themerge/create-prskills:
forloops causegit: command not found— never use loops. Use inline per-repo / per-PR commands.- Use full paths for coreutils:
/usr/bin/head,/usr/bin/sed.
Set ROOT, SEE, FEE once at the top of each Bash call:
ROOT=$(git rev-parse --show-toplevel); SEE="$ROOT/server/ee"; FEE="$ROOT/frontend/ee"Skip this entire phase in append mode. First resolve $RELBR and the mode:
ROOT=$(git rev-parse --show-toplevel); BASE="<lts-3.16 for lts | main for beta>"
# $RELBR = the --target release branch if given, else the computed name (Step 1).
if git -C "$ROOT" ls-remote --heads origin "$RELBR" | /usr/bin/grep -q .; then
echo "APPEND MODE — $RELBR exists; skip version bump + branch creation, go to Phase 2."
else
echo "FRESH CUT — create $RELBR (Phase 1 below)."
fi$NEWVER from the branch name (strip the leading release/v). Ensure the submodule release branches exist (Step 3 is still idempotent — create any missing one). Then jump to Phase 2. The release base PRs already exist and are reused in Phase 1 Step 4 / Phase 4.The three version files (.version, server/.version, frontend/.version) all live in the root repo, so the version bump is a root-only commit.
bump-minor-version.sh bumps the patch component and preserves the suffix (-beta, -lts). Compute the same value first so we can name the branch:
ROOT=$(git rev-parse --show-toplevel)
BASE="<lts-3.16 for lts | main for beta>"
git -C "$ROOT" fetch origin "$BASE"
# Read the version from ORIGIN's base tip, not the working tree — no checkout needed,
# and immune to a stale/diverged local base branch.
VER=$(git -C "$ROOT" show "origin/$BASE:.version" | /usr/bin/head -n1)
BASEV=${VER%%-*}; SUF=${VER#*-}
IFS=. read -r MAJ MIN PAT <<< "$BASEV"
PAT=$((PAT+1))
NEWVER="${MAJ}.${MIN}.${PAT}-${SUF}"
RELBR="release/v${NEWVER}"
echo "current=$VER new=$NEWVER branch=$RELBR"The suffix flows from the base branch's .version as-is (lts-3.16 → -lts, main → -beta), so RELBR already carries the right suffix — do not append another one. If $VER has no - suffix, stop and ask the user: the bump script would be a no-op.
Do not switch the user's main checkout. It has uncommitted/untracked files (including this skill's own untracked .claude/skills/cut-release), and the base/PR branches track .claude/skills — so git checkout <base> in the main tree fails with "would lose untracked files in .claude/skills" and silently leaves you on the wrong branch, and any bump then dirties the wrong tree. Create the release branch in a throwaway worktree cut from origin/$BASE:
WT="$SCRATCH/cut-release-wt" # a scratchpad path, NOT inside the repo
git -C "$ROOT" worktree remove --force "$WT" 2>/dev/null
git -C "$ROOT" worktree add -b "$RELBR" "$WT" "origin/$BASE" # anchored to origin — immune to a stale local base branch
# Run the bump INSIDE the worktree. The script writes ./.version relative to CWD,
# so cwd MUST be the worktree, and the file is not +x so call it via `bash`:
( cd "$WT" && bash ./release-scripts/bump-minor-version.sh )
/usr/bin/head -1 "$WT/.version" "$WT/server/.version" "$WT/frontend/.version" # VERIFY all three == $NEWVER before committing
git -C "$WT" commit -m "chore: bump version to ${NEWVER}" -- .version server/.version frontend/.version
git -C "$WT" push -u origin "$RELBR"Gotchas, each learned the hard way:
bash "$WT/release-scripts/..." with cwd still at $ROOT bumps the main tree's files (script uses ./.version). Always ( cd "$WT" && bash ./release-scripts/... ).bump-minor-version.sh without --create-branch — the branch already exists; the flag would open an unwanted chore/bump-version-* PR.$NEWVER after the bump, stop — the script didn't run.git -C "$ROOT" worktree remove --force "$WT") only at the very end.Create $RELBR in both submodules, cut from each submodule's own base branch (lts-3.16 for lts, main for beta). They are needed for two reasons, so create them whenever any PR (root or submodule) is in scope:
server/ee / frontend/ee gitlink conflicts, and we resolve that pointer (Phase 3) — so the branch must exist even if no submodule PR was listed.Operate directly on the user's $SEE / $FEE submodule checkouts (they are independent git repos — the root worktree's submodules are not initialized). First verify each is clean; this switches them off their current branch, so restore them at the end (Phase 4 cleanup).
git -C "$SEE" fetch origin "$BASE"
git -C "$SEE" checkout -b "$RELBR" "origin/$BASE"
git -C "$SEE" push -u origin "$RELBR"
git -C "$FEE" fetch origin "$BASE"
git -C "$FEE" checkout -b "$RELBR" "origin/$BASE"
git -C "$FEE" push -u origin "$RELBR"($BASE here is the submodule's own base — lts-3.16 for lts, main for beta. Verify the submodule actually has that base branch before cutting.)
No version bump in submodules — the version files are root-only.
Open a release base PR ($RELBR → $BASE) in the root repo and in every submodule whose release branch has commits ahead of its base. These are the PRs the Phase 4 roster is posted on. Do it idempotently: check first, create only if missing.
# per repo (ToolJet, ee-server, ee-frontend):
EXIST=$(gh pr list --repo <owner/repo> --head "$RELBR" --state all --json number,url -q '.[0].url')
if [ -n "$EXIST" ]; then echo "exists: $EXIST"; else
gh pr create --repo <owner/repo> --base "$BASE" --head "$RELBR" \
--title "Release v${NEWVER}" \
--body "Release branch for \`${NEWVER}\`. Retargeted PRs are listed in the roster comment below."
fi$RELBR" — that means the submodule's release branch is identical to its base (no PRs were retargeted into it). That's expected; skip it, don't treat it as an error.Build the full set of PRs to retarget. Keep a record per PR of: repo, number, headBranch, author.
For each input PR, fetch its metadata:
gh pr view <number> --repo <repo> --json number,headRefName,author,baseRefName,urlheadRefName, then find submodule PRs on the same head branch (ToolJet's convention is that a feature's root and submodule branches share a name — see the create-pr skill):gh pr list --repo ToolJet/ee-server --head "<headBranch>" --state open --json number,headRefName,author,url
gh pr list --repo ToolJet/ee-frontend --head "<headBranch>" --state open --json number,headRefName,author,urlDe-duplicate (a root PR and its submodule PRs may both be listed explicitly). Present the expanded set to the user before mutating anything.
A PR's current baseRefName tells which release line it belongs to: base lts-3.16 → lts, base main (or develop) → beta. A PR must match the chosen edition. Retargeting across lines is always wrong (it drags the full main↔lts divergence in as conflicts), so error out and go no further — do not create branches, change bases, or merge:
lts-3.16.main / develop, i.e. not lts-3.16.Check it before Phase 1 has created anything — ideally run Phase 2's metadata fetch first, validate, then start Phase 1. If any PR violates the rule, stop and report, e.g.:
ERROR: edition=beta but these PRs target the lts line (base lts-3.16):
- ToolJet#18174 (feat/ext-api-v2-app-access)
- ee-server#895 (feat/ext-api-v2-app-access)
Pick edition=lts for these PRs, or remove them. Aborting — nothing was changed.Because this gate must fire before any mutation, reorder the run: do Phase 2 (fetch + classify + expand + validate) first, and only then Phase 1 (create branches / bump / open release PR). Nothing in Phase 1 is safe to leave behind if validation fails.
For each PR: change its base to $RELBR and merge $RELBR into its head branch so conflicts surface now. Order matters: process submodule PRs BEFORE their paired root PRs, because a paired root PR's gitlink must be set to the merged submodule PR-head tip (your chosen resolution). Keep a map headBranch → merged submodule tip SHA as you go.
gh pr editgh pr edit --base goes through GraphQL, which returns a flaky Something went wrong while executing your query 500 (seen repeatedly right after the base branch is freshly pushed). The REST PATCH is reliable (it's what create-release.sh used):
gh api -X PATCH repos/<owner/repo>/pulls/<number> -f base="$RELBR" --jq '.base.ref'
# verify:
gh pr view <number> --repo <owner/repo> --json baseRefName -q .baseRefNameOperate in the submodule's own repo dir. Capture the resulting tip:
D="<$SEE | $FEE>"
git -C "$D" fetch origin "<headBranch>" "$RELBR"
git -C "$D" checkout -B "<headBranch>" "origin/<headBranch>"
if git -C "$D" merge --no-edit "origin/$RELBR"; then
git -C "$D" push origin "<headBranch>"
echo "SUBTIP <headBranch>=$(git -C "$D" rev-parse HEAD)" # record for the paired root PR
else
git -C "$D" merge --abort; echo "CONFLICT <repo>#<number>" # label + author (Step 3)
fiReuse the Phase 1 worktree $WT — the main checkout still can't switch to a PR branch (untracked .claude/skills). The merge conflicts only on .version (take the release side) and the server/ee/frontend/ee gitlinks. Resolve a gitlink to the paired submodule PR's merged tip ($SUBTIP from Step 2a) when one exists; otherwise to that submodule's origin/$RELBR tip. A gitlink is resolved with update-index --cacheinfo, NOT checkout --theirs:
git -C "$ROOT" fetch origin "<headBranch>" "$RELBR"
git -C "$WT" checkout -B "<headBranch>" "origin/<headBranch>"
git -C "$WT" merge --no-edit "origin/$RELBR" # exits non-zero on the expected gitlink/version conflicts
# version files -> release side (often already auto-merged; harmless if nothing staged):
git -C "$WT" checkout --theirs .version server/.version frontend/.version 2>/dev/null; git -C "$WT" add .version server/.version frontend/.version 2>/dev/null
# each CONFLICTED gitlink -> paired merged submodule tip ($SUBTIP) or that submodule's origin/$RELBR:
git -C "$WT" update-index --cacheinfo 160000,"<SUBTIP-or-$RELBR-tip>",server/ee
# (repeat for frontend/ee only if it is in the unmerged list)
REM="$(git -C "$WT" diff --name-only --diff-filter=U)"
if [ -z "$REM" ]; then
git -C "$WT" commit --no-edit && git -C "$WT" push origin "<headBranch>"
echo "OK <repo>#<number>"
else
git -C "$WT" merge --abort; echo "CONFLICT <repo>#<number> -> $REM" # real code conflict
fiRecord each PR as OK or CONFLICT (author). Before moving on, confirm git -C "$WT" diff --name-only --diff-filter=U is empty (never leave a half-merged tree).
Only version files and gitlink pointers are auto-resolved — they conflict on every retarget and carry no author intent. Any other remaining path is a real code conflict: abort and leave it for the author. Never auto-resolve those.
For every PR that conflicted, label it and leave it for its author (mirrors create-release.sh's merge-conflict label). Use REST to dodge the GraphQL flakiness:
gh api -X POST repos/<owner/repo>/issues/<number>/labels -f "labels[]=merge-conflict"Post/refresh the roster on every release base PR — the root PR gets the full roster (all PRs, both repos); each submodule release PR gets a roster of that submodule's PRs. Cross-link the submodule release PR(s) from the root roster. List conflicts explicitly and @mention the author of each conflicted PR.
Build the roster from a live query, not from this run's input — so append-mode re-runs show the complete set (previously-added PRs included). Every retargeted PR has its base set to $RELBR, so query by base:
gh pr list --repo ToolJet/ToolJet --base "$RELBR" --state all --json number,author,state,title
gh pr list --repo ToolJet/ee-server --base "$RELBR" --state all --json number,author,state,title
gh pr list --repo ToolJet/ee-frontend --base "$RELBR" --state all --json number,author,state,titleMap each PR's status: MERGED → ✅ Merged; OPEN and reached ready in Phase 3 → ✅ ready to merge; left conflicted → ⚠️ conflict. Update the existing roster comment in place (don't post a duplicate on re-runs): find your prior roster comment and edit it via REST, else post a new one.
# find a prior roster comment id (first one whose body starts with the roster heading):
gh api repos/ToolJet/ToolJet/issues/<release-pr-number>/comments --jq '.[] | select(.body|startswith("## Retargeted onto")) | .id' | /usr/bin/head -1
# edit it: gh api -X PATCH repos/ToolJet/ToolJet/issues/comments/<id> -f body="$(cat <<'EOF' ... EOF)"
# or create: gh pr comment <release-pr-number> --repo ToolJet/ToolJet --body "$(cat <<'EOF' ... EOF)"Roster body shape:
## Retargeted onto `release/vNEWVER`
| PR | Repo | Author | Status |
|----|------|--------|--------|
| #123 | ToolJet | @alice | ✅ ready to merge |
| #45 | ee-server | @bob | ⚠️ conflict |
### Needs attention (merge conflicts)
- ee-server#45 — @bob: resolve conflicts against `release/vNEWVER` and push.Status is ready to merge (retargeted + integrated, not yet merged), merged (Phase 5 done), or conflict. Omit the "Needs attention" section when nothing conflicted.
Merging is opt-in and interactive — never merge without explicit go-ahead. Only the PRs that reached ready to merge in Phase 3 are eligible; conflicted ones are skipped (they belong to their authors).
Ask the user two things before merging anything:
Then merge each ready PR into its base ($RELBR). Merge submodule PRs before their paired root PRs (same reason as Phase 3 — the root PR's server/ee pointer should reference the merged submodule state):
# squash:
gh pr merge <number> --repo <owner/repo> --squash
# or normal merge commit:
gh pr merge <number> --repo <owner/repo> --merge--admin bypasses protections and must only be used when the user explicitly asks (the release branch typically requires 1 review; enforce_admins is usually off, so an admin can bypass).gh pr view <number> --json state,mergedAt).$RELBR, so a later PR that shares files (or the server/ee gitlink) with an earlier one flips to conflicting — gh pr merge then fails with "Pull Request has merge conflicts". Re-sync it before merging: in a worktree, merge the updated origin/$RELBR into the PR head, resolve .version (release side) and each gitlink to the submodule's current origin/$RELBR tip (which now contains all already-merged submodule PRs), push, then merge. Merging in gitlink-dependency order (submodules fully merged first, then root) minimizes this.Beyond the roster on the release base PR, comment on each individual PR — root and submodule alike — with its own final outcome, so the state is visible from the PR itself:
gh pr comment <number> --repo <owner/repo> --body "<status>"✅ Merged into \$RELBR` (squash). Part of release <release-PR-url>.`⚠️ Merge conflict against \$RELBR` — @<author> please resolve and push.(plus themerge-conflict` label.)⏳ Retargeted onto \$RELBR` and ready, but not merged: <reason>.`Then give the user a final summary: release branch, release base PR URL, and per-PR outcome (merged / ready but not merged / conflict / blocked), with authors for anything left open.
git -C "$ROOT" worktree remove --force "$WT" # remove the Phase 1 worktree
git -C "$SEE" checkout <original-branch> # submodules were switched in Phase 1 Step 3
git -C "$FEE" checkout <original-branch>
git -C "$ROOT" worktree prune
git -C "$ROOT" branch -D "$RELBR" 2>/dev/null # the local release branch is throwaway; the remote one staysThe remote release branches and PR changes are the deliverables; everything local is scratch.
gh pr edit (base, labels) hits a flaky GraphQL 500 — prefer REST (gh api -X PATCH .../pulls/<n> -f base=..., gh api -X POST .../issues/<n>/labels -f labels[]=merge-conflict) and always verify the change took.merge --abort or resolve, verify git -C "$WT" diff --diff-filter=U is empty before continuing.gh isn't authenticated for a submodule repo, skip that repo's PRs and report it rather than failing the whole run.© ToolJet, AGPL-3.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
Just SKILL.md in .agents/skills/cut-release of ToolJet/ToolJet.
Open the folder on GitHubat commit 1786038
ToolJet Release Cutter next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| ToolJet Release Cutter this skillToolJet/ToolJet | 41k | — | ~5.6k | Automated safety check: Pass | AGPL-3.0 | |
| Contributor-First PR MergeHKUDS/OpenHarness | 16k | 1 repos | ~847 | Automated safety check: Pass | MIT | |
| Create Pull Requestcline/cline | 70k | 1 repos | ~1.6k | Automated safety check: Pass | Apache-2.0 | |
| Creating Description For Gh PRredis/jedis | 12k | — | ~838 | Automated safety check: Pass | MIT | |
| Create Pull Request with Work Item IDmakeplane/plane | 60k | — | ~824 | Automated safety check: Pass | AGPL-3.0 | |
| React Router Pull Request Creatorremix-run/react-router | 57k | — | ~2.5k | Automated safety check: Pass | MIT |
HKUDS/OpenHarness
Merges external GitHub pull requests while keeping the original author credited, and fixes conflicts after the merge instead of rewriting the contribution.
cline/cline
Opens a GitHub pull request from your current branch with the gh CLI, after reviewing the commits and diff and gathering the details the PR needs.
redis/jedis
Generate a clear, concise GitHub PR title and description from the diff between two local git branches, and save it to prDescription.md in the repo root.
makeplane/plane
Opens a pull request for the current branch using the repo's template, a work item ID in the title and a description filled in from the actual diff.
remix-run/react-router
Packages finished React Router work into a draft pull request: branch, commit, push, a written PR body and the right GitHub labels.
pascalorg/editor
Opens or refreshes a pull request on pascalorg/editor from the current branch, describing only what the branch's commits and diff actually contain.
ToolJet/ToolJet
Turns an API description, such as an OpenAPI file or a Postman collection, into a connector plugin for ToolJet's marketplace and checks it with the repo's validator.
ToolJet/ToolJet
Commits changes across ToolJet's root repo and its server/ee and frontend/ee submodules, writing messages from the diffs and updating submodule pointers in order.
ToolJet/ToolJet
Opens a pull request for the current ToolJet branch, pushing the root repo and the ee submodules, creating submodule PRs first and then the main PR with a generated description.
ToolJet/ToolJet
Reviews a ToolJet pull request of any size and writes a findings report for you to read first, scaling the process to the diff and posting to GitHub only on request.
ToolJet/ToolJet
Merges a source branch into the current branch across ToolJet's root repo and its server/ee and frontend/ee submodules, handling conflicts and submodule order.
ToolJet/ToolJet
Decides where a new agent skill belongs in the ToolJet repo, public root or private ee submodule, then wires the symlinks so it loads in Claude Code, Cursor and Codex.
Categories
Cuts a ToolJet release branch, bumps the version and moves feature PRs onto it, or adds more PRs to a release that already exists. An interactive workflow built on the `gh` CLI for ToolJet maintainers. You supply an edition, `lts` or `beta`, and the pull requests to move, which can be root PRs or PRs in the `ee-server` and `ee-frontend` submodule repositories.
ToolJet Release Cutter fits situations like: starting an LTS or beta release and retargeting its feature PRs; adding late PRs to a release branch that was already cut; finding which PRs conflict with a release branch before merging.
Run `npx skills add ToolJet/ToolJet --skill cut-release -a claude-code`. Or copy the skill folder (.agents/skills/cut-release in ToolJet/ToolJet) into .claude/skills/cut-release in your project. Claude Code loads it when a task matches its description.
Run `npx skills add ToolJet/ToolJet --skill cut-release -a codex`. Or copy the skill folder (.agents/skills/cut-release in ToolJet/ToolJet) into .agents/skills/cut-release in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add ToolJet/ToolJet --skill cut-release -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/cut-release, .gemini/skills/cut-release, .github/skills/cut-release and .opencode/skills/cut-release in your project.
Going by SKILL.md and its folder, ToolJet Release Cutter needs the command-line tools its instructions call (git, gh and bash). Our summary lists: The `gh` CLI, authenticated for the ToolJet, ee-server and ee-frontend repositories.
SKILL.md contains no URLs. Its commands use git and gh, which can reach the network depending on how they are called. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
ToolJet Release Cutter is published under the AGPL-3.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 5.6k tokens (SKILL.md is roughly 22k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with ToolJet Release Cutter: Contributor-First PR Merge (HKUDS/OpenHarness, 16k stars), Create Pull Request (cline/cline, 70k stars), Creating Description For Gh PR (redis/jedis, 12k stars) and Create Pull Request with Work Item ID (makeplane/plane, 60k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
ToolJet (a GitHub organization) maintains it in ToolJet/ToolJet, which has 41,045 GitHub stars. The repository holds 7 skills in this directory. The repository was last updated on October 7, 2026.
Source: ToolJet/ToolJet on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.