agent-skills/skill-publish/SKILL.md
Full publish workflow for @hasna npm packages. Runs tests, logical commits, push, OSS checks (if hasna org), posts publish intent to git-publishing, publishes to npm, installs locally via bun, verifies via CLI, confirms in-thread. Never uses npm to install.
npx skillsauth add hasna/skills skill-publishInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Complete end-to-end publish for @hasna/* packages. Covers OSS requirements,
version bump, validation, PR merge, publish intent to git-publishing, npm
registry publish, bun install, CLI verification, and in-thread confirmation.
# Detect org from git remote
REMOTE=$(git remote get-url origin)
ORG=$(echo $REMOTE | grep -oP '(?<=github\.com[:/])[^/]+')
PKG=$(cat package.json | python3 -c "import sys,json; print(json.load(sys.stdin)['name'])")
echo "Org: $ORG | Package: $PKG"
If org is hasna (public open source), verify before doing anything else:
LICENSE — Must be Apache 2.0:
[ -f LICENSE ] || echo "MISSING: LICENSE file"
grep -q "Apache" LICENSE 2>/dev/null || echo "WRONG LICENSE: must be Apache-2.0"
If missing, create it (full Apache 2.0 text). Update package.json → "license": "Apache-2.0".
README.md — Must exist with at minimum: package name, description, install command, basic usage.
package.json fields required:
"license": "Apache-2.0""description" — non-empty"repository" — pointing to github.com/hasna/<name>"publishConfig": { "registry": "https://registry.npmjs.org", "access": "public" }GitHub repo visibility — Hasna OSS packages must be public, but changing repo visibility is a risky disclosure action. Check it and stop for explicit human approval if it is private; do not auto-flip visibility inside this skill.
if gh repo view --json isPrivate -q '.isPrivate' | grep -q true; then
echo "BLOCKED: repo is private; get explicit approval and complete a full history/secret review before making it public"
exit 1
fi
If org is hasnaxyz (private), ensure:
"publishConfig": { "access": "restricted" }Do release edits on a branch, never directly on main.
git switch -c "release/${PKG##*/}-$(date -u +%Y%m%d%H%M%S)"
# Check current published version. Prefer bun; fall back to npm only if present.
CURRENT=$(bun pm view "$PKG" version 2>/dev/null || npm view "$PKG" version 2>/dev/null || echo "0.0.0")
echo "Current: $CURRENT"
# Bump patch in package.json unless the user explicitly requested another level.
node -e "
const fs = require('fs');
const pkg = JSON.parse(fs.readFileSync('package.json'));
const parts = pkg.version.split('.').map(Number);
parts[2]++;
pkg.version = parts.join('.');
fs.writeFileSync('package.json', JSON.stringify(pkg, null, 2) + '\n');
console.log('New version:', pkg.version);
"
bun test 2>&1
[ "$(cat package.json | python3 -c "import sys,json; print('build' in json.load(sys.stdin).get('scripts',{}))")" = "True" ] && bun run build
If tests fail → fix them before continuing. Never publish broken tests.
git add package.json bun.lock
SECRET_PATTERN='sk-(ant|proj)-|npm_[[:alpha:]]|gh[op]_|AKIA[[:upper:][:digit:]]|xai[-]|AI[z]a'
if git diff --cached --diff-filter=ACM --unified=0 | grep -iE "^\\+[^+].*($SECRET_PATTERN)"; then
echo "SECRETS - STOP"
exit 1
fi
NEW_VERSION=$(cat package.json | python3 -c "import sys,json; print(json.load(sys.stdin)['version'])")
git commit -m "chore: release $PKG@$NEW_VERSION"
git push -u origin HEAD
Open a PR for the release branch. Wait for required CI and an independent review,
then merge it. After merge, update local main and verify that package.json
on main contains the exact version you are about to publish. Do not publish
from an unmerged branch or unreviewed local commit.
gh pr create --fill
# after CI/review approval:
gh pr merge --squash --delete-branch
git switch main
git pull --ff-only origin main
test "$(cat package.json | python3 -c "import sys,json; print(json.load(sys.stdin)['version'])")" = "$NEW_VERSION"
Fleet rule: publish intent goes to the git-publishing channel BEFORE any
bun publish/npm publish, and the result is confirmed in the same thread
after. Re-check blockers first — an unread [FREEZE] means stop and escalate
to help.
conversations blockers --from <agent> # unread [FREEZE] → STOP
NEW_VERSION=$(cat package.json | python3 -c "import sys,json; print(json.load(sys.stdin)['version'])")
INTENT_ID=$(conversations send "publish intent: $PKG@$NEW_VERSION — <one-line changelog>" \
--channel git-publishing --from <agent> -j \
| python3 -c "import sys,json; print(json.load(sys.stdin)['id'])")
echo "Intent posted: message $INTENT_ID"
ACCESS=public
[ "$ORG" = "hasnaxyz" ] && ACCESS=restricted
if command -v bun >/dev/null 2>&1; then
bun publish --access "$ACCESS"
elif command -v npm >/dev/null 2>&1; then
npm publish --access "$ACCESS"
else
echo "BLOCKED: neither bun nor npm publish is available"
exit 1
fi
bun install -g $PKG
Remove any npm global install if it exists:
if command -v npm >/dev/null 2>&1 && npm list -g "$PKG" 2>/dev/null | grep -q "$PKG"; then
npm uninstall -g "$PKG" && echo "Removed npm global install"
fi
Get the CLI binary name from package.json#bin, then test it:
BIN=$(cat package.json | python3 -c "import sys,json; bins=json.load(sys.stdin).get('bin',{}); print(next(iter(bins), '')) if isinstance(bins, dict) else print('')" 2>/dev/null)
if [ -z "$BIN" ]; then
echo "BLOCKED: package.json has no CLI bin to verify"
exit 1
fi
"$BIN" --version || "$BIN" --help 2>&1 | head -3
echo "$PKG published and verified via $BIN"
Reply to the intent message — success or failure, always close the thread:
conversations reply "published: $PKG@$NEW_VERSION — verified via $BIN" --to $INTENT_ID --from <agent>
If the publish failed or was aborted after the intent post:
conversations reply "publish aborted: $PKG@$NEW_VERSION — <reason>" --to $INTENT_ID --from <agent>
Repo releases (GitHub tags/releases) are announced on git-releases, not
git-publishing.
npm install -g — always bun install -gmain; use a branch, PR, CI, review, and mergegit-publishing first (step 7)hasna org: Apache 2.0 + public + README required — no exceptionshasnaxyz org: restricted access requiredtesting
Merge a GitHub pull request, merge when green, use a merge queue, or decide whether a pull request is mergeable. Use only for explicit merge intent, not ordinary review.
development
Generate premium performance audit reports for web apps, APIs, or SaaS surfaces with metrics, findings, budgets, remediation plans, and manifest metadata.
data-ai
Generate premium customer feedback reports from reviews, support tickets, surveys, call notes, or raw feedback with clusters, sentiment, root causes, roadmap recommendations, evidence, and manifest metadata.
development
Generate high-quality PDF documents from markdown, HTML, or templates