Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions .claude/skills/release/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,12 +78,14 @@ git push && git push --tags
./scripts/tag-plugins.sh X.Y.Z

# 10. Push each extension to its standalone GitHub repo
# (tractorjuice/arckit-gemini, arckit-codex, …):
# (tractorjuice/arckit-gemini, arckit-codex, …). This also creates or
# preserves each extension repo's vX.Y.Z tag and GitHub Release:
./scripts/push-extensions.sh
```

After step 10, confirm the GitHub Release was created (the `release.yml` workflow runs on the
`vX.Y.Z` tag push) and report the release URL and which extension repos were pushed.
`vX.Y.Z` tag push). Also confirm every standalone extension repo has a `vX.Y.Z` tag and GitHub
Release, then report the release URLs and which extension repos were pushed.

## Common Gotchas

Expand Down Expand Up @@ -113,7 +115,12 @@ The highest-signal failures — collected from real releases. Read these before
the same number by design; don't try to skew them.
- **`push-extensions.sh` needs `GH_TOKEN`** and skips repos that don't yet exist on GitHub — a
"skipped" line is not an error for a brand-new extension, but double-check it's not skipping a
repo that *should* exist.
repo that *should* exist. It now creates/preserves standalone extension `vX.Y.Z` tags and
GitHub Releases; use `ARCKIT_SKIP_EXTENSION_RELEASES=1` only when intentionally doing a
commit-only sync.
- **Do not put release numbers in extension READMEs.** Extension release identity lives in
`VERSION` files, manifests, Git tags, and GitHub Releases. README-pinned versions drift and
are blocked by `tests/plugin/test_release_process.py`.
- **Order is load-bearing.** bump → convert → commit → validate → tag → tag-plugins → push-extensions.
Re-running an earlier step after a later one (e.g. editing files after the commit) means the tag
no longer points at the released tree. If you edit after committing, redo from the commit.
Expand Down
19 changes: 17 additions & 2 deletions docs/RELEASING.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ ArcKit ships in seven formats, each with its own version file. They are all bump
|--------|---------|
| `scripts/bump-version.sh <version>` | Updates all version files in one pass |
| `scripts/generate-release-notes.sh [prev-tag]` | Parses `git log` between tags into Keep a Changelog markdown (Added / Fixed / Changed / Breaking Changes), filters out `chore: bump version` commits, auto-detects previous tag if omitted |
| `scripts/push-extensions.sh [name...]` | Pushes extension dirs to their separate GitHub repos (`tractorjuice/arckit-gemini`, `tractorjuice/arckit-codex`, etc.). Uses `GH_TOKEN`. Skips repos that don't yet exist on GitHub |
| `scripts/push-extensions.sh [name...]` | Pushes extension dirs to their separate GitHub repos (`tractorjuice/arckit-gemini`, `tractorjuice/arckit-codex`, etc.), then creates or preserves each repo's `vX.Y.Z` tag and GitHub Release. Uses `GH_TOKEN`. Skips repos that don't yet exist on GitHub. Set `ARCKIT_SKIP_EXTENSION_RELEASES=1` only for a commit-only sync |
| `.github/workflows/release.yml` | Creates the GitHub Release automatically on `v*` tag push (tag-push triggered, does not commit back to main) |

## Development Workflow
Expand Down Expand Up @@ -87,10 +87,21 @@ claude plugin prune --dry-run
git tag -a vX.Y.Z -m "vX.Y.Z"
git push && git push --tags

# 10. Push to extension repos (Gemini, Codex, etc.)
# 10. Push to extension repos (Gemini, Codex, etc.).
# This also publishes each extension repo's vX.Y.Z tag and GitHub Release.
./scripts/push-extensions.sh
```

After step 10, verify the umbrella GitHub Release and every extension GitHub Release exists:

- `tractorjuice/arc-kit`
- `tractorjuice/arckit-gemini`
- `tractorjuice/arckit-codex`
- `tractorjuice/arckit-opencode`
- `tractorjuice/arckit-copilot`
- `tractorjuice/arckit-paperclip`
- `tractorjuice/arckit-vibe`

### Note on `claude plugin tag`

This command creates `{plugin-name}--vX.Y.Z` style tags (e.g. `arckit--v4.14.0`), which would not trigger `.github/workflows/release.yml` (it matches `v[0-9]+.[0-9]+.[0-9]+`). We use `--dry-run` for its validation behaviour only — it cross-checks `plugins/arckit-claude/.claude-plugin/plugin.json` against the marketplace entry in `.claude-plugin/marketplace.json` and exits non-zero on mismatch, catching version drift before the real `git tag -a vX.Y.Z` runs.
Expand All @@ -115,6 +126,10 @@ After the umbrella tag (step 9), also create native per-plugin tags:

This creates `arckit--vX.Y.Z`, `arckit-uae--vX.Y.Z`, ..., `arckit-at--vX.Y.Z` for the Claude Code plugin system's bookkeeping. Idempotent — re-running skips tags that already exist.

Extension README files do not carry release numbers. Keep release identity in `VERSION` files,
manifests, tags, and GitHub Releases so README content cannot drift from marketplace-visible
artifacts.

## Adding New Package Data Files

Update `pyproject.toml` `[tool.hatch.build.targets.wheel.shared-data]` so the file ships with the CLI wheel.
6 changes: 0 additions & 6 deletions extensions/arckit-codex/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -322,10 +322,4 @@ arckit-codex/
└── guides/ # Command usage guides
```

## Version

**Current Release: v5.14.0**

---

**ArcKit Codex CLI Extension** -- Generated by `scripts/converter.py`
9 changes: 0 additions & 9 deletions extensions/arckit-vibe/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
The Enterprise Architecture Governance Harness for Mistral Vibe CLI.

> **Status**: Beta (Community Preview) 🟡
> **Version**: 5.14.0
> **ArcKit Version**: 5.14.0
>
> **Note**: This extension is currently in beta. The extension is published from the standalone `tractorjuice/arckit-vibe` repository and regenerated from ArcKit's canonical plugin sources.

Expand Down Expand Up @@ -401,13 +399,6 @@ MIT License - see [LICENSE](LICENSE) file for details.
- **Discussion**: [GitHub Discussions](https://github.com/tractorjuice/arc-kit/discussions)
- **ArcKit Repository**: [tractorjuice/arc-kit](https://github.com/tractorjuice/arc-kit)

## Version History

| Version | Date | Changes |
|---------|------|---------|
| 5.14.0 | 2026-06-17 | Published as standalone generated extension repository |
| 5.13.1 | 2026-06-16 | Initial Mistral Vibe extension release |

## Acknowledgments

- Built on [Mistral Vibe](https://github.com/mistralai/mistral-vibe)
Expand Down
158 changes: 136 additions & 22 deletions scripts/push-extensions.sh
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
#!/usr/bin/env bash
set -uo pipefail

# push-extensions.sh — Push extension directories to their separate GitHub repos.
# push-extensions.sh — Publish extension directories to their separate GitHub repos.
# Usage: ./scripts/push-extensions.sh [extension...]
#
# Examples:
# ./scripts/push-extensions.sh # Push all extensions
# ./scripts/push-extensions.sh gemini codex # Push only gemini and codex
#
# Requires: GH_TOKEN with repo scope, or gh CLI authenticated with push access.
# By default this also creates/preserves a vX.Y.Z tag and GitHub Release in
# each extension repo. Set ARCKIT_SKIP_EXTENSION_RELEASES=1 for a commit-only
# sync.

REPO_OWNER="tractorjuice"

Expand Down Expand Up @@ -40,6 +43,8 @@ fi
# ── Read version from root VERSION file ───────────────────────────────────────
VERSION=$(cat "$ROOT_DIR/VERSION")
COMMIT_MSG="chore: sync with arc-kit v${VERSION}"
TAG="v${VERSION}"
SKIP_RELEASES="${ARCKIT_SKIP_EXTENSION_RELEASES:-0}"

# ── Helpers ───────────────────────────────────────────────────────────────────
green() { printf '\033[0;32m%s\033[0m\n' "$1"; }
Expand All @@ -55,8 +60,109 @@ check_repo_exists() {
fi
}

remote_tag_commit() {
local tag="$1"
local commit

# Annotated tags expose the target commit through ^{}; lightweight tags do not.
commit=$(git ls-remote --tags origin "refs/tags/${tag}^{}" | awk '{print $1}' | head -1)
if [[ -z "$commit" ]]; then
commit=$(git ls-remote --tags origin "refs/tags/${tag}" | awk '{print $1}' | head -1)
fi
printf '%s' "$commit"
}

ensure_repo_topic() {
local repo_name="$1"
local wanted_topic="$2"
local current_json
local next_json

current_json=$(gh api "repos/${REPO_OWNER}/${repo_name}/topics" \
-H 'Accept: application/vnd.github+json' 2>/dev/null) || {
yellow " Could not read topics for ${REPO_OWNER}/${repo_name} — skipping topic check"
return 0
}

if jq -e --arg topic "$wanted_topic" '(.names // []) | index($topic)' \
<<<"$current_json" >/dev/null; then
return 0
fi

next_json=$(jq --arg topic "$wanted_topic" \
'.names = (((.names // []) + [$topic]) | unique)' \
<<<"$current_json")

if gh api "repos/${REPO_OWNER}/${repo_name}/topics" \
-X PUT \
-H 'Accept: application/vnd.github+json' \
--input - <<<"$next_json" >/dev/null; then
green " ✓ Added GitHub topic: ${wanted_topic}"
else
yellow " Could not update topics for ${REPO_OWNER}/${repo_name}"
fi
}

publish_release_artifacts() {
local repo_name="$1"
local head_sha
local existing_tag_commit
local release_notes

if [[ "$SKIP_RELEASES" == "1" ]]; then
yellow " Extension release publishing disabled by ARCKIT_SKIP_EXTENSION_RELEASES=1"
return 0
fi

head_sha=$(git rev-parse HEAD)
existing_tag_commit=$(remote_tag_commit "$TAG")

if [[ -n "$existing_tag_commit" ]]; then
if [[ "$existing_tag_commit" != "$head_sha" ]]; then
red " Tag ${TAG} already exists but points at ${existing_tag_commit:0:8}, not ${head_sha:0:8}"
return 1
fi
yellow " Tag ${TAG} already exists at HEAD"
else
echo " Creating tag ${TAG}..."
if ! git tag -a "$TAG" -m "${repo_name} ${TAG}"; then
red " Failed to create tag ${TAG} for ${REPO_OWNER}/${repo_name}"
return 1
fi
if ! git push --quiet origin "refs/tags/${TAG}"; then
red " Failed to push tag ${TAG} for ${REPO_OWNER}/${repo_name}"
return 1
fi
green " ✓ Pushed tag ${TAG}"
fi

if gh release view "$TAG" --repo "${REPO_OWNER}/${repo_name}" &>/dev/null; then
yellow " GitHub Release ${TAG} already exists"
return 0
fi

release_notes=$(cat <<EOF
Synced from tractorjuice/arc-kit ${TAG}.

Main ArcKit release: https://github.com/tractorjuice/arc-kit/releases/tag/${TAG}
Source commit: ${head_sha}
EOF
)

echo " Creating GitHub Release ${TAG}..."
if gh release create "$TAG" \
--repo "${REPO_OWNER}/${repo_name}" \
--title "${repo_name} ${TAG}" \
--notes "$release_notes" >/dev/null; then
green " ✓ Created GitHub Release ${TAG}"
else
red " Failed to create GitHub Release ${TAG} for ${REPO_OWNER}/${repo_name}"
return 1
fi
}

# ── Main loop ─────────────────────────────────────────────────────────────────
PUSHED=0
PROCESSED=0
SKIPPED=0
FAILED=0

Expand Down Expand Up @@ -116,38 +222,46 @@ for target in "${TARGETS[@]}"; do
git add -A
if git diff --cached --quiet; then
yellow " No changes — already up to date"
((SKIPPED++))
cd "$ROOT_DIR"
continue
fi
else
# Show summary of changes
CHANGED=$(git diff --cached --stat | tail -1)
echo " Changes: $CHANGED"

# Show summary of changes
CHANGED=$(git diff --cached --stat | tail -1)
echo " Changes: $CHANGED"
# Commit and push
if ! git commit -m "$COMMIT_MSG" --quiet; then
red " Failed to commit changes for ${REPO_OWNER}/${repo_name}"
((FAILED++))
cd "$ROOT_DIR"
continue
fi
if ! git push --quiet; then
red " Failed to push ${REPO_OWNER}/${repo_name}"
((FAILED++))
cd "$ROOT_DIR"
continue
fi
green " ✓ Pushed to ${REPO_OWNER}/${repo_name}"
fi

# Commit and push
if ! git commit -m "$COMMIT_MSG" --quiet; then
red " Failed to commit changes for ${REPO_OWNER}/${repo_name}"
((FAILED++))
cd "$ROOT_DIR"
continue
if [[ "$target" == "gemini" ]]; then
ensure_repo_topic "$repo_name" "gemini-cli-extension"
fi
if ! git push --quiet; then
red " Failed to push ${REPO_OWNER}/${repo_name}"

if ! publish_release_artifacts "$repo_name"; then
((FAILED++))
cd "$ROOT_DIR"
continue
fi
green " ✓ Pushed to ${REPO_OWNER}/${repo_name}"
((PUSHED++))

((PROCESSED++))
cd "$ROOT_DIR"
done

# ── Summary ───────────────────────────────────────────────────────────────────
echo ""
echo "── Summary ──"
echo " Pushed: $PUSHED"
echo " Skipped: $SKIPPED"
echo " Failed: $FAILED"
echo " Processed: $PROCESSED"
echo " Skipped: $SKIPPED"
echo " Failed: $FAILED"

[[ $FAILED -eq 0 ]] || exit 1
Loading
Loading