Release Clawpatch
openclaw/clawpatch
clawpatch release: version/changelog, CI, npm publish, GitHub release, verify.
A skill your agent uses whenever releasing or deploying KanVibe desktop from a clean, up-to-date dev checkout: ask only for the target version and release-note approval, then let the AI update…
$ npx skills add rookedsysc/kanvibe --skill kanvibe-release-deploy -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install rookedsysc/kanvibe kanvibe-release-deploy --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/rookedsysc/kanvibe.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.claude/skills/kanvibe-release-deploy .claude/skills/kanvibe-release-deploy && 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 "kanvibe-release-deploy" agent skill from https://github.com/rookedsysc/kanvibe/tree/main/.claude/skills/kanvibe-release-deploy into .claude/skills/kanvibe-release-deploy/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "kanvibe-release-deploy", 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/rookedsysc/kanvibe/tree/main/.claude/skills/kanvibe-release-deployType 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 rookedsysc/kanvibe --skill kanvibe-release-deploy -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install rookedsysc/kanvibe kanvibe-release-deploy --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/rookedsysc/kanvibe.git skills-src && mkdir -p .agents/skills && cp -r skills-src/.claude/skills/kanvibe-release-deploy .agents/skills/kanvibe-release-deploy && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "kanvibe-release-deploy" agent skill from https://github.com/rookedsysc/kanvibe/tree/main/.claude/skills/kanvibe-release-deploy into .agents/skills/kanvibe-release-deploy/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "kanvibe-release-deploy", 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 rookedsysc/kanvibe --skill kanvibe-release-deploy -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install rookedsysc/kanvibe kanvibe-release-deploy --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/rookedsysc/kanvibe.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/.claude/skills/kanvibe-release-deploy .cursor/skills/kanvibe-release-deploy && 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 "kanvibe-release-deploy" agent skill from https://github.com/rookedsysc/kanvibe/tree/main/.claude/skills/kanvibe-release-deploy into .cursor/skills/kanvibe-release-deploy/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "kanvibe-release-deploy", 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/rookedsysc/kanvibe.git --path .claude/skills/kanvibe-release-deploy--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 rookedsysc/kanvibe --skill kanvibe-release-deploy -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install rookedsysc/kanvibe kanvibe-release-deploy --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/rookedsysc/kanvibe.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/.claude/skills/kanvibe-release-deploy .gemini/skills/kanvibe-release-deploy && 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 "kanvibe-release-deploy" agent skill from https://github.com/rookedsysc/kanvibe/tree/main/.claude/skills/kanvibe-release-deploy into .gemini/skills/kanvibe-release-deploy/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "kanvibe-release-deploy", 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 rookedsysc/kanvibe kanvibe-release-deployInstalls 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 rookedsysc/kanvibe --skill kanvibe-release-deploy -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/rookedsysc/kanvibe.git skills-src && mkdir -p .github/skills && cp -r skills-src/.claude/skills/kanvibe-release-deploy .github/skills/kanvibe-release-deploy && 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 "kanvibe-release-deploy" agent skill from https://github.com/rookedsysc/kanvibe/tree/main/.claude/skills/kanvibe-release-deploy into .github/skills/kanvibe-release-deploy/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "kanvibe-release-deploy", 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 rookedsysc/kanvibe --skill kanvibe-release-deploy -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install rookedsysc/kanvibe kanvibe-release-deploy --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/rookedsysc/kanvibe.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/.claude/skills/kanvibe-release-deploy .opencode/skills/kanvibe-release-deploy && 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 "kanvibe-release-deploy" agent skill from https://github.com/rookedsysc/kanvibe/tree/main/.claude/skills/kanvibe-release-deploy into .opencode/skills/kanvibe-release-deploy/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "kanvibe-release-deploy", 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.
kanvibe-release-deployA skill your agent uses whenever releasing or deploying KanVibe desktop from a clean, up-to-date dev checkout: ask only for the target version and release-note approval, then let the AI update…
Kanvibe Release Deploy is an agent skill from rookedsysc/kanvibe. Use this skill whenever releasing or deploying KanVibe desktop from a clean, up-to-date dev checkout: ask only for the target version and release-note approval, then let the AI update package versions, run pnpm run deploy, publish the DMG GitHub release, update the Homebrew cask, create the release PR, and auto-merge the release/promotion PRs when checks pass. Always use it for KanVibe release versions, DMG uploads, or Homebrew cask checksum updates.
Its SKILL.md is about 12k tokens, which your agent loads only when the skill is triggered. The skill folder holds 2 other files, including reference files (for example `references/homebrew-cask-repository.md`).
It sits in Development, covering Changelog and release notes, Task management and Git worktrees. It works with Homebrew, GitHub, pnpm and Git. The repository describes itself as: Keyboard-first desktop Kanban workspace for AI coding agents with embedded terminals, git worktrees, and hook-driven task tracking. The licence is AGPL-3.0.
7 steps, taken from the step headings in SKILL.md.
Read from SKILL.md and the folder at commit 54cebc8. 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:
ghgitpnpmnodexcruncurlrubybrewFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md. Its commands use gh, git, pnpm and curl, which can reach the network depending on how they are called.
From URLs in SKILL.md, links to its own repository left out.
Names these keys or tokens, usually read from environment variables:
APPLE_API_KEYAPPLE_API_KEY_IDFrom names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Kanvibe Release Deploy loads about 12k tokens when it runs, and up to ~12k if it reads all its reference files. Until then it costs about 120 tokens; SKILL.md has 4,290 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 noted patterns worth knowing about, such as sudo or a known installer.
*Leaking signing secrets.** Never print `.env` contents or Apple API key material in release notes, logs, commits, or DiAutomated 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 rookedsysc/kanvibe at commit 54cebc8, republished under its AGPL-3.0 licence (© rookedsysc). 4,290 words, ~11,869 tokens.
.claude/skills/kanvibe-release-deploy/SKILL.md (or your agent's skills folder). This skill also uses 1 other file; get the full folder from GitHub.This skill coordinates the KanVibe desktop release workflow from version selection to DMG publication, Homebrew cask update, release PR creation, and automatic merge. It is intentionally release-operator focused and may only be invoked from a clean, up-to-date local dev checkout: read and report the current package.json version, ask the user for the target release version, update the package version, run the pnpm run deploy DMG build, launch the built app and confirm it actually starts, get explicit approval for the release notes, upload the exact DMG artifact to the rookedsysc/kanvibe GitHub release, update the separate Homebrew cask repository with the same version and SHA-256, create the KanVibe release PR, then merge the release and promotion PRs automatically once checks and review-thread gates pass.
The smoke test in step 3.5 is not optional. Signing, notarization, stapling, and checksum verification all pass on a bundle whose app cannot start, and shipping one of those is how KanVibe 1.0.4 reached users.
The only routine user gates are target-version selection and release-note approval. After those are approved, do not ask for additional confirmation for build, release publication, cask update, PR creation, auto-merge enablement, or merge completion unless a blocker or branch-policy violation appears.
Before touching the cask, read references/homebrew-cask-repository.md. The cask repository location and cask file path live there so this skill stays portable if the tap checkout moves.
Use this skill when the user asks to:
pnpm run deploy for KanVibe release packaging;dist/KanVibe-<version>.dmg;1.0.2 where the expected DMG is dist/KanVibe-1.0.2.dmg.Do not use this skill for docs-site deploys, Linux-only package checks, routine feature PRs that do not publish a desktop release, or any checkout that is not clean, up-to-date dev.
1.0.2, not v1.0.2.KanVibe-<version>.dmg because electron-builder.yml sets dmg.artifactName: "KanVibe-${version}.${ext}".https://github.com/rookedsysc/kanvibe/releases/download/#{version}/KanVibe-#{version}.dmg.pnpm run deploy, not an earlier artifact.pnpm run deploy is the release build command for this workflow. It runs scripts/dist-deploy.cjs, which builds the DMG, codesigns, notarizes, staples, verifies the packaged dependencies, and prints the SHA-256.pnpm run deploy performs macOS signing/notarization, so it must run on macOS with Apple signing tools configured. Do not fabricate build, notarization, or checksum output from Linux.packageManager field to package.json. electron-builder resolves the package manager from that field before anything else, which selects a dependency collector that drops transitive dependencies from app.asar and ships an app that crashes on startup. Pin the pnpm version through pnpm/action-setup's version input in CI instead.packageManager pin, corepack may resolve pnpm 11, which ignores pnpm.onlyBuiltDependencies and skips every native build script. Verify with pnpm --version during preflight.dev checkout. Stop on main, feature branches, existing release branches, detached HEADs, dirty worktrees, or a local dev HEAD that differs from origin/dev.origin/dev. Do not accept, infer, or ask about alternate source branches for this workflow.main without asking for another confirmation.dev, or the visible PR diff contains files outside the expected release bump scope.Work from the KanVibe application repository, not the Homebrew tap.
pwd
git fetch origin --prune
CURRENT_BRANCH=$(git branch --show-current)
if [ "$CURRENT_BRANCH" != "dev" ]; then
echo "KanVibe release deploy must start from the local dev branch; current branch is ${CURRENT_BRANCH:-detached}." >&2
exit 1
fi
if [ -n "$(git status --short)" ]; then
echo "KanVibe release deploy must start from a clean dev worktree." >&2
git status --short
exit 1
fi
git pull --ff-only origin dev
if [ "$(git rev-parse HEAD)" != "$(git rev-parse origin/dev)" ]; then
echo "Local dev must match origin/dev before cutting a release branch." >&2
exit 1
fi
git status --short --branch
node -p "process.version"
CURRENT_VERSION=$(node -p "require('./package.json').version")
printf 'Current KanVibe version: %s\n' "$CURRENT_VERSION"
# The release build must run under pnpm 10; pnpm 11 ignores pnpm.onlyBuiltDependencies
# and silently skips every native build script.
PNPM_VERSION=$(pnpm --version)
printf 'pnpm: %s\n' "$PNPM_VERSION"
case "$PNPM_VERSION" in
10.*) ;;
*) echo "Release build requires pnpm 10; found ${PNPM_VERSION}." >&2; exit 1 ;;
esac
# package.json must NOT carry a packageManager field: it forces electron-builder into a
# dependency collector that drops transitive dependencies from app.asar.
if node -p "require('./package.json').packageManager" | grep -qv undefined; then
echo "package.json has a packageManager field; remove it before releasing." >&2
exit 1
fi
gh auth status
gh release list --limit 5 --repo rookedsysc/kanvibeIf pnpm --version is not 10.x, do not silently continue and do not add a packageManager field to fix it. Install or select pnpm 10 for this shell, then rerun preflight.
After printing the current version, ask the user which version to release before editing files:
현재 KanVibe 버전은 <CURRENT_VERSION>입니다. 배포 버전을 몇으로 올릴까요? 예: 1.0.2Only proceed after the user supplies the target version. If the original user prompt already supplied the exact target version, echo the current version and target version back to the user and proceed without asking again. This is the first of the two routine user gates; after the target version is selected, do not ask again until release-note review.
Validate these before proceeding:
x.y.z value such as 1.0.2; do not include a leading v or any prerelease/build suffix (for example 1.0.3-beta.1). The desktop update checker (src/desktop/shared/releaseUpdates.ts) only parses ^v?\d+\.\d+\.\d+$, so a prerelease tag would be invisible to in-app updates.package.json version unless the user explicitly wants to rebuild the same version.dev checkout before creating the release branch. If the current branch is not dev, stop and tell the user to rerun the skill from dev; do not silently proceed from main, a feature branch, or an existing release branch.dev matches origin/dev after git fetch/git pull --ff-only. The release source is always origin/dev; do not ask about or honor alternate source branches in this workflow.rookedsysc/kanvibe.uname -sIf the required host/tooling is unavailable, stop and report the blocker. Do not fake pnpm run deploy, DMG, notarization, or checksum output.
After the target version is selected, create a dedicated release branch from the current origin/dev SHA. Do not use an existing release branch as the invocation point; reuse an existing release branch only after the dev preflight passes and only when it is the same release version with no unrelated files. This keeps the release PR narrow and prevents dirty local feature work from leaking into the release.
TARGET_VERSION="<version supplied by user>"
BASE_BRANCH="dev"
RELEASE_BRANCH="release/${TARGET_VERSION}"
RELEASE_WT="/home/rookedsysc/Documents/kanvibe/kanvibe__worktrees/release-${TARGET_VERSION}"
git fetch origin --prune
SOURCE_SHA=$(git rev-parse "origin/${BASE_BRANCH}")
git worktree add -b "$RELEASE_BRANCH" "$RELEASE_WT" "$SOURCE_SHA"
cd "$RELEASE_WT"
printf 'Release source SHA: %s\n' "$SOURCE_SHA"
git status --short --branchIf the release worktree path or branch already exists, re-read its status and PR state before reusing it. Reuse only when it is the same release version and contains no unrelated files.
package.json VersionAfter the user chooses the target version, update package.json before running the build. Use a deterministic script instead of manually editing JSON punctuation.
The same script also keeps the tracked package-lock.json in sync, because its top-level version and root packages[""].version fields otherwise keep advertising the previous release and npm-based install/packaging paths would see conflicting metadata. The format check rejects prerelease/build suffixes so the published tag always matches the desktop update checker.
Both files contain several "version" lines, and only three of them belong to the release. The replacement therefore anchors on whole-line exact matches and, inside package-lock.json, on the packages[""] block boundary. It never matches substrings and never treats indentation as optional.
Every anchor is resolved in memory before the first writeFileSync, so a failed anchor leaves both files untouched. That ordering matters: a partial bump would leave package.json on the new version while package-lock.json stayed on the old one, and the already at ${version} guard would then reject the retry until someone manually ran git checkout -- package.json.
TARGET_VERSION="<version supplied by user>"
node -e '
const fs = require("node:fs");
const version = process.argv[1];
if (!/^\d+\.\d+\.\d+$/.test(version)) {
throw new Error(`Invalid release version (expected plain x.y.z, no v/prerelease): ${version}`);
}
const current = JSON.parse(fs.readFileSync("package.json", "utf8")).version;
if (current === version) {
throw new Error(`package.json is already at ${version}; pick a different target version`);
}
const rootNeedle = ` "version": "${current}",`;
const rootReplacement = ` "version": "${version}",`;
const findExactlyOneLine = (label, lines, needle) => {
const matches = lines.reduce((indexes, line, index) => (line === needle ? [...indexes, index] : indexes), []);
if (matches.length !== 1) {
throw new Error(`${label}: expected exactly 1 line matching ${JSON.stringify(needle)}, got ${matches.length}`);
}
return matches[0];
};
// package.json: the release version is the only two-space-indented version line.
// devEngines.packageManager.version is indented deeper and must survive untouched.
const packageLines = fs.readFileSync("package.json", "utf8").split("\n");
packageLines[findExactlyOneLine("package.json top-level version", packageLines, rootNeedle)] = rootReplacement;
const hasLock = fs.existsSync("package-lock.json");
let lockLines = null;
if (hasLock) {
lockLines = fs.readFileSync("package-lock.json", "utf8").split("\n");
lockLines[findExactlyOneLine("package-lock.json top-level version", lockLines, rootNeedle)] = rootReplacement;
// packages[""] is the root package block. Its six-space version line is textually identical
// to the one in every dependency that happens to sit on the same version, so scan only
// between the block opener and its closing brace.
const blockStart = lockLines.indexOf(` "": {`);
if (blockStart === -1) {
throw new Error("package-lock.json: packages[\"\"] block opener not found");
}
const innerNeedle = ` "version": "${current}",`;
let innerIndex = -1;
for (let i = blockStart + 1; i < lockLines.length; i++) {
if (lockLines[i] === " },") break;
if (lockLines[i] === innerNeedle) {
innerIndex = i;
break;
}
}
if (innerIndex === -1) {
throw new Error("package-lock.json: packages[\"\"].version line not found inside the root package block");
}
lockLines[innerIndex] = ` "version": "${version}",`;
}
// Every anchor is resolved by this point, so the writes happen together or not at all.
// Writing package.json before the lock anchors were checked used to leave a half-bumped
// tree on a lock failure, and the "already at" guard above then refused the retry.
fs.writeFileSync("package.json", packageLines.join("\n"));
if (hasLock) {
fs.writeFileSync("package-lock.json", lockLines.join("\n"));
}
' "$TARGET_VERSION"
node -p "require('./package.json').version"
node -p "require('./package.json').devEngines.packageManager.version"
test -f package-lock.json && node -e 'const lock = require("./package-lock.json"); console.log(lock.version, lock.packages[""].version);'
git diff --numstat -- package.json package-lock.jsonThe bump is correct only when all four hold:
package.json version and both package-lock.json versions print the user-selected target version. Re-reading them through require also proves the files still parse as JSON.devEngines.packageManager.version still prints the pnpm pin, not the release version.git diff --numstat reports 1 1 package.json and 2 2 package-lock.json. Any other count means the edit escaped its anchors.git status.Commit the version bump (including package-lock.json) with the release changes or ensure the release tag targets a commit that already contains the updated files.
Three earlier approaches were tried and discarded. Do not reintroduce them.
| Approach | Used in | Defect |
|---|---|---|
JSON.parse → JSON.stringify round-trip | up to 1.0.8 | Reserializes the whole file, so unrelated lines land in the release diff. |
^(\s*)"version": "\d+\.\d+\.\d+" regex | 1.0.8 | \s* also matches the deeper indentation of devEngines.packageManager.version, which rewrites the pnpm pin to the release version. |
text.split(<literal indented needle>) | 1.1.0 | split has no concept of line boundaries. A six-space dependency line contains the two-space needle as a substring, so the anchor is not isolated. |
The third defect stayed invisible in 1.1.0 only by luck: the then-current version 1.0.10 happened to match no dependency. In 1.2.0 the current version was 1.1.0, a common value that twenty dependencies in package-lock.json also carried, and the two-space needle matched 22 places. The safety of that anchor depended on how rare the current version string happened to be — which is not a property a release procedure may rely on.
KanVibe requires Node 24.x. If node -p "process.version" is not v24, switch to Node 24 with the local toolchain available on that Mac before running the release build.
Run the release build command after the version bump:
pnpm install --frozen-lockfile
pnpm run deployExpected artifact for version 1.0.2:
dist/KanVibe-1.0.2.dmgFor any version, derive and verify the artifact path from package.json:
VERSION=$(node -p "require('./package.json').version")
DMG="dist/KanVibe-${VERSION}.dmg"
test -f "$DMG"
shasum -a 256 "$DMG"Keep the checksum from this final DMG for the cask update. If you rebuild for any reason, the checksum changes — always re-hash the artifact you are actually publishing.
scripts/dist-deploy.cjs prints the dependency-collection mode and a packaging guard result. Both lines must appear and must look like this:
• searching for node modules pm=npm searchDir=/Users/<user>/Documents/kanvibe/kanvibe
[kanvibe] packaged node_modules verified: 278 packagesIf the collector reports pm=pnpm, the build is wrong even though it will succeed: that collector silently omits transitive dependencies. Check that package.json has no packageManager field and that the deploy script is invoking electron-builder directly, then rebuild. The guard aborts the deploy when required modules are missing, but treat pm=pnpm itself as a failure signal.
When pnpm run deploy fails with HTTP status code: 403. A required agreement is missing or has expired, this is an Apple account state problem, not a build problem. Do not rebuild to retry — check the credential cheaply instead:
xcrun notarytool history --key "$APPLE_API_KEY" --key-id "$APPLE_API_KEY_ID" --issuer "$APPLE_API_ISSUER"If a previous release notarized successfully with the same key and team, the configuration is fine and only the agreement changed. The Account Holder must accept the pending agreement at developer.apple.com, and propagation to the notary service takes a few minutes. Poll the command above and resume the build once it returns history.
Do not publish a release without this step. Code signing, notarization, stapling, and checksum verification all pass on a bundle whose app cannot start. KanVibe 1.0.4 shipped exactly that way: the DMG was signed, notarized, stapled, and its checksum matched, but app.asar was missing seven transitive dependencies and the app died on launch with Cannot find module 'ms'.
Mount the DMG you are about to publish, copy the app out, launch it against a throwaway data directory, and read that run's own diagnostics log:
VERSION=$(node -p "require('./package.json').version")
SMOKE_DATA=$(mktemp -d /tmp/kanvibe-smoke-XXXXXX)
LOG="$SMOKE_DATA/logs/kanvibe-desktop.log"
hdiutil attach "dist/KanVibe-${VERSION}.dmg" -nobrowse -readonly -quiet
MOUNT="/Volumes/KanVibe ${VERSION}-arm64"
spctl -a -t exec -vv "$MOUNT/KanVibe.app"
rm -rf /tmp/KanVibe-smoke.app
cp -R "$MOUNT/KanVibe.app" /tmp/KanVibe-smoke.app
hdiutil detach "$MOUNT" -quiet
open -n --env "KANVIBE_APP_DATA_DIR=$SMOKE_DATA" -a /tmp/KanVibe-smoke.app
sleep 15
pgrep -f "KanVibe-smoke.app/Contents/MacOS/KanVibe" >/dev/null || { echo "app exited during startup" >&2; exit 1; }
wc -l "$LOG"
grep -icE "cannot find module|MODULE_NOT_FOUND|unhandled rejection" "$LOG"
grep -c "invoke-succeeded" "$LOG"
pkill -f "KanVibe-smoke.app"
rm -rf /tmp/KanVibe-smoke.app "$SMOKE_DATA"The release may proceed only when all four hold:
spctl -a -t exec reports accepted and source=Notarized Developer ID.0.invoke-succeeded count is greater than zero, which proves the database and IPC layer actually came up rather than the window merely opening.Read the counts from $LOG inside $SMOKE_DATA, never from ~/Library/Application Support/kanvibe. Do not delete the user's log to get a clean read.
electron/main.js calls applyAppDataDirectoryOverride(app, process.env) at module load, well before app.whenReady(). That helper (electron/runtimeEnvironment.js) turns KANVIBE_APP_DATA_DIR into app.setPath("userData", ...), and the diagnostics log path is derived from userData (electron/diagnostics.js), so the smoke run's database and its log both follow the temporary directory. The user's installed instance, its data, and its existing log are left alone — during the 1.2.0 smoke the verdict came from a 43-line isolated log while /Applications/KanVibe.app kept running untouched.
open --env applies the variable to the launched app only and does not touch the shell, so this stays inside the repository's runtime-environment safety rules. open -n is required: without it macOS just focuses an already-running instance instead of starting the copy under test.
If the smoke test fails, stop. Do not publish, do not update the cask, and do not open PRs. Diagnose the bundle first; app.asar contents can be listed from its header:
# Run this twice: once for the candidate bundle, once for a build known to be good.
ASAR_PATH="dist/mac-arm64/KanVibe.app/Contents/Resources/app.asar"
# ASAR_PATH="/Applications/KanVibe.app/Contents/Resources/app.asar"
node -e '
const fs=require("fs");
const p=process.argv[1] || "dist/mac-arm64/KanVibe.app/Contents/Resources/app.asar";
const fd=fs.openSync(p,"r");
const b=Buffer.alloc(16); fs.readSync(fd,b,0,16,0);
const hb=Buffer.alloc(b.readUInt32LE(12)); fs.readSync(fd,hb,0,hb.length,16);
const h=JSON.parse(hb.toString("utf8").replace(/\0+$/,""));
fs.closeSync(fd);
const entries=Object.entries(h.files.node_modules.files);
const expanded=entries.reduce((n,[name,entry])=>n+(name.startsWith("@")?Object.keys(entry.files??{}).length:1),0);
console.log(`${entries.length} top-level entries (scopes counted once)`);
console.log(`${expanded} packages with scopes expanded <-- matches the build guard`);
' "$ASAR_PATH"The two numbers are both correct and they are not interchangeable. The build guard line packaged node_modules verified: N packages comes from readAsarNodeModuleNames() in scripts/dist-deploy.cjs, which expands @scope/name into one entry per scoped package, so it always reports the larger number. Counting the top-level keys instead reports the smaller one. Reading the smaller number as if it were the guard's makes a healthy build look like the 1.0.4 dependency-loss failure.
Do not judge either number against a remembered absolute value; package counts move whenever dependencies change. Judge by comparison: rerun the same snippet with ASAR_PATH pointed at a build known to be good — the installed /Applications/KanVibe.app/Contents/Resources/app.asar is the convenient one — and compare it with the candidate bundle. The || fallback rather than ?? is deliberate, because an unset ASAR_PATH still passes an empty-string argument that ?? would accept as a path.
Draft release notes in a temporary markdown file. Match the existing release-note tone: concise sections such as ## New Features, ## Improvements and Stability, and ## Packaging Note. Include a packaging note naming the exact DMG artifact, for example:
## Packaging Note
- The version was updated from `1.0.1` to `1.0.2` before building.
- The DMG artifact is `KanVibe-1.0.2.dmg`.Publishing a GitHub release is outward-facing, so the release notes must be reviewed and approved by the user before any gh release create or gh release edit runs. This is the second and final routine user gate. After approval, continue through commit/push, release publication, cask update, release PR creation, and auto-merge without asking for more confirmation unless a blocker appears.
$RELEASE_NOTES).$RELEASE_NOTES with the corrected English text, re-translate, and present both versions again. Repeat until approved.Create the release with the DMG asset only after the user approves the notes. Use the raw version tag and upload the generated DMG, not a directory or renamed copy.
After release-note approval, commit and push the release branch before deriving the tag target. The target SHA must already exist in GitHub, and the visible release PR diff must stay limited to the version bump files. If extra files appear, stop and report the blocker instead of tagging or merging.
VERSION=$(node -p "require('./package.json').version")
DMG="dist/KanVibe-${VERSION}.dmg"
RELEASE_NOTES="/tmp/kanvibe-release-${VERSION}.md"
BASE_BRANCH="dev"
RELEASE_BRANCH="${RELEASE_BRANCH:-release/${VERSION}}"
# Verify the release branch diff is only the expected version bump against the required dev base.
git fetch origin --prune
git diff --name-status "origin/${BASE_BRANCH}"
node -e '
const { execSync } = require("node:child_process");
const baseBranch = process.argv[1];
const allowed = new Set(["package.json", "package-lock.json"]);
const files = execSync(`git diff --name-only origin/${baseBranch}`, { encoding: "utf8" })
.trim()
.split(/\n/)
.filter(Boolean);
const unexpected = files.filter((file) => !allowed.has(file));
if (unexpected.length > 0) {
throw new Error(`Unexpected release PR files: ${unexpected.join(", ")}`);
}
' "$BASE_BRANCH"
git add package.json package-lock.json
if ! git diff --cached --quiet; then
git commit -m "chore(release): publish KanVibe ${VERSION}"
fi
git push -u origin "$RELEASE_BRANCH"
# Fail fast if the committed release branch does not contain the selected version.
if ! git diff --quiet -- package.json package-lock.json || ! git diff --cached --quiet -- package.json package-lock.json; then
echo "package.json/package-lock.json have uncommitted changes; commit the version bump before tagging the release." >&2
exit 1
fi
HEAD_VERSION=$(git show HEAD:package.json | node -p "JSON.parse(require('fs').readFileSync(0,'utf8')).version")
if [ "$HEAD_VERSION" != "$VERSION" ]; then
echo "HEAD package.json version ($HEAD_VERSION) does not match target ($VERSION); commit the bump onto the release commit first." >&2
exit 1
fi
TARGET_SHA=$(git rev-parse HEAD)
gh release create "$VERSION" "$DMG" \
--repo rookedsysc/kanvibe \
--target "$TARGET_SHA" \
--title "KanVibe ${VERSION} Release Notes" \
--notes-file "$RELEASE_NOTES" \
--latestIf the release already exists, do not create a duplicate. Update the notes/title if needed and upload the DMG with --clobber:
gh release edit "$VERSION" \
--repo rookedsysc/kanvibe \
--title "KanVibe ${VERSION} Release Notes" \
--notes-file "$RELEASE_NOTES"
gh release upload "$VERSION" "$DMG" \
--repo rookedsysc/kanvibe \
--clobberVerify the release and asset URL:
gh release view "$VERSION" --repo rookedsysc/kanvibe --json tagName,name,assets,url
curl -I -L "https://github.com/rookedsysc/kanvibe/releases/download/${VERSION}/KanVibe-${VERSION}.dmg"Read references/homebrew-cask-repository.md first and use its local repository path and cask file path.
Compute the checksum from the exact DMG uploaded to the release:
VERSION=$(node -p "require('./package.json').version")
DMG="dist/KanVibe-${VERSION}.dmg"
SHA256=$(shasum -a 256 "$DMG" | cut -d ' ' -f 1)
printf '%s\n' "$SHA256"In the Homebrew cask repository:
main.version and sha256 lines in Casks/kanvibe.rb.#{version} and KanVibe-#{version}.dmg.Command outline:
CASK_REPO="<read from references/homebrew-cask-repository.md>"
CASK_FILE="$CASK_REPO/Casks/kanvibe.rb"
git -C "$CASK_REPO" status --short --branch
git -C "$CASK_REPO" pull --ff-only origin main
ruby -c "$CASK_FILE"
# Use file tools or a small script to replace version and sha256 with $VERSION and $SHA256.
ruby -c "$CASK_FILE"
git -C "$CASK_REPO" diff -- Casks/kanvibe.rb
git -C "$CASK_REPO" add Casks/kanvibe.rb
git -C "$CASK_REPO" commit -m "Update KanVibe cask to ${VERSION}"
git -C "$CASK_REPO" push origin mainHomebrew rejects casks that live outside a tap, so brew fetch --cask Casks/kanvibe.rb against the local checkout fails with Homebrew requires casks to be in a tap. Verify in two stages instead.
Before pushing, check the syntax and prove the checksum against the bytes GitHub actually serves:
ruby -c "$CASK_FILE"
curl -sL "https://github.com/rookedsysc/kanvibe/releases/download/${VERSION}/KanVibe-${VERSION}.dmg" | shasum -a 256That hash must equal the sha256 written into the cask. After pushing, verify through the real tap name:
brew update
brew fetch --cask rookedsysc/kanvibe/kanvibe # expect: ✔︎ Cask kanvibe (<version>)After the GitHub release and Homebrew cask are published, the release commit must land on main so main reflects the shipped version. The target-version selection and release-note approval are sufficient authorization for the remaining PR work. Do not ask for another merge confirmation; instead, create the PRs, verify the gates, and merge or enable auto-merge automatically.
Stop and report a blocker only if checks fail, a PR has active unresolved review threads, GitHub says the PR is not mergeable, required permissions are missing, or the PR file list is outside the expected release scope.
devCreate or reuse the release PR from release/<version> to dev. Write a concise English PR body with Summary, Test Plan, GitHub release URL, DMG asset path, SHA-256, and Homebrew cask commit/status.
VERSION=$(node -p "require('./package.json').version")
RELEASE_BRANCH="${RELEASE_BRANCH:-release/${VERSION}}"
BASE_BRANCH="dev"
RELEASE_PR_BODY="/tmp/kanvibe-release-pr-${VERSION}.md"
# Use file tools to write $RELEASE_PR_BODY before running gh pr create.
RELEASE_PR=$(gh pr list --repo rookedsysc/kanvibe --base "$BASE_BRANCH" --head "$RELEASE_BRANCH" --state open --json number --jq '.[0].number // empty')
if [ -z "$RELEASE_PR" ]; then
gh pr create --repo rookedsysc/kanvibe --base "$BASE_BRANCH" --head "$RELEASE_BRANCH" \
--title "chore(release): publish KanVibe ${VERSION}" \
--body-file "$RELEASE_PR_BODY" \
--assignee @me
RELEASE_PR=$(gh pr list --repo rookedsysc/kanvibe --base "$BASE_BRANCH" --head "$RELEASE_BRANCH" --state open --json number --jq '.[0].number')
fi
RELEASE_BASE=$(gh pr view "$RELEASE_PR" --repo rookedsysc/kanvibe --json baseRefName --jq .baseRefName)
test "$RELEASE_BASE" = "dev"
gh pr view "$RELEASE_PR" --repo rookedsysc/kanvibe --json url,baseRefName,headRefName,headRefOid,mergeable,assignees,files,statusCheckRollup
RELEASE_HEAD_SHA=$(gh pr view "$RELEASE_PR" --repo rookedsysc/kanvibe --json headRefOid --jq .headRefOid)
printf 'Release PR head SHA: %s\n' "$RELEASE_HEAD_SHA"Verify the release PR file list before merging. It should normally contain only package.json and package-lock.json. If a release-specific file is intentionally added, name it in the PR body and final handoff; otherwise stop.
This file-scope check applies only to the release PR into dev. The promotion PR into main legitimately carries every commit that main is behind by, so a large diff there is expected and is not a blocker.
gh pr view "$RELEASE_PR" --repo rookedsysc/kanvibe --json files --jq '.files[].path'
gh pr checks "$RELEASE_PR" --repo rookedsysc/kanvibe --watchCheck active review threads before merging:
UNRESOLVED_THREADS=$(gh api graphql \
-f owner=rookedsysc -f name=kanvibe -F number="$RELEASE_PR" \
-f query='query($owner:String!, $name:String!, $number:Int!) { repository(owner:$owner, name:$name) { pullRequest(number:$number) { reviewThreads(first:100) { nodes { isResolved isOutdated } } } } }' \
--jq '[.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false and .isOutdated == false)] | length')
test "$UNRESOLVED_THREADS" = "0"Merge automatically when checks are green. If checks are still pending but the PR is otherwise mergeable, enable auto-merge and keep watching until the PR becomes MERGED before starting the promotion PR. Never enable auto-merge after a failed, cancelled, timed-out, or action-required check. Auto-merge on this repository can land the PR before the pending checks finish; see the note below.
CHECK_STATE=$(gh pr checks "$RELEASE_PR" --repo rookedsysc/kanvibe --json state --jq '
if any(.[]; .state == "FAILURE" or .state == "CANCELLED" or .state == "TIMED_OUT" or .state == "ACTION_REQUIRED") then "failure"
elif all(.[]; .state == "SUCCESS" or .state == "SKIPPED") then "success"
else "pending" end
')
case "$CHECK_STATE" in
success)
gh pr merge "$RELEASE_PR" --repo rookedsysc/kanvibe --merge
;;
pending)
gh pr merge "$RELEASE_PR" --repo rookedsysc/kanvibe --auto --merge
gh pr checks "$RELEASE_PR" --repo rookedsysc/kanvibe --watch
;;
failure)
echo "Release PR checks failed; do not enable auto-merge." >&2
exit 1
;;
esac
gh pr view "$RELEASE_PR" --repo rookedsysc/kanvibe --json state,mergeCommit,mergedBy,headRefNamedev has no required status checks:
gh api repos/rookedsysc/kanvibe/branches/dev/protection --jq '.required_status_checks.contexts'This returns an empty list, which means Type Check & Test is not a merge gate. gh pr merge --auto has nothing to wait for, so a PR whose checks read pending can merge immediately. Do not read a pending line in gh pr checks as "waiting to merge"; it only says the workflow has not finished yet. In the 1.2.0 release, PR #359 merged while this check was still in_progress — that followed the procedure, but the merge landed ahead of the CI conclusion.
Because the merge does not wait, confirm the release commit's CI conclusion separately after merging:
gh run list --repo rookedsysc/kanvibe --commit "$RELEASE_HEAD_SHA" --json databaseId,name,status,conclusion
gh run view <databaseId> --repo rookedsysc/kanvibeRELEASE_HEAD_SHA (captured in §6.1) is the right commit to query, because it is the commit the DMG was built from. The merge commit on dev is a different SHA with its own runs; it answers "is dev green", not "did the released code pass".
Every run for that commit must end with conclusion: success. A failed conclusion after a completed merge is a blocker: report it instead of continuing quietly.
Delete release branches only after that conclusion exists. Deleting a branch cancels workflow runs still in progress on it, which destroys the evidence this step depends on.
mainAfter the release PR is merged into dev, promote the same pinned release branch SHA to main; do not use the moving dev branch as the promotion PR head. This keeps main aligned with the exact TARGET_SHA used for the DMG release instead of accidentally merging commits that landed on dev after the release branch was cut.
# RELEASE_HEAD_SHA was captured from the release PR before merge. Recreate the remote branch if the repository auto-deleted it after the dev merge.
git fetch origin main dev --prune
PROMOTION_BRANCH="${PROMOTION_BRANCH:-$RELEASE_BRANCH}"
if ! git ls-remote --exit-code --heads origin "$PROMOTION_BRANCH" >/dev/null 2>&1; then
git push origin "${RELEASE_HEAD_SHA}:refs/heads/${PROMOTION_BRANCH}"
fi
PROMOTION_PR=$(gh pr list --repo rookedsysc/kanvibe --base main --head "$PROMOTION_BRANCH" --state open --json number --jq '.[0].number // empty')
if [ -z "$PROMOTION_PR" ]; then
gh pr create --repo rookedsysc/kanvibe --base main --head "$PROMOTION_BRANCH" \
--title "Release: promote KanVibe ${VERSION} to main" \
--body "Promote the pinned ${VERSION} release branch to main after the published DMG release and Homebrew cask update." \
--assignee @me
PROMOTION_PR=$(gh pr list --repo rookedsysc/kanvibe --base main --head "$PROMOTION_BRANCH" --state open --json number --jq '.[0].number')
fi
PROMOTION_HEAD_SHA=$(gh pr view "$PROMOTION_PR" --repo rookedsysc/kanvibe --json headRefOid --jq .headRefOid)
test "$PROMOTION_HEAD_SHA" = "$RELEASE_HEAD_SHA"
gh pr view "$PROMOTION_PR" --repo rookedsysc/kanvibe --json url,baseRefName,headRefName,headRefOid,mergeable,assignees,statusCheckRollup
UNRESOLVED_THREADS=$(gh api graphql \
-f owner=rookedsysc -f name=kanvibe -F number="$PROMOTION_PR" \
-f query='query($owner:String!, $name:String!, $number:Int!) { repository(owner:$owner, name:$name) { pullRequest(number:$number) { reviewThreads(first:100) { nodes { isResolved isOutdated } } } } }' \
--jq '[.data.repository.pullRequest.reviewThreads.nodes[] | select(.isResolved == false and .isOutdated == false)] | length')
test "$UNRESOLVED_THREADS" = "0"
CHECK_STATE=$(gh pr checks "$PROMOTION_PR" --repo rookedsysc/kanvibe --json state --jq '
if any(.[]; .state == "FAILURE" or .state == "CANCELLED" or .state == "TIMED_OUT" or .state == "ACTION_REQUIRED") then "failure"
elif all(.[]; .state == "SUCCESS" or .state == "SKIPPED") then "success"
else "pending" end
')
case "$CHECK_STATE" in
success)
gh pr merge "$PROMOTION_PR" --repo rookedsysc/kanvibe --merge
;;
pending)
gh pr merge "$PROMOTION_PR" --repo rookedsysc/kanvibe --auto --merge
gh pr checks "$PROMOTION_PR" --repo rookedsysc/kanvibe --watch
;;
failure)
echo "Promotion PR checks failed; do not enable auto-merge." >&2
exit 1
;;
esac
PROMOTION_STATE=$(gh pr view "$PROMOTION_PR" --repo rookedsysc/kanvibe --json state,mergeCommit,mergedBy --jq .state)
printf 'Promotion PR state: %s\n' "$PROMOTION_STATE"
git fetch origin main
MAIN_VERSION=$(git show origin/main:package.json | node -p "JSON.parse(require('fs').readFileSync(0,'utf8')).version")
test "$MAIN_VERSION" = "$VERSION"
# Clean up the release branch only after the main promotion is merged and the release commit's
# workflow runs have reached a conclusion. Deleting the branch cancels runs still in progress,
# so the pending-run count below is the gate, not just something to read.
if [ "$PROMOTION_STATE" = "MERGED" ]; then
gh run list --repo rookedsysc/kanvibe --commit "$RELEASE_HEAD_SHA" --json name,status,conclusion
PENDING_RUNS=$(gh run list --repo rookedsysc/kanvibe --commit "$RELEASE_HEAD_SHA" \
--json status --jq '[.[] | select(.status != "completed")] | length')
if [ "$PENDING_RUNS" != "0" ]; then
echo "release commit runs still in progress ($PENDING_RUNS); skip branch cleanup" >&2
else
git push origin --delete "$PROMOTION_BRANCH" || true
fi
fiBefore reporting success, collect real output for:
dev, clean status, and local HEAD matching origin/dev before the release branch was cut;pnpm --version showing 10.x and package.json carrying no packageManager field;git diff --numstat -- package.json package-lock.json showing 1 1 package.json and 2 2 package-lock.json, plus devEngines.packageManager.version still on the pnpm pin;pnpm run deploy completion, including the pm=npm collector line and the packaged node_modules verified: N packages guard line;spctl verdict, process alive after launch, module-error count 0, and a non-zero invoke-succeeded count;test -f dist/KanVibe-<version>.dmg and shasum -a 256;gh release view <version> showing the KanVibe-<version>.dmg asset;curl -I -L against the release asset URL returning an HTTP success/redirect chain rather than a 404;version and sha256 changes, plus the cask repository push result or exact blocker;dev;gh run list --commit <sha>, collected after the merge because a pending check does not hold it;main;git show origin/main:package.json confirming main now contains the release version, or the exact blocker if auto-merge is still pending.Final handoff format:
완료했습니다.
- Dev preflight: <branch/status/local HEAD == origin/dev evidence>
- Previous version: <current version reported before bump>
- Release version: <target version selected by user>
- DMG: `dist/KanVibe-<version>.dmg`
- SHA-256: `<sha256>`
- Packaging guard: <collector mode + verified package count>
- Smoke test: <app alive / module errors / invoke-succeeded count>
- GitHub release: <release URL>
- Homebrew cask: <commit SHA or branch/status>
- Release PR: <URL> → <merged/auto-merge/blocker>
- Release commit CI: <workflow conclusion for the release commit>
- Promotion PR: <URL> → <merged/auto-merge/blocker>
- main version: <confirmed version or pending reason>
- Verification:
- `<command>` → <real result>
- `<command>` → <real result>package.json before pnpm run deploy, then derive the DMG path from the updated version.pnpm run deploy requires macOS codesign, notarytool, and stapler, so stop honestly when the host is not Darwin.v tag. The cask URL uses the raw version as the release tag. v1.0.2 will break the current cask URL..env contents or Apple API key material in release notes, logs, commits, or Discord output.gh release create/gh release edit.main without another routine confirmation.dev. Do not create the main promotion PR with --head dev; use the pinned release branch/SHA so commits that land on dev after the DMG build cannot ride along into main.dev checkout. This workflow is dev-only at invocation. Stop instead of switching branches, inferring a different source, or running directly from main, feature branches, release branches, or detached HEADs.pnpm deploy. deploy is a reserved pnpm command (workspace deploy), so pnpm deploy fails with ERR_PNPM_CANNOT_DEPLOY and never runs the release script. Always invoke the release build as pnpm run deploy.app.asar was missing seven transitive dependencies. Step 3.5 is the only check that catches this class of failure.packageManager field to package.json. electron-builder reads it before lock files and before the environment, which selects a dependency collector that drops transitive dependencies in this repository's hoisted layout. This is what broke 1.0.4. Pin pnpm through CI's pnpm/action-setup version input instead.pm= collector line. The build succeeds either way, so pm=pnpm is easy to scroll past. It means the artifact is probably incomplete — treat it as a failure, not a detail.xcrun notarytool history and, if an earlier release succeeded with the same key, wait for propagation rather than spending build cycles.open -a. Running KanVibe.app/Contents/MacOS/KanVibe as a child of the terminal makes macOS attribute the app's file-access prompt to the terminal process; a denial there revokes ~/Documents access for the whole process tree and blocks the rest of the release.dev. The promotion PR into main carries every commit main is behind by, so a wide diff there is normal.spctl -a -t open on the DMG reports rejected / no usable signature and the app inside reports "does not have a ticket stapled" even for a correct release — the ticket is stapled to the DMG, and the disk image itself is not Developer ID signed. Judge with xcrun stapler validate <dmg> and spctl -a -t exec on the app.© rookedsysc, 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
SKILL.md and 1 other file (references) in .claude/skills/kanvibe-release-deploy of rookedsysc/kanvibe.
Open the folder on GitHubat commit 54cebc8
Kanvibe Release Deploy 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 |
|---|---|---|---|---|---|---|
| Kanvibe Release Deploy this skillrookedsysc/kanvibe | 144 | — | ~12k | Automated safety check: Notes | AGPL-3.0 | |
| Release Clawpatchopenclaw/clawpatch | 813 | — | ~1.1k | Automated safety check: Pass | MIT | |
| Releasecyanfish-x/tellux | 207 | — | ~1.3k | Automated safety check: Pass | MIT | |
| Verdaccio Pull Request Workflowverdaccio/verdaccio | 18k | — | ~1.9k | Automated safety check: Pass | MIT | |
| ZCF Release AutomationUfoMiao/zcf | 6.1k | — | ~3.4k | Automated safety check: Pass | MIT | |
| AnyDrag Release RoutineXueshiQiao/AnyDrag | 226 | — | ~2.6k | Automated safety check: Pass | GPL-3.0 |
openclaw/clawpatch
clawpatch release: version/changelog, CI, npm publish, GitHub release, verify.
cyanfish-x/tellux
Cut and publish a new tellux release — bump version, curate a changelog summary from recent commits, pause for the user to manually pnpm publish (browser 2FA), then push the tag and create the…
verdaccio/verdaccio
Takes a change through a verdaccio pull request: branch, local checks, changeset, title and body, labels, CI and review rounds, and ports to other release lines.
UfoMiao/zcf
Automates a version release with changesets: analyzes code changes, writes a bilingual CHANGELOG, bumps the version and commits through a release branch and pull request.
XueshiQiao/AnyDrag
Runs the full AnyDrag release process end to end, from cumulative bilingual release notes through version bumping to watching CI and the Homebrew cask update.
jillesvangurp/kt-search
A skill your agent uses when the user wants to cut, publish, tag, or create a GitHub release for kt-search, especially when the task includes version bumping, validating that commits are pushed…
Categories
A skill your agent uses whenever releasing or deploying KanVibe desktop from a clean, up-to-date dev checkout: ask only for the target version and release-note approval, then let the AI update…. Kanvibe Release Deploy is an agent skill from rookedsysc/kanvibe. Use this skill whenever releasing or deploying KanVibe desktop from a clean, up-to-date dev checkout: ask only for the target version and release-note approval, then let the AI update package versions, run pnpm run deploy, publish the DMG GitHub release, update the Homebrew cask, create the release PR, and auto-merge the release/promotion PRs when checks pass.
Kanvibe Release Deploy fits situations like: deploying KanVibe desktop from a clean; up-to-date dev checkout: ask only for the target version and release-note approval; then let the AI update package versions; run pnpm run deploy.
Run `npx skills add rookedsysc/kanvibe --skill kanvibe-release-deploy -a claude-code`. Or copy the skill folder (.claude/skills/kanvibe-release-deploy in rookedsysc/kanvibe) into .claude/skills/kanvibe-release-deploy in your project. Claude Code loads it when a task matches its description.
Run `npx skills add rookedsysc/kanvibe --skill kanvibe-release-deploy -a codex`. Or copy the skill folder (.claude/skills/kanvibe-release-deploy in rookedsysc/kanvibe) into .agents/skills/kanvibe-release-deploy 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 rookedsysc/kanvibe --skill kanvibe-release-deploy -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/kanvibe-release-deploy, .gemini/skills/kanvibe-release-deploy, .github/skills/kanvibe-release-deploy and .opencode/skills/kanvibe-release-deploy in your project.
Going by SKILL.md and its folder, Kanvibe Release Deploy needs the command-line tools its instructions call (gh, git, pnpm, node, xcrun and curl) and credentials named APPLE_API_KEY and APPLE_API_KEY_ID.
SKILL.md contains no URLs. Its commands use gh, git and curl, 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 notes only (mentions a .env file), nothing it rates as a warning. It is not a guarantee. Review the folder before installing.
Kanvibe Release Deploy 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 12k tokens (SKILL.md is roughly 47k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full. Its references folder adds about 513 tokens, read only when the agent opens those files.
Skills that share tags, products or a category with Kanvibe Release Deploy: Release Clawpatch (openclaw/clawpatch, 813 stars), Release (cyanfish-x/tellux, 207 stars), Verdaccio Pull Request Workflow (verdaccio/verdaccio, 18k stars) and ZCF Release Automation (UfoMiao/zcf, 6.1k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
rookedsysc (a GitHub user) maintains it in rookedsysc/kanvibe, which has 144 GitHub stars. The repository was last updated on October 10, 2026.
Source: rookedsysc/kanvibe on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.