CS-10900: Add Claude Code plugin for boxel-cli#4632
Conversation
Ships a first-class Claude Code plugin for @cardstack/boxel-cli, taking inspiration from the standalone repo's .claude/CLAUDE.md and packaging it per https://code.claude.com/docs/en/plugins. Plugin lives at packages/boxel-cli/plugin/ with a repo-root .claude-plugin/marketplace.json so external users can `/plugin marketplace add cardstack/boxel` and Cardstack engineers can `claude --plugin-dir packages/boxel-cli/plugin`. Seven skills under the /boxel-cli: namespace: - boxel-development, boxel-file-structure (ported from standalone) - realm-sync, realm-history, file-ops, search, profile (new wrappers) Hybrid generation: scripts/build-plugin.ts walks the Commander tree (extracted into src/build-program.ts so both the runtime entry point and the generator share one source of truth) and rewrites the <!-- generated:commands --> blocks in each SKILL.md. Curated narrative outside those markers is hand-authored. CI gates added in ci-lint.yaml: 1. Synopsis freshness — `pnpm build:plugin && git diff --exit-code`. 2. Synopsis-bump coupling — if any generated block changed, plugin.json version must also change in the same PR (otherwise marketplace consumers won't see the update; Claude caches by version string). Plugin and CLI version independently — prose-only updates bump only plugin.json; CLI bug fixes bump only package.json. Plugin distribution stays git-based via the marketplace, no separate npm publish. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Move the release model from the (now-removed) ticket plan doc into packages/boxel-cli/plugin/README.md so future maintainers can ship a new plugin version without digging through Linear: bump-decision table, the 4-step release flow, how users pick up updates, and how to add another plugin to the marketplace catalog. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e935183029
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| SYNOPSIS_CHANGED=$(git diff "$BASE" "$HEAD" -- 'packages/boxel-cli/plugin/skills/**/SKILL.md' \ | ||
| | grep -E '^[+-](### `boxel |\*\*Arguments|\*\*Options|- `[-A-Za-z]+|- `<|- `\[)' || true) |
There was a problem hiding this comment.
Fail when synopsis diff cannot be computed
The synopsis/version coupling check masks git diff errors by appending || true inside the SYNOPSIS_CHANGED assignment, so if either SHA is unavailable (for example in shallow checkouts) the step silently treats it as “no synopsis changes” and exits successfully. This makes the guard non-deterministic and can allow generated skill updates to merge without the required plugin version bump.
Useful? React with 👍 / 👎.
| HEAD="${{ github.event.pull_request.head.sha }}" | ||
| # Did any synopsis line change? | ||
| SYNOPSIS_CHANGED=$(git diff "$BASE" "$HEAD" -- 'packages/boxel-cli/plugin/skills/**/SKILL.md' \ | ||
| | grep -E '^[+-](### `boxel |\*\*Arguments|\*\*Options|- `[-A-Za-z]+|- `<|- `\[)' || true) |
There was a problem hiding this comment.
Detect all generated synopsis edits before skipping bump check
The grep pattern only matches headings/argument/option bullet formats, so generated-block edits that only change descriptive text (for example updated command descriptions from Commander metadata) are ignored and the workflow skips the version-bump requirement. Since those description lines are part of the generated synopsis, this creates a false negative and can publish stale plugin content under an unchanged version.
Useful? React with 👍 / 👎.
Summary
Ships a first-class Claude Code plugin for
@cardstack/boxel-cli, packaging the kind of guidance the standalonecardstack/boxel-clirepo carries in its.claude/CLAUDE.mdso it's distributable via marketplace.packages/boxel-cli/plugin/with manifest.claude-plugin/plugin.json..claude-plugin/marketplace.jsonlists the plugin via relative path; external users/plugin marketplace add cardstack/boxelthen/plugin install boxel-cli.claude --plugin-dir packages/boxel-cli/plugin./boxel-cli:namespace:boxel-development—.gtsauthoring (ported from standalone, ~90KB of evergreen Boxel patterns)boxel-file-structure— file/directory naming +adoptsFromrules (ported from standalone)realm-sync,realm-history,file-ops,search,profile— new wrappers around the monorepo CLI's namespaced commands (incl. CS-10627file touch)How "generated from the code" works
scripts/build-plugin.tswalks the Commander tree and rewrites the<!-- generated:commands -->block inside each SKILL.md. The Commander tree is extracted intosrc/build-program.tsso both the runtime entry point (src/index.ts) and the generator share one source of truth. Curated narrative outside the markers is hand-authored.Two CI gates in
ci-lint.yaml:pnpm build:plugin && git diff --exit-code. Fails if commands changed without regenerating skills.plugin.jsonversionmust also be in the diff. Without a version bump, Claude's marketplace cache won't surface the update.Versioning
Plugin and CLI version independently:
plugin.jsononly.package.jsononly.The plugin is not npm-published. The marketplace clones it from
cardstack/boxeldirectly.npm install -g @cardstack/boxel-cliremains the prerequisite for users — Pattern A in plugins-reference (matches howpyright-lspand other npm-backed plugins work).Test plan
cd packages/boxel-cli && pnpm build:plugin→ no diff (idempotent)pnpm lintinpackages/boxel-clipasses (eslint + tsc)pnpm test:unitinpackages/boxel-clipasses (158 tests)pnpm buildinpackages/boxel-cliproduces a workingdist/index.jsclaude --plugin-dir packages/boxel-cli/pluginfrom repo root:/helplists/boxel-cli:*skills/boxel-cli:realm-syncagainst a local realm-server)pnpm build:plugin, confirmgit diffshows the synopsis updateplugin.json🤖 Generated with Claude Code