Skip to content
Back to skills

Cf Ship Custom

ASecurity

git remote get-url origin | sed 's|git@github.com:|https://github.com/|' | sed 's|\.git$||' git log <TAG>..HEAD --oneline --no-merges -- <paths> git show <hash> --stat ``` **Cross-cutting commit attribution:** A commit that touches paths in multiple packages will appear in both `git log` results. Attribute it to the **primary** package only (see Step B2). Do NOT write duplicate entries across changelogs for the same commit. **Only edit `plugin/CHANGELOG.md` and `cli/CHANGELOG.md`.** `plugin-c...

  • 4 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 19, 2026
ai-agentsgobashcode-reviewgitci/cd

Works with

  • cli

Security analysis

A100/100

Pro scans all 3 files and shows the line behind each finding

Scanned October 5, 2026

npx -y skills add dinhanhthi/coding-friend --skill cf-ship-custom --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Cf Ship Custom?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Cf Ship Custom
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/dinhanhthi-cf-ship-custom/badge)](https://www.skillsdirectory.com/skills/dinhanhthi-cf-ship-custom)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
## Before

This is a **version bump + ship + release** operation. Run these steps BEFORE the standard cf-ship workflow.

**Args** (optional): `[patch|minor|major] [plugin|cli]`

### Step B1: Get bump context

**Run this ALWAYS — even when the working tree is clean.** A clean working tree means changes are already committed and pushed; it does NOT mean there is nothing to release. Tags may not yet exist for the current file versions.

```bash
bash .coding-friend/skills/cf-ship-custom/scripts/bump-info.sh [package-arg] [level-arg]
```

Read the output. It tells you: which packages changed, current vs tag version, whether to bump or just update changelog, and path→package mapping.

### Step B2: Determine bump level and filter packages

If level not in args, analyze the changes and determine patch/minor/major automatically. Do NOT ask for confirmation — proceed immediately.

**Bump level taxonomy — match changelog labels:**

- **PATCH** (x.x.Z): improvements or refinements to existing behavior (expanding allow-lists, tweaking output, polishing UX, bug fixes, typos, docs) — changelog label "Improved" or "Fixed". Example: expanding `auto-approve` allow-list → PATCH, not MINOR.
- **MINOR** (x.Y.0): new capability a user can invoke or opt into — new skill, new CLI command, new hook, new feature flag — changelog label "Added" or "New".
- **MAJOR** (X.0.0): breaking change (config format change, removed skill, changed CLI behavior).

**Default to PATCH. Bias strongly toward PATCH over MINOR.**

- When in doubt between PATCH and MINOR, choose PATCH.
- A single new capability alongside improvements → still PATCH unless the new capability is substantial and user-facing on its own.
- MINOR is reserved for releases where new capabilities are the dominant story — multiple new skills, commands, or features added, not just one incidental addition alongside fixes.
- Small additions that extend existing behavior (new option to an existing command, new entry in an existing list, new config key for an existing feature) → PATCH, not MINOR.

**Rule of thumb:** if all commits in a version only improve/refine/fix existing things (no new user-facing capability), the bump is PATCH. If only one minor new thing was added among many improvements, still prefer PATCH.

**Cross-cutting commits — primary package attribution:**
When a commit touches paths in multiple packages, determine which package owns the change by looking at where the **essence** of the change lives — not just which paths were touched:

- If a commit is primarily a CLI feature/fix (most changed files are in `cli/`, the feature is a CLI command, the tests are CLI tests) and only incidentally updates a plugin file (e.g., a hook reads new config) → it is a **CLI-only change**. Do NOT bump or changelog the plugin for it.
- If a commit independently changes both packages (e.g., adds a new plugin skill AND a new CLI command) → attribute to both, with separate package-appropriate descriptions.
- **Key signal:** if the plugin changelog entry would describe CLI commands/features (e.g., `cf statusline`, `cf config`) rather than plugin-specific behavior, it's a CLI change — not a plugin change.

After filtering, if a package flagged by bump-info.sh has NO commits that are primarily its own, skip bumping it entirely.

### Step B3: Bump version files

For each package that needs bumping (state = "bump", not "already-bumped"):

```bash
bash .coding-friend/skills/cf-ship-custom/scripts/bump.sh <package> <new_version>
```

### Step B4: Update changelogs

For each affected package, collect commits since its last tag and write entries under `## v{version} (TODAY'S DATE)` — e.g. `## v0.15.0 (2026-03-26)`. Use `date +%Y-%m-%d` to get today's date. Use backticks for inline code references (skill names like `cf-plan`, agent names like `cf-code-reviewer`, commands like `/cf-commit`, file names, config keys, CLI commands, technical identifiers). Append commit links `[#hash](repo/commit/hash)`. Deduplicate against existing entries.

**CRITICAL — Net changes only:** Changelog entries must reflect the **net difference vs the previous released version**, NOT individual commits. Before writing entries, consolidate all commits for the version and determine what actually changed end-to-end:

- If commit A adds feature X (with part Y) and commit B removes part Y → write ONE entry: "Add X (without Y)". Do NOT write a separate "Remove Y" entry — Y never existed in the previous version.
- If commit A adds something and commit B reverts it entirely → write NOTHING for that change.
- If commit A adds something and commit B improves/modifies it → write ONE entry describing the final state.
- Think of it as: **compare the codebase at the last tag vs HEAD** — only describe differences a user upgrading from the previous version would notice. Internal iteration within the version is invisible to users.

```bash
# Get repo URL
git remote get-url origin | sed 's|git@github.com:|https://github.com/|' | sed 's|\.git$||'

# Commits per package (use tag from bump-info output)
git log <TAG>..HEAD --oneline --no-merges -- <paths>

# For cross-cutting commits, check the full stat to determine primary package
git show <hash> --stat
```

**Cross-cutting commit attribution:** A commit that touches paths in multiple packages will appear in both `git log` results. Attribute it to the **primary** package only (see Step B2). Do NOT write duplicate entries across changelogs for the same commit.

**Only edit `plugin/CHANGELOG.md` and `cli/CHANGELOG.md`.** `plugin-codex/CHANGELOG.md` and `plugin-antigravity/CHANGELOG.md` are **generated mirrors** of `plugin/CHANGELOG.md` — the `.githooks/pre-commit` hook reruns `npm run build:codex` + `npm run build:agy` + `npm run build:devin` and stages the result whenever `plugin/` or `package.json` is staged. Never hand-edit them (the codex mirror rewrites `/cf-*` to `$cf-*`). Same for the generated manifests `plugin-codex/.codex-plugin/plugin.json`, `plugin-antigravity/plugin.json` and `plugin-devin/.claude-plugin/plugin.json`: they inherit their version from root `package.json` at build time, so `bump.sh plugin` is all you need.

These package changelogs (`plugin/CHANGELOG.md`, `cli/CHANGELOG.md`) are the source for GitHub Releases. The public changelog is the [GitHub Releases](https://github.com/dinhanhthi/coding-friend/releases) page (`/changelog` on the website redirects there). Do **not** update `website/src/content/index.md` or any other website markdown as part of ship.

### Step B5: Ship (commit + push)

Proceed with the **standard cf-ship workflow** (verify → commit → push). Use `bump <packages> to <versions>` as the commit hint.

**Verification — run ALL of these, this repo has two separate test suites and root-only checks miss the CLI one:**

```bash
npm run test:scripts        # root: generator/catalog tests (~29)
(cd cli && npm test)        # CLI + hooks: what tests.yml actually runs (~67 + ~15 files)
npm run verify:codex-drift  # plugin-codex artifact in sync
npm run verify:agy-drift    # plugin-antigravity artifact in sync
npm run verify:devin-drift  # plugin-devin artifact in sync
npm run lint:codex
npm run lint:agy
npm run lint:devin
```

`npm run test:scripts` alone is NOT enough — `.github/workflows/tests.yml` has a separate `cli` job running `npm test` inside `cli/`, and a release shipped on a red CLI suite before because only the root suite was run. Never report "verification passed" without the `cli` line above.

**IMPORTANT:** If already on the `main` branch, do NOT create a new branch. Commit and push directly to `main` — no PR needed.

**On a feature branch:** commit, push, and open the PR (standard cf-ship Steps 3–4), then **STOP — skip Step B6 (no tags)**. The website, `plugin/CHANGELOG.md` on `main` and the marketplace all read from `main`; a tag pushed from an unmerged branch publishes a GitHub Release whose version and changelog are missing on `main`. Print the PR URL and tell the user: merge the PR, then run `/cf-ship` again from an up-to-date `main`. That rerun sees `already-bumped` in Step B1, finds the changelog entry already present in Step B4 (dedupe), has nothing to commit, and goes straight to Step B6. This works for merge, rebase and squash merges alike, because the tag is only ever created on `main`'s own commit.

### Step B6: Create tags and push

After the commit is pushed, create git tags and push them to trigger CI/CD.

**Guard — tag only a commit on `origin/main`.** Run first and stop (report, do not tag) if any check fails:

```bash
git branch --show-current                   # must be: main
git fetch origin main --quiet
test "$(git rev-parse HEAD)" = "$(git rev-parse origin/main)" && echo OK   # HEAD pushed and equal to origin/main
```

On a feature branch, or with local commits not yet on `origin/main`, do not tag — see "On a feature branch" in Step B5.

**You only push these by hand** (never invent extra host tags):

| What you push           | Tag                                 | What it triggers                                                                             |
| ----------------------- | ----------------------------------- | -------------------------------------------------------------------------------------------- |
| Plugin                  | `v{version}` e.g. `v0.15.0`         | One GitHub Release covering Claude + Codex + AGY + Devin (generated trees in the same tagged commit) |
| CLI (if CLI was bumped) | `cli-v{version}` e.g. `cli-v1.24.0` | npm publish + GitHub Release                                                                 |

How it works: `.github/workflows/release.yml` (trigger `v*`) verifies locked versions (`package.json`, `plugin/.claude-plugin/plugin.json`, `plugin-codex/.codex-plugin/plugin.json`, `plugin-antigravity/plugin.json`, `plugin-devin/.claude-plugin/plugin.json`) plus Codex/AGY/Devin drift and lint, then publishes **one** GitHub Release for that `v*` tag. Codex (`plugin-codex/`), Antigravity (`plugin-antigravity/`) and Devin (`plugin-devin/`) ship as generated artifacts in the tagged commit — not as extra tags.

**The repo may have more than one remote** (e.g. a contributor fork). Always name `origin` explicitly in tag pushes and confirm it points at the canonical repo first: `git remote get-url origin`.

```bash
# Create tags (one per released package)
git tag <tag>

# Push each tag individually — plugin tag MUST be pushed last.
# Order: 1. cli-v* (first)  2. v* (plugin — always last)
git push origin <tag>
```

**Verify the tag actually landed** — a push that prints success is not proof the release started:

```bash
git ls-remote --tags origin | grep -F "<tag>"        # hand-pushed tag present on origin?
gh run list --workflow=release.yml --limit 3         # release workflow triggered?
```

If the hand-pushed tag is missing or no `release.yml` run appeared, report it — do NOT silently re-push or assume success.

**IMPORTANT**: Do NOT use `git push origin main --tags`. Pushing multiple tags at once may fail to trigger GitHub Actions workflows. Push each tag individually. When both CLI and plugin tags exist, **always push the plugin tag (`v*`) last** — the plugin GitHub Release workflow updates the marketplace cache, which must include the latest CLI version.

### Step B7: Print summary

```
Released:
  Plugin v0.15.0   → tag v0.15.0 pushed (you) → one GitHub Release (Claude + Codex + AGY + Devin)
  Codex            → ships on the same v0.15.0 tag (`plugin-codex/` in the tagged commit)
  Antigravity      → ships on the same v0.15.0 tag (`plugin-antigravity/` in the tagged commit)
  Devin            → ships on the same v0.15.0 tag (`plugin-devin/` in the tagged commit)
  CLI v1.24.0      → tag cli-v1.24.0 pushed (you) → npm publish + GitHub Release

Check CI/CD status:
  https://github.com/dinhanhthi/coding-friend/actions
```

Always list Codex, AGY and Devin in this summary when a plugin `v*` was pushed — they ship on that same tag, not as extra tags. If `release.yml` has not finished, point at the run; do not treat that as a missed step.

**NO CONFIRMATIONS:** Do NOT ask for confirmation at any step — not for bump level, not for pushing, not for creating PRs, not for tagging. Analyze, decide, and execute autonomously.

## Rules

- Published tags on `origin` = single source of truth. Run `git fetch --tags` first (bump-info.sh does this).
- NEVER bump if file version > tag version — only update changelog.
- ALWAYS keep `plugin/.claude-plugin/plugin.json` + root `package.json` in sync.
- NEVER add duplicate changelog entries. One feature = one bullet. Entries must reflect net changes vs the previous released version — do NOT list intermediate additions/removals that cancel each other out within the same version.
- Changelog sections use today's date directly — NEVER use `(unpublished)`.
- Do NOT update website markdown (`website/src/content/index.md` or any `website/` content). The site is a single page; `/changelog` redirects to GitHub Releases.
- If a tag already exists, do NOT force-create tags — error and stop.
- NEVER create or push a `v*` / `cli-v*` tag on a commit other than `origin/main`'s HEAD. From a feature branch, stop after the PR; tags come from a `/cf-ship` rerun on `main` after merge.
- NEVER hand-edit generated artifacts: `plugin-codex/**`, `plugin-antigravity/**` and `plugin-devin/**` are rebuilt from `plugin/` + root `package.json` by `.githooks/pre-commit`. Edit the source, not the mirror.
- Only `v*` and `cli-v*` are pushed by hand. Do not invent extra host tags.
- Push tags without asking for confirmation — the `## After` NO CONFIRMATIONS rule applies here too.

## After

**NO CONFIRMATIONS:** Do NOT ask for confirmation at any step — not for bump level, not for pushing, not for creating PRs, not for tagging. Analyze, decide, and execute autonomously.

Files in this skill

  • SKILL.md12 KB
  • scripts/bump-info.sh3.1 KB
  • scripts/bump.sh1.5 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.