feat: migrate repo releases to manual release-please (#293)

This commit is contained in:
Trevin Chow
2026-03-17 17:58:13 -07:00
committed by GitHub
parent 74fb71731a
commit f47f829d81
44 changed files with 1967 additions and 801 deletions

View File

@@ -1,211 +0,0 @@
---
name: release-docs
description: Build and update the documentation site with current plugin components
argument-hint: "[optional: --dry-run to preview changes without writing]"
---
# Release Documentation Command
You are a documentation generator for the compound-engineering plugin. Your job is to ensure the documentation site at `plugins/compound-engineering/docs/` is always up-to-date with the actual plugin components.
## Overview
The documentation site is a static HTML/CSS/JS site based on the Evil Martians LaunchKit template. It needs to be regenerated whenever:
- Agents are added, removed, or modified
- Commands are added, removed, or modified
- Skills are added, removed, or modified
- MCP servers are added, removed, or modified
## Step 1: Inventory Current Components
First, count and list all current components:
```bash
# Count agents
ls plugins/compound-engineering/agents/*.md | wc -l
# Count commands
ls plugins/compound-engineering/commands/*.md | wc -l
# Count skills
ls -d plugins/compound-engineering/skills/*/ 2>/dev/null | wc -l
# Count MCP servers
ls -d plugins/compound-engineering/mcp-servers/*/ 2>/dev/null | wc -l
```
Read all component files to get their metadata:
### Agents
For each agent file in `plugins/compound-engineering/agents/*.md`:
- Extract the frontmatter (name, description)
- Note the category (Review, Research, Workflow, Design, Docs)
- Get key responsibilities from the content
### Commands
For each command file in `plugins/compound-engineering/commands/*.md`:
- Extract the frontmatter (name, description, argument-hint)
- Categorize as Workflow or Utility command
### Skills
For each skill directory in `plugins/compound-engineering/skills/*/`:
- Read the SKILL.md file for frontmatter (name, description)
- Note any scripts or supporting files
### MCP Servers
For each MCP server in `plugins/compound-engineering/mcp-servers/*/`:
- Read the configuration and README
- List the tools provided
## Step 2: Update Documentation Pages
### 2a. Update `docs/index.html`
Update the stats section with accurate counts:
```html
<div class="stats-grid">
<div class="stat-card">
<span class="stat-number">[AGENT_COUNT]</span>
<span class="stat-label">Specialized Agents</span>
</div>
<!-- Update all stat cards -->
</div>
```
Ensure the component summary sections list key components accurately.
### 2b. Update `docs/pages/agents.html`
Regenerate the complete agents reference page:
- Group agents by category (Review, Research, Workflow, Design, Docs)
- Include for each agent:
- Name and description
- Key responsibilities (bullet list)
- Usage example: `claude agent [agent-name] "your message"`
- Use cases
### 2c. Update `docs/pages/commands.html`
Regenerate the complete commands reference page:
- Group commands by type (Workflow, Utility)
- Include for each command:
- Name and description
- Arguments (if any)
- Process/workflow steps
- Example usage
### 2d. Update `docs/pages/skills.html`
Regenerate the complete skills reference page:
- Group skills by category (Development Tools, Content & Workflow, Image Generation)
- Include for each skill:
- Name and description
- Usage: `claude skill [skill-name]`
- Features and capabilities
### 2e. Update `docs/pages/mcp-servers.html`
Regenerate the MCP servers reference page:
- For each server:
- Name and purpose
- Tools provided
- Configuration details
- Supported frameworks/services
## Step 3: Update Metadata Files
Ensure counts are consistent across:
1. **`plugins/compound-engineering/.claude-plugin/plugin.json`**
- Update `description` with correct counts
- Update `components` object with counts
- Update `agents`, `commands` arrays with current items
2. **`.claude-plugin/marketplace.json`**
- Update plugin `description` with correct counts
3. **`plugins/compound-engineering/README.md`**
- Update intro paragraph with counts
- Update component lists
## Step 4: Validate
Run validation checks:
```bash
# Validate JSON files
cat .claude-plugin/marketplace.json | jq .
cat plugins/compound-engineering/.claude-plugin/plugin.json | jq .
# Verify counts match
echo "Agents in files: $(ls plugins/compound-engineering/agents/*.md | wc -l)"
grep -o "[0-9]* specialized agents" plugins/compound-engineering/docs/index.html
echo "Commands in files: $(ls plugins/compound-engineering/commands/*.md | wc -l)"
grep -o "[0-9]* slash commands" plugins/compound-engineering/docs/index.html
```
## Step 5: Report Changes
Provide a summary of what was updated:
```
## Documentation Release Summary
### Component Counts
- Agents: X (previously Y)
- Commands: X (previously Y)
- Skills: X (previously Y)
- MCP Servers: X (previously Y)
### Files Updated
- docs/index.html - Updated stats and component summaries
- docs/pages/agents.html - Regenerated with X agents
- docs/pages/commands.html - Regenerated with X commands
- docs/pages/skills.html - Regenerated with X skills
- docs/pages/mcp-servers.html - Regenerated with X servers
- plugin.json - Updated counts and component lists
- marketplace.json - Updated description
- README.md - Updated component lists
### New Components Added
- [List any new agents/commands/skills]
### Components Removed
- [List any removed agents/commands/skills]
```
## Dry Run Mode
If `--dry-run` is specified:
- Perform all inventory and validation steps
- Report what WOULD be updated
- Do NOT write any files
- Show diff previews of proposed changes
## Error Handling
- If component files have invalid frontmatter, report the error and skip
- If JSON validation fails, report and abort
- Always maintain a valid state - don't partially update
## Post-Release
After successful release:
1. Suggest updating CHANGELOG.md with documentation changes
2. Remind to commit with message: `docs: Update documentation site to match plugin components`
3. Remind to push changes
## Usage Examples
```bash
# Full documentation release
claude /release-docs
# Preview changes without writing
claude /release-docs --dry-run
# After adding new agents
claude /release-docs
```

6
.github/.release-please-manifest.json vendored Normal file
View File

@@ -0,0 +1,6 @@
{
".": "2.42.0",
"plugins/compound-engineering": "2.42.0",
"plugins/coding-tutor": "1.2.1",
".claude-plugin": "1.0.0"
}

64
.github/release-please-config.json vendored Normal file
View File

@@ -0,0 +1,64 @@
{
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
"include-component-in-tag": true,
"packages": {
".": {
"release-type": "simple",
"package-name": "cli",
"changelog-path": "CHANGELOG.md",
"extra-files": [
{
"type": "json",
"path": "package.json",
"jsonpath": "$.version"
}
]
},
"plugins/compound-engineering": {
"release-type": "simple",
"package-name": "compound-engineering",
"changelog-path": "../../CHANGELOG.md",
"extra-files": [
{
"type": "json",
"path": ".claude-plugin/plugin.json",
"jsonpath": "$.version"
},
{
"type": "json",
"path": ".cursor-plugin/plugin.json",
"jsonpath": "$.version"
}
]
},
"plugins/coding-tutor": {
"release-type": "simple",
"package-name": "coding-tutor",
"changelog-path": "../../CHANGELOG.md",
"extra-files": [
{
"type": "json",
"path": ".claude-plugin/plugin.json",
"jsonpath": "$.version"
},
{
"type": "json",
"path": ".cursor-plugin/plugin.json",
"jsonpath": "$.version"
}
]
},
".claude-plugin": {
"release-type": "simple",
"package-name": "marketplace",
"changelog-path": "../CHANGELOG.md",
"extra-files": [
{
"type": "json",
"path": "marketplace.json",
"jsonpath": "$.metadata.version"
}
]
}
}
}

View File

@@ -7,6 +7,31 @@ on:
workflow_dispatch:
jobs:
pr-title:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
permissions:
pull-requests: read
steps:
- name: Validate PR title
uses: amannn/action-semantic-pull-request@v6.1.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
requireScope: false
types: |
feat
fix
docs
refactor
chore
test
ci
build
perf
revert
test:
runs-on: ubuntu-latest
@@ -21,5 +46,8 @@ jobs:
- name: Install dependencies
run: bun install
- name: Validate release metadata
run: bun run release:validate
- name: Run tests
run: bun test

View File

@@ -1,47 +0,0 @@
name: Publish to npm
on:
push:
branches: [main]
workflow_dispatch:
jobs:
publish:
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
issues: write
pull-requests: write
concurrency:
group: publish-${{ github.ref }}
cancel-in-progress: false
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Run tests
run: bun test
- name: Setup Node.js for release
uses: actions/setup-node@v4
with:
# npm trusted publishing requires Node 22.14.0+.
node-version: "24"
- name: Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npx semantic-release

83
.github/workflows/release-pr.yml vendored Normal file
View File

@@ -0,0 +1,83 @@
name: Release PR
on:
push:
branches: [main]
workflow_dispatch:
permissions:
contents: write
pull-requests: write
issues: write
concurrency:
group: release-pr-${{ github.ref }}
cancel-in-progress: false
jobs:
release-pr:
runs-on: ubuntu-latest
outputs:
cli_release_created: ${{ steps.release.outputs.release_created }}
cli_tag_name: ${{ steps.release.outputs.tag_name }}
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Validate release metadata scripts
run: bun run release:validate
- name: Maintain release PR
id: release
uses: googleapis/release-please-action@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
config-file: .github/release-please-config.json
manifest-file: .github/.release-please-manifest.json
publish-cli:
needs: release-pr
if: needs.release-pr.outputs.cli_release_created == 'true'
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
concurrency:
group: publish-${{ needs.release-pr.outputs.cli_tag_name }}
cancel-in-progress: false
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
ref: ${{ needs.release-pr.outputs.cli_tag_name }}
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Run tests
run: bun test
- name: Setup Node.js for release
uses: actions/setup-node@v4
with:
node-version: "24"
- name: Publish package
run: npm publish --provenance --access public

94
.github/workflows/release-preview.yml vendored Normal file
View File

@@ -0,0 +1,94 @@
name: Release Preview
on:
workflow_dispatch:
inputs:
title:
description: "Conventional title to evaluate (defaults to the latest commit title on this ref)"
required: false
type: string
cli_bump:
description: "CLI bump override"
required: false
type: choice
options: [auto, patch, minor, major]
default: auto
compound_engineering_bump:
description: "compound-engineering bump override"
required: false
type: choice
options: [auto, patch, minor, major]
default: auto
coding_tutor_bump:
description: "coding-tutor bump override"
required: false
type: choice
options: [auto, patch, minor, major]
default: auto
marketplace_bump:
description: "marketplace bump override"
required: false
type: choice
options: [auto, patch, minor, major]
default: auto
jobs:
preview:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup Bun
uses: oven-sh/setup-bun@v2
with:
bun-version: latest
- name: Install dependencies
run: bun install --frozen-lockfile
- name: Determine title and changed files
id: inputs
shell: bash
run: |
TITLE="${{ github.event.inputs.title }}"
if [ -z "$TITLE" ]; then
TITLE="$(git log -1 --pretty=%s)"
fi
FILES="$(git diff --name-only HEAD~1...HEAD | tr '\n' ' ')"
echo "title=$TITLE" >> "$GITHUB_OUTPUT"
echo "files=$FILES" >> "$GITHUB_OUTPUT"
- name: Add preview note
run: |
echo "This preview currently evaluates the selected ref from its latest commit title and changed files." >> "$GITHUB_STEP_SUMMARY"
echo "It is side-effect free, but it does not yet reconstruct the full accumulated open release PR state." >> "$GITHUB_STEP_SUMMARY"
- name: Validate release metadata
run: bun run release:validate
- name: Preview release
shell: bash
run: |
TITLE='${{ steps.inputs.outputs.title }}'
FILES='${{ steps.inputs.outputs.files }}'
args=(--title "$TITLE" --json)
for file in $FILES; do
args+=(--file "$file")
done
args+=(--override "cli=${{ github.event.inputs.cli_bump || 'auto' }}")
args+=(--override "compound-engineering=${{ github.event.inputs.compound_engineering_bump || 'auto' }}")
args+=(--override "coding-tutor=${{ github.event.inputs.coding_tutor_bump || 'auto' }}")
args+=(--override "marketplace=${{ github.event.inputs.marketplace_bump || 'auto' }}")
bun run scripts/release/preview.ts "${args[@]}" | tee /tmp/release-preview.txt
- name: Publish preview summary
shell: bash
run: cat /tmp/release-preview.txt >> "$GITHUB_STEP_SUMMARY"

View File

@@ -1,36 +0,0 @@
{
"branches": [
"main"
],
"tagFormat": "v${version}",
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
[
"@semantic-release/changelog",
{
"changelogTitle": "# Changelog\n\nAll notable changes to the `@every-env/compound-plugin` CLI tool will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\nRelease numbering now follows the repository `v*` tag line. Starting at `v2.34.0`, the root CLI package and this changelog stay on that shared version stream. Older entries below retain the previous `0.x` CLI numbering."
}
],
"@semantic-release/npm",
[
"@semantic-release/git",
{
"assets": [
"CHANGELOG.md",
"package.json"
],
"message": "chore(release): ${nextRelease.version} [skip ci]"
}
],
[
"@semantic-release/github",
{
"successComment": false,
"failCommentCondition": false,
"labels": false,
"releasedLabels": false
}
]
]
}

View File

@@ -1,19 +1,85 @@
# Agent Instructions
This repository contains a Bun/TypeScript CLI that converts Claude Code plugins into other agent platform formats.
This repository primarily houses the `compound-engineering` coding-agent plugin and the Claude Code marketplace/catalog metadata used to distribute it.
It also contains:
- the Bun/TypeScript CLI that converts Claude Code plugins into other agent platform formats
- additional plugins under `plugins/`, such as `coding-tutor`
- shared release and metadata infrastructure for the CLI, marketplace, and plugins
`AGENTS.md` is the canonical repo instruction file. Root `CLAUDE.md` exists only as a compatibility shim for tools and conversions that still look for it.
## Quick Start
```bash
bun install
bun test # full test suite
bun run release:validate # check plugin/marketplace consistency
```
## Working Agreement
- **Branching:** Create a feature branch for any non-trivial change. If already on the correct branch for the task, keep using it; do not create additional branches or worktrees unless explicitly requested.
- **Safety:** Do not delete or overwrite user data. Avoid destructive commands.
- **Testing:** Run `bun test` after changes that affect parsing, conversion, or output.
- **Release versioning:** The root CLI package (`package.json`, root `CHANGELOG.md`, and repo `v*` tags) uses one shared release line managed by semantic-release on `main`. Do not start or maintain a separate root CLI version stream. Use conventional commits and let release automation write the next root package version. Keep the root changelog header block in sync with `.releaserc.json` `changelogTitle` so generated release entries stay under the header. Embedded marketplace plugin metadata (`plugins/compound-engineering/.claude-plugin/plugin.json` and `.claude-plugin/marketplace.json`) is a separate version surface and may differ, but contributors should not guess or hand-bump release versions for it in normal PRs. The automated release process decides the next plugin/marketplace releases and changelog entries after deciding which merged changes ship together.
- **Release versioning:** Releases are prepared by release automation, not normal feature PRs. The repo now has multiple release components (`cli`, `compound-engineering`, `coding-tutor`, `marketplace`) and one canonical root `CHANGELOG.md`. Use conventional titles such as `feat:` and `fix:` so release automation can classify change intent, but do not hand-bump release-owned versions or changelog entries in routine PRs.
- **Output Paths:** Keep OpenCode output at `opencode.json` and `.opencode/{agents,skills,plugins}`. For OpenCode, command go to `~/.config/opencode/commands/<name>.md`; `opencode.json` is deep-merged (never overwritten wholesale).
- **ASCII-first:** Use ASCII unless the file already contains Unicode.
## Adding a New Target Provider (e.g., Codex)
## Directory Layout
Use this checklist when introducing a new target provider:
```
src/ CLI entry point, parsers, converters, target writers
plugins/ Plugin workspaces (compound-engineering, coding-tutor)
.claude-plugin/ Claude marketplace catalog metadata
tests/ Converter, writer, and CLI tests + fixtures
docs/ Requirements, plans, solutions, and target specs
```
## Repo Surfaces
Changes in this repo may affect one or more of these surfaces:
- `compound-engineering` under `plugins/compound-engineering/`
- the Claude marketplace catalog under `.claude-plugin/`
- the converter/install CLI in `src/` and `package.json`
- secondary plugins such as `plugins/coding-tutor/`
Do not assume a repo change is "just CLI" or "just plugin" without checking which surface owns the affected files.
## Plugin Maintenance
When changing `plugins/compound-engineering/` content:
- Update substantive docs like `plugins/compound-engineering/README.md` when the plugin behavior, inventory, or usage changes.
- Do not hand-bump release-owned versions in plugin or marketplace manifests.
- Do not hand-add canonical release entries to the root `CHANGELOG.md`.
- Run `bun run release:validate` if agents, commands, skills, MCP servers, or release-owned descriptions/counts may have changed.
Useful validation commands:
```bash
bun run release:validate
cat .claude-plugin/marketplace.json | jq .
cat plugins/compound-engineering/.claude-plugin/plugin.json | jq .
```
## Coding Conventions
- Prefer explicit mappings over implicit magic when converting between platforms.
- Keep target-specific behavior in dedicated converters/writers instead of scattering conditionals across unrelated files.
- Preserve stable output paths and merge semantics for installed targets; do not casually change generated file locations.
- When adding or changing a target, update fixtures/tests alongside implementation rather than treating docs or examples as sufficient proof.
## Commit Conventions
- Use conventional titles such as `feat: ...`, `fix: ...`, `docs: ...`, and `refactor: ...`.
- Component scope is optional. Example: `feat(coding-tutor): add quiz reset`.
- Breaking changes must be explicit with `!` or a breaking-change footer so release automation can classify them correctly.
## Adding a New Target Provider
Only add a provider when the target format is stable, documented, and has a clear mapping for tools/permissions/hooks. Use this checklist:
1. **Define the target entry**
- Add a new handler in `src/targets/index.ts` with `implemented: false` until complete.
@@ -37,17 +103,6 @@ Use this checklist when introducing a new target provider:
5. **Docs**
- Update README with the new `--to` option and output locations.
## When to Add a Provider
Add a new provider when at least one of these is true:
- A real user/workflow needs it now.
- The target format is stable and documented.
- Theres a clear mapping for tools/permissions/hooks.
- You can write fixtures + tests that validate the mapping.
Avoid adding a provider if the target spec is unstable or undocumented.
## Agent References in Skills
When referencing agents from within skill SKILL.md files (e.g., via the `Agent` or `Task` tool), always use the **fully-qualified namespace**: `compound-engineering:<category>:<agent-name>`. Never use the short agent name alone.
@@ -60,4 +115,7 @@ This prevents resolution failures when the plugin is installed alongside other p
## Repository Docs Convention
- **Plans** live in `docs/plans/` and track implementation progress.
- **Requirements** live in `docs/brainstorms/` — requirements exploration and ideation.
- **Plans** live in `docs/plans/` — implementation plans and progress tracking.
- **Solutions** live in `docs/solutions/` — documented decisions and patterns.
- **Specs** live in `docs/specs/` — target platform format specifications.

View File

@@ -1,11 +1,15 @@
# Changelog
All notable changes to the `@every-env/compound-plugin` CLI tool will be documented in this file.
This is the canonical changelog for the repository.
Historical entries below reflect the older repo-wide release model. After the release-please migration cutover, new entries are written here as component-scoped headings such as `compound-engineering vX.Y.Z - YYYY-MM-DD` and `cli vX.Y.Z - YYYY-MM-DD`.
All notable changes to the `@every-env/compound-plugin` CLI tool and other release-owned repo components will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
Release numbering now follows the repository `v*` tag line. Starting at `v2.34.0`, the root CLI package and this changelog stay on that shared version stream. Older entries below retain the previous `0.x` CLI numbering.
Historical release numbering below follows the older repository `v*` tag line. Older entries remain intact for continuity.
# [2.42.0](https://github.com/EveryInc/compound-engineering-plugin/compare/v2.41.1...v2.42.0) (2026-03-17)

408
CLAUDE.md
View File

@@ -1,407 +1 @@
# compound-engineering-plugin - Claude Code Plugin Marketplace
This repository is a Claude Code plugin marketplace that distributes the `compound-engineering` plugin to developers building with AI-powered tools.
## Repository Structure
```
compound-engineering-plugin/
├── .claude-plugin/
│ └── marketplace.json # Marketplace catalog (lists available plugins)
├── docs/ # Documentation site (GitHub Pages)
│ ├── index.html # Landing page
│ ├── css/ # Stylesheets
│ ├── js/ # JavaScript
│ └── pages/ # Reference pages
└── plugins/
└── compound-engineering/ # The actual plugin
├── .claude-plugin/
│ └── plugin.json # Plugin metadata
├── agents/ # 24 specialized AI agents
├── commands/ # 13 slash commands
├── skills/ # 11 skills
├── mcp-servers/ # 2 MCP servers (playwright, context7)
├── README.md # Plugin documentation
└── CHANGELOG.md # Version history
```
## Philosophy: Compounding Engineering
**Each unit of engineering work should make subsequent units of work easier—not harder.**
When working on this repository, follow the compounding engineering process:
1. **Plan** → Understand the change needed and its impact
2. **Delegate** → Use AI tools to help with implementation
3. **Assess** → Verify changes work as expected
4. **Codify** → Update this CLAUDE.md with learnings
## Working with This Repository
## CLI Release Versioning
The repository has two separate version surfaces:
1. **Root CLI package**`package.json`, root `CHANGELOG.md`, and repo `v*` tags all share one release line managed by semantic-release on `main`.
2. **Embedded marketplace plugin metadata**`plugins/compound-engineering/.claude-plugin/plugin.json` and `.claude-plugin/marketplace.json` track the distributed Claude plugin metadata and can differ from the root CLI package version.
Rules:
- Do not start a separate root CLI version stream. The root CLI follows the repo tag line.
- Do not hand-bump the root CLI `package.json` or root `CHANGELOG.md` for routine feature work. Use conventional commits and let semantic-release write the released root version back to git.
- Keep the root `CHANGELOG.md` header block aligned with `.releaserc.json` `changelogTitle`. If they drift, semantic-release will prepend release notes above the header.
- Do not guess or hand-bump embedded plugin release versions in routine PRs. The automated release process decides the next plugin/marketplace version and generate release changelog entries after choosing which merged changes ship together.
### Adding a New Plugin
1. Create plugin directory: `plugins/new-plugin-name/`
2. Add plugin structure:
```
plugins/new-plugin-name/
├── .claude-plugin/plugin.json
├── agents/
├── commands/
└── README.md
```
3. Update `.claude-plugin/marketplace.json` to include the new plugin
4. Test locally before committing
### Updating the Compounding Engineering Plugin
When agents, commands, or skills are added/removed, follow this checklist:
#### 1. Count all components accurately
```bash
# Count agents
ls plugins/compound-engineering/agents/*.md | wc -l
# Count commands
ls plugins/compound-engineering/commands/*.md | wc -l
# Count skills
ls -d plugins/compound-engineering/skills/*/ 2>/dev/null | wc -l
```
#### 2. Update ALL description strings with correct counts
The description appears in multiple places and must match everywhere:
- [ ] `plugins/compound-engineering/.claude-plugin/plugin.json` → `description` field
- [ ] `.claude-plugin/marketplace.json` → plugin `description` field
- [ ] `plugins/compound-engineering/README.md` → intro paragraph
Format: `"Includes X specialized agents, Y commands, and Z skill(s)."`
#### 3. Do not pre-cut release versions
Contributors should not guess the next released plugin version in a normal PR:
- [ ] No manual bump in `plugins/compound-engineering/.claude-plugin/plugin.json` → `version`
- [ ] No manual bump in `.claude-plugin/marketplace.json` → plugin `version`
#### 4. Update documentation
- [ ] `plugins/compound-engineering/README.md` → list all components
- [ ] Do not cut a release section in `plugins/compound-engineering/CHANGELOG.md` for a normal feature PR
- [ ] `CLAUDE.md` → update structure diagram if needed
#### 5. Rebuild documentation site
Run the release-docs command to update all documentation pages:
```bash
claude /release-docs
```
This will:
- Update stats on the landing page
- Regenerate reference pages (agents, commands, skills, MCP servers)
- Update the changelog page
- Validate all counts match actual files
#### 6. Validate JSON files
```bash
cat .claude-plugin/marketplace.json | jq .
cat plugins/compound-engineering/.claude-plugin/plugin.json | jq .
```
#### 6. Verify before committing
```bash
# Ensure counts in descriptions match actual files
grep -o "Includes [0-9]* specialized agents" plugins/compound-engineering/.claude-plugin/plugin.json
ls plugins/compound-engineering/agents/*.md | wc -l
```
### Marketplace.json Structure
The marketplace.json follows the official Claude Code spec:
```json
{
"name": "marketplace-identifier",
"owner": {
"name": "Owner Name",
"url": "https://github.com/owner"
},
"metadata": {
"description": "Marketplace description",
"version": "1.0.0"
},
"plugins": [
{
"name": "plugin-name",
"description": "Plugin description",
"version": "1.0.0",
"author": { ... },
"homepage": "https://...",
"tags": ["tag1", "tag2"],
"source": "./plugins/plugin-name"
}
]
}
```
**Only include fields that are in the official spec.** Do not add custom fields like:
- `downloads`, `stars`, `rating` (display-only)
- `categories`, `featured_plugins`, `trending` (not in spec)
- `type`, `verified`, `featured` (not in spec)
### Plugin.json Structure
Each plugin has its own plugin.json with detailed metadata:
```json
{
"name": "plugin-name",
"version": "1.0.0",
"description": "Plugin description",
"author": { ... },
"keywords": ["keyword1", "keyword2"],
"components": {
"agents": 15,
"commands": 6,
"hooks": 2
},
"agents": {
"category": [
{
"name": "agent-name",
"description": "Agent description",
"use_cases": ["use-case-1", "use-case-2"]
}
]
},
"commands": {
"category": ["command1", "command2"]
}
}
```
## Documentation Site
The documentation site is at `/docs` in the repository root (for GitHub Pages). This site is built with plain HTML/CSS/JS (based on Evil Martians' LaunchKit template) and requires no build step to view.
### Documentation Structure
```
docs/
├── index.html # Landing page with stats and philosophy
├── css/
│ ├── style.css # Main styles (LaunchKit-based)
│ └── docs.css # Documentation-specific styles
├── js/
│ └── main.js # Interactivity (theme toggle, mobile nav)
└── pages/
├── getting-started.html # Installation and quick start
├── agents.html # All 24 agents reference
├── commands.html # All 13 commands reference
├── skills.html # All 11 skills reference
├── mcp-servers.html # MCP servers reference
└── changelog.html # Version history
```
### Keeping Docs Up-to-Date
**IMPORTANT:** After ANY change to agents, commands, skills, or MCP servers, run:
```bash
claude /release-docs
```
This command:
1. Counts all current components
2. Reads all agent/command/skill/MCP files
3. Regenerates all reference pages
4. Updates stats on the landing page
5. Updates the changelog from CHANGELOG.md
6. Validates counts match across all files
### Manual Updates
If you need to update docs manually:
1. **Landing page stats** - Update the numbers in `docs/index.html`:
```html
<span class="stat-number">24</span> <!-- agents -->
<span class="stat-number">13</span> <!-- commands -->
```
2. **Reference pages** - Each page in `docs/pages/` documents all components in that category
3. **Changelog** - `docs/pages/changelog.html` mirrors `CHANGELOG.md` in HTML format
### Viewing Docs Locally
Since the docs are static HTML, you can view them directly:
```bash
# Open in browser
open docs/index.html
# Or start a local server
cd docs
python -m http.server 8000
# Then visit http://localhost:8000
```
## Testing Changes
### Test Locally
1. Install the marketplace locally:
```bash
claude /plugin marketplace add /Users/yourusername/compound-engineering-plugin
```
2. Install the plugin:
```bash
claude /plugin install compound-engineering
```
3. Test agents and commands:
```bash
claude /review
claude agent kieran-rails-reviewer "test message"
```
### Validate JSON
Before committing, ensure JSON files are valid:
```bash
cat .claude-plugin/marketplace.json | jq .
cat plugins/compound-engineering/.claude-plugin/plugin.json | jq .
```
## Common Tasks
### Adding a New Agent
1. Create `plugins/compound-engineering/agents/new-agent.md`
2. Update plugin.json agent count and agent list
3. Update README.md agent list
4. Test with `claude agent new-agent "test"`
### Adding a New Command
1. Create `plugins/compound-engineering/commands/new-command.md`
2. Update plugin.json command count and command list
3. Update README.md command list
4. Test with `claude /new-command`
### Adding a New Skill
1. Create skill directory: `plugins/compound-engineering/skills/skill-name/`
2. Add skill structure:
```
skills/skill-name/
├── SKILL.md # Skill definition with frontmatter (name, description)
└── scripts/ # Supporting scripts (optional)
```
3. Update plugin.json description with new skill count
4. Update marketplace.json description with new skill count
5. Update README.md with skill documentation
6. Update CHANGELOG.md with the addition
7. Test with `claude skill skill-name`
**Skill file format (SKILL.md):**
```markdown
---
name: skill-name
description: Brief description of what the skill does
---
# Skill Title
Detailed documentation...
```
### Updating Tags/Keywords
Tags should reflect the compounding engineering philosophy:
- Use: `ai-powered`, `compound-engineering`, `workflow-automation`, `knowledge-management`
- Avoid: Framework-specific tags unless the plugin is framework-specific
## Commit Conventions
Follow these patterns for commit messages:
- `Add [agent/command name]` - Adding new functionality
- `Remove [agent/command name]` - Removing functionality
- `Update [file] to [what changed]` - Updating existing files
- `Fix [issue]` - Bug fixes
- `Simplify [component] to [improvement]` - Refactoring
Include the attribution footer (fill in your actual values):
```
🤖 Generated with [MODEL] via [HARNESS](HARNESS_URL) + Compound Engineering v[VERSION]
Co-Authored-By: [MODEL] ([CONTEXT] context, [THINKING]) <noreply@anthropic.com>
```
**Fill in at commit/PR time:**
| Placeholder | Value | Example |
|-------------|-------|---------|
| Placeholder | Value | Example |
|-------------|-------|---------|
| `[MODEL]` | Model name | Claude Opus 4.6, GPT-5.4 |
| `[CONTEXT]` | Context window (if known) | 200K, 1M |
| `[THINKING]` | Thinking level (if known) | extended thinking |
| `[HARNESS]` | Tool running you | Claude Code, Codex, Gemini CLI |
| `[HARNESS_URL]` | Link to that tool | `https://claude.com/claude-code` |
| `[VERSION]` | `plugin.json` → `version` | 2.40.0 |
## Resources to search for when needing more information
- [Claude Code Plugin Documentation](https://docs.claude.com/en/docs/claude-code/plugins)
- [Plugin Marketplace Documentation](https://docs.claude.com/en/docs/claude-code/plugin-marketplaces)
- [Plugin Reference](https://docs.claude.com/en/docs/claude-code/plugins-reference)
## Key Learnings
_This section captures important learnings as we work on this repository._
### 2024-11-22: Added gemini-imagegen skill and fixed component counts
Added the first skill to the plugin and discovered the component counts were wrong (said 15 agents, actually had 17). Created a comprehensive checklist for updating the plugin to prevent this in the future.
**Learning:** Always count actual files before updating descriptions. The counts appear in multiple places (plugin.json, marketplace.json, README.md) and must all match. Use the verification commands in the checklist above.
### 2024-10-09: Simplified marketplace.json to match official spec
The initial marketplace.json included many custom fields (downloads, stars, rating, categories, trending) that aren't part of the Claude Code specification. We simplified to only include:
- Required: `name`, `owner`, `plugins`
- Optional: `metadata` (with description and version)
- Plugin entries: `name`, `description`, `version`, `author`, `homepage`, `tags`, `source`
**Learning:** Stick to the official spec. Custom fields may confuse users or break compatibility with future versions.
@AGENTS.md

View File

@@ -0,0 +1,89 @@
---
date: 2026-03-17
topic: release-automation
---
# Release Automation and Changelog Ownership
## Problem Frame
The repository currently has one automated release flow for the npm CLI, but the broader release story is split across CI, manual maintainer workflows, stale docs, and multiple version surfaces. That makes it hard to batch releases intentionally, hard for multiple maintainers to share release responsibility, and easy for changelogs, plugin manifests, and derived metadata like component counts to drift out of sync. The goal is to move to a release model that supports intentional batching, independent component versioning, centralized history, and CI-owned release authority without forcing version bumps for untouched plugins.
## Requirements
- R1. The release process must be manually triggered; merging to `main` must not automatically publish a release.
- R2. The release system must support batching: releasable merges may accumulate on `main` until maintainers decide to cut a release.
- R3. The release system must maintain a single release PR for the whole repo that stays open until merged and automatically accumulates additional releasable changes merged to `main`.
- R4. The release system must support independent version bumps for these components: `cli`, `compound-engineering`, `coding-tutor`, and `marketplace`.
- R5. The release system must not bump untouched plugins or unrelated components.
- R6. The release system must preserve one centralized root `CHANGELOG.md` as the canonical changelog for the repository.
- R7. The root changelog must record releases as top-level entries per component version, rather than requiring separate changelog files per plugin.
- R8. Existing root changelog history must be preserved during the migration; the new release model must not discard or rewrite historical entries in a way that loses continuity.
- R9. `plugins/compound-engineering/CHANGELOG.md` must no longer be treated as the canonical changelog after the migration.
- R10. The release process must replace the current `release-docs` workflow; `release-docs` must no longer act as a release authority or required release step.
- R11. Narrow scripts must replace `release-docs` responsibilities, including metadata synchronization, count calculation, docs generation where still needed, and validation.
- R12. Release automation must be the sole authority for version bumps, changelog writes, and computed metadata updates such as counts of agents, skills, commands, or similar release-owned descriptions.
- R13. The release flow must support a dry-run mode that summarizes what would happen without publishing, tagging, or committing release changes.
- R14. Dry run output must clearly summarize which components would release, the proposed version bumps, the changelog entries that would be added, and any blocking validation failures.
- R15. Marketplace version bumps must happen only for marketplace-level changes, such as marketplace metadata changes or adding/removing plugins from the catalog.
- R16. Updating a plugin version alone must not require a marketplace version bump.
- R17. Plugin-only content changes must be releasable without requiring a CLI version bump when the CLI code itself has not changed.
- R18. The release model must remain compatible with the current install behavior where `bunx @every-env/compound-plugin install ...` runs the npm CLI but fetches named plugin content from the GitHub repository at runtime.
- R19. The release process must be triggerable by a maintainer or an AI agent through CI without requiring a local maintainer-only skill.
- R20. The resulting model must scale to future plugins without requiring the repo to special-case `compound-engineering` forever.
- R21. The release model must continue to rely on conventional release intent signals (`feat`, `fix`, breaking changes, etc.), but component scopes in commit or PR titles must remain optional rather than required.
- R22. Release automation must infer component ownership primarily from changed files, not from commit or PR title scopes alone.
- R23. The repo should enforce parseable conventional PR or merge titles strongly enough for release tooling to classify change type, while avoiding mandatory component scoping on every change.
- R24. The manual CI-driven release workflow must support explicit bump overrides for exceptional cases, at least `patch`, `minor`, and `major`, without requiring maintainers to create fake or empty commits purely to coerce a release.
- R25. Bump overrides must be expressible per component rather than only as a repo-wide override.
- R26. Dry run output must clearly show both the inferred bump and any applied manual override for each affected component.
## Success Criteria
- Maintainers can let multiple PRs merge to `main` without immediately cutting a release.
- At any point, maintainers can inspect a release PR or dry run and understand what would ship next.
- A change to `coding-tutor` does not force a version bump to `compound-engineering`.
- A plugin version bump does not force a marketplace version bump unless marketplace-level files changed.
- Release-owned metadata and counts stay in sync without relying on a local slash command.
- The root changelog remains readable and continuous before and after the migration.
## Scope Boundaries
- This work does not require changing how Claude Code itself consumes plugin and marketplace versions.
- This work does not require solving end-user auto-update discovery for non-Claude harnesses in v1.
- This work does not require adding dedicated per-plugin changelog files as the canonical history model.
- This work does not require immediate future automation of release timing; manual release remains the default.
## Key Decisions
- **Use `release-please` rather than a single release-line flow**: The repo now has multiple independently versioned components, and the release PR model matches the need to batch merges on `main` until a release is intentionally cut.
- **One release PR for the whole repo**: Centralized release visibility matters more than separate PRs per component, and a single release PR can still carry multiple component bumps.
- **Manual release timing**: The release process should prepare and accumulate the next release automatically, but the decision to cut that release should remain explicit.
- **Root changelog stays canonical**: Centralized history is more important than per-plugin changelog isolation for the current repo shape.
- **Top-level changelog entries per component version**: This preserves one changelog file while keeping independent component version history readable.
- **Retire `release-docs`**: Its responsibilities are too broad, stale, and conflated. Release logic, docs logic, and metadata synchronization should be separated.
- **Scripts for narrow responsibilities**: Explicit scripts are easier to validate, automate, and reuse from CI than a local repo-maintenance skill.
- **Marketplace version is catalog-scoped**: Plugin version bumps alone should not imply a marketplace release.
- **Conventional type required, component scope optional**: Release intent should still come from conventional commit semantics, but requiring `(compound-engineering)` on most repo changes would add unnecessary wording overhead. Component detection should remain file-driven.
- **Manual bump override is an explicit escape hatch**: Automatic bump inference remains the default, but maintainers should be able to override a component's release level in CI for exceptional cases without awkward synthetic commits.
## Dependencies / Assumptions
- The current install flow for named plugins continues to fetch plugin content from GitHub at runtime, so plugin content releases can remain independent from CLI releases unless CLI behavior also changes.
- Claude Code already respects marketplace and plugin versions, so those version surfaces remain meaningful release signals.
## Outstanding Questions
### Deferred to Planning
- [Affects R3][Technical] Should the release PR be updated automatically on every push to `main`, or via a manually triggered maintenance workflow that refreshes the release PR state on demand?
- [Affects R7][Technical] What exact root changelog format best balances readability and automation for multiple component-version entries in one file?
- [Affects R11][Technical] Which responsibilities should become distinct scripts versus steps embedded directly in the CI workflow?
- [Affects R12][Technical] Which release-owned metadata fields should be computed automatically versus validated and left untouched when no count change is needed?
- [Affects R9][Technical] Should `plugins/compound-engineering/CHANGELOG.md` be deleted, frozen, or replaced with a short pointer note after the migration?
- [Affects R21][Technical] Should conventional-format enforcement happen on PR titles, squash-merge titles, commits, or some combination of them?
- [Affects R24][Technical] Should manual bump overrides be implemented as workflow inputs that shape the generated release PR directly, or as an internal generated release-control commit on the release branch only?
## Next Steps
`/ce:plan` for structured implementation planning

View File

@@ -0,0 +1,605 @@
---
title: "feat: Migrate repo releases to manual release-please with centralized changelog"
type: feat
status: active
date: 2026-03-17
origin: docs/brainstorms/2026-03-17-release-automation-requirements.md
---
# feat: Migrate repo releases to manual release-please with centralized changelog
## Overview
Replace the current single-line `semantic-release` flow and maintainer-local `release-docs` workflow with a repo-owned release system built around `release-please`, a single accumulating release PR, explicit component version ownership, release automation-owned metadata/count updates, and a centralized root `CHANGELOG.md`. The new model keeps release timing manual by making merge of the generated release PR the release action while allowing dry-run previews and automatic release PR maintenance as new merges land on `main`.
## Problem Frame
The current repo mixes one automated root CLI release line with manual plugin release conventions and stale docs/tooling. `publish.yml` publishes on every push to `main`, `.releaserc.json` only understands the root package, `release-docs` still encodes outdated repo structure, and plugin-level version/changelog ownership is inconsistent. The result is drift across root changelog history, plugin manifests, computed counts, and contributor guidance. The origin requirements define a different target: manual release timing, one release PR for the whole repo, independent component versions, no bumps for untouched plugins, centralized changelog ownership, and CI-owned release authority. (see origin: docs/brainstorms/2026-03-17-release-automation-requirements.md)
## Requirements Trace
- R1. Manual release; no publish on every merge to `main`
- R2. Batched releasable changes may accumulate on `main`
- R3. One release PR for the whole repo that auto-accumulates releasable merges
- R4. Independent version bumps for `cli`, `compound-engineering`, `coding-tutor`, and `marketplace`
- R5. Untouched components do not bump
- R6. Root `CHANGELOG.md` remains canonical
- R7. Root changelog uses top-level component-version entries
- R8. Existing changelog history is preserved
- R9. `plugins/compound-engineering/CHANGELOG.md` is no longer canonical
- R10. Retire `release-docs` as release authority
- R11. Replace `release-docs` with narrow scripts
- R12. Release automation owns versions, counts, and release metadata
- R13. Support dry run with no side effects
- R14. Dry run summarizes proposed component bumps, changelog entries, and blockers
- R15. Marketplace version bumps only for marketplace-level changes
- R16. Plugin version changes do not imply marketplace version bumps
- R17. Plugin-only content changes do not force CLI version bumps
- R18. Preserve compatibility with current install behavior where the npm CLI fetches plugin content from GitHub at runtime
- R19. Release flow is triggerable through CI by maintainers or AI agents
- R20. The model must scale to additional plugins
- R21. Conventional release intent signals remain required, but component scopes in titles remain optional
- R22. Component ownership is inferred primarily from changed files, not title scopes alone
- R23. The repo enforces parseable conventional PR or merge titles without requiring component scope on every change
- R24. Manual CI release supports explicit bump overrides for exceptional cases without fake commits
- R25. Bump overrides are per-component rather than repo-wide only
- R26. Dry run shows inferred bump and applied override clearly
## Scope Boundaries
- No change to how Claude Code consumes marketplace/plugin version fields
- No end-user auto-update discovery flow for non-Claude harnesses in v1
- No per-plugin canonical changelog model
- No fully automatic timed release cadence in v1
## Context & Research
### Relevant Code and Patterns
- `.github/workflows/publish.yml` currently runs `npx semantic-release` on every push to `main`; this is the behavior being retired.
- `.releaserc.json` is the current single-line release configuration and only writes `CHANGELOG.md` and `package.json`.
- `package.json` already exposes repo-maintenance scripts and is the natural place to add release preview/validation script entrypoints.
- `src/commands/install.ts` resolves named plugin installs by cloning the GitHub repo and reading `plugins/<name>` at runtime; this means plugin content releases can remain independent from npm CLI releases when CLI code is unchanged.
- `.claude-plugin/marketplace.json`, `plugins/compound-engineering/.claude-plugin/plugin.json`, and `plugins/coding-tutor/.claude-plugin/plugin.json` are the current version-bearing metadata surfaces that need explicit ownership.
- `.claude/commands/release-docs.md` is stale and mixes docs generation, metadata synchronization, validation, and release guidance; it should be replaced rather than modernized in place.
- Existing planning docs in `docs/plans/` use one file per plan, frontmatter with `origin`, and dependency-ordered implementation units with explicit file paths; this plan follows that pattern.
### Institutional Learnings
- `docs/solutions/plugin-versioning-requirements.md` already encodes an important constraint: version bumps and changelog entries should be release-owned, not added in routine feature PRs. The migration should preserve that principle while moving the authority into CI.
### External References
- `release-please` release PR model supports maintaining a standing release PR that updates as more work lands on the default branch.
- `release-please` manifest mode supports multi-component repos and per-component extra file updates, which is a strong fit for plugin manifests and marketplace metadata.
- GitHub Actions `workflow_dispatch` provides a stable manual trigger surface for dry-run preview workflows.
## Key Technical Decisions
- **Use `release-please` for version planning and release PR lifecycle**: The repo needs one accumulating release PR with multiple independently versioned components; that is closer to `release-please`'s native model than to `semantic-release`.
- **Keep one centralized root changelog**: The root `CHANGELOG.md` remains the canonical changelog. Release automation must render component-labeled entries into that one file rather than splitting canonical history across plugin-local changelog files.
- **Use top-level component-version entries in the root changelog**: Each released component version gets its own top-level entry in `CHANGELOG.md`, including the component name, version, and release date in the heading. This keeps one centralized file while preserving readable independent version history.
- **Treat component versioning and changelog rendering as related but separate concerns**: `release-please` can own component version bumps and release PR state, but root changelog formatting may require repo-specific rendering logic to preserve a single readable canonical file.
- **Use explicit release scripts for repo-specific logic**: Count computation, metadata sync, dry-run summaries, and root changelog shaping should live in versioned scripts rather than hidden maintainer-local command prompts.
- **Preserve current plugin delivery assumptions**: Plugin content updates do not force CLI version bumps unless the converter/installer behavior in `src/` changes.
- **Marketplace is catalog-scoped**: Marketplace version bumps depend on marketplace file changes such as plugin additions/removals or marketplace metadata edits, not routine plugin release version updates.
- **Use conventional type as release intent, not mandatory component scope**: `feat`, `fix`, and explicit breaking-change markers remain important release signals, but component scope in PR or merge titles is optional and should not be required for common compound-engineering work.
- **File ownership is authoritative for component selection**: Optional title scope can help notes and validation, but changed-file ownership rules should decide which components bump.
- **Support manual bump overrides as an explicit escape hatch**: Inferred bumping remains the default, but the CI-driven release flow should allow per-component `patch` / `minor` / `major` overrides for exceptional cases without requiring synthetic commits on `main`.
- **Deprecate, do not rely on, legacy changelog/docs surfaces**: `plugins/compound-engineering/CHANGELOG.md` and `release-docs` should stop being live authorities; they should be removed, frozen, or reduced to pointer guidance only after the new flow is in place.
## Root Changelog Format
The root `CHANGELOG.md` should remain the only canonical changelog and should use component-version entries rather than repo-wide release-event entries.
### Format Rules
- Each released component gets its own top-level entry.
- Entry headings include the component name, version, and release date.
- Entries are ordered newest-first in the single root file.
- When multiple components release from the same merged release PR, they appear as adjacent entries with the same date.
- Each entry contains only changes relevant to that component.
- The file keeps a short header note explaining that it is the canonical changelog for the repo and that versions are component-scoped.
- Historical root changelog entries remain in place; the migration adds a note and changes formatting only for new entries after cutover.
### Recommended Heading Shape
```md
## compound-engineering v2.43.0 - 2026-04-10
### Features
- ...
### Fixes
- ...
```
Additional examples:
```md
## coding-tutor v1.2.2 - 2026-04-18
### Fixes
- ...
## marketplace v1.3.0 - 2026-04-18
### Changed
- Added `new-plugin` to the marketplace catalog.
## cli v2.43.1 - 2026-04-21
### Fixes
- Correct OpenClaw install path handling.
```
### Migration Rules
- Preserve all existing root changelog history as published.
- Add a short migration note near the top stating that, starting with the cutover release, entries are recorded per component version in the root file.
- Do not attempt to rewrite or normalize all older entries into the new structure.
- `plugins/compound-engineering/CHANGELOG.md` should no longer receive new canonical entries after cutover.
## Component Release Rules
The release system should use explicit file-to-component ownership rules so unchanged components do not bump accidentally.
### Component Definitions
- **`cli`**: The npm-distributed `@every-env/compound-plugin` package and its release-owned root metadata.
- **`compound-engineering`**: The plugin rooted at `plugins/compound-engineering/`.
- **`coding-tutor`**: The plugin rooted at `plugins/coding-tutor/`.
- **`marketplace`**: Marketplace-level metadata rooted at `.claude-plugin/` and any future repo-owned marketplace-only surfaces.
### File-to-Component Mapping
#### `cli`
Changes that should trigger a `cli` release:
- `src/**`
- `package.json`
- `bun.lock`
- CLI-only tests or fixtures that validate root CLI behavior:
- `tests/cli.test.ts`
- other top-level tests whose subject is the CLI itself
- Release-owned root files only when they reflect a CLI release rather than another component:
- root `CHANGELOG.md` entry generation for the `cli` component
Changes that should **not** trigger `cli` by themselves:
- Plugin content changes under `plugins/**`
- Marketplace metadata changes under `.claude-plugin/**`
- Docs or brainstorm/plan documents unless the repo explicitly decides docs-only changes are releasable for the CLI
#### `compound-engineering`
Changes that should trigger a `compound-engineering` release:
- `plugins/compound-engineering/**`
- Tests or fixtures whose primary purpose is validating compound-engineering content or conversion results derived from that plugin
- Release-owned metadata updates for the compound-engineering plugin:
- `plugins/compound-engineering/.claude-plugin/plugin.json`
- Root `CHANGELOG.md` entry generation for the `compound-engineering` component
Changes that should **not** trigger `compound-engineering` by themselves:
- `plugins/coding-tutor/**`
- Root CLI implementation changes in `src/**`
- Marketplace-only metadata changes
#### `coding-tutor`
Changes that should trigger a `coding-tutor` release:
- `plugins/coding-tutor/**`
- Tests or fixtures whose primary purpose is validating coding-tutor content or conversion results derived from that plugin
- Release-owned metadata updates for the coding-tutor plugin:
- `plugins/coding-tutor/.claude-plugin/plugin.json`
- Root `CHANGELOG.md` entry generation for the `coding-tutor` component
Changes that should **not** trigger `coding-tutor` by themselves:
- `plugins/compound-engineering/**`
- Root CLI implementation changes in `src/**`
- Marketplace-only metadata changes
#### `marketplace`
Changes that should trigger a `marketplace` release:
- `.claude-plugin/marketplace.json`
- Future marketplace-only docs or config files if the repo later introduces them
- Adding a new plugin directory under `plugins/` when that addition is accompanied by marketplace catalog changes
- Removing a plugin from the marketplace catalog
- Marketplace metadata changes such as owner info, catalog description, or catalog-level structure changes
Changes that should **not** trigger `marketplace` by themselves:
- Routine version bumps to existing plugin manifests
- Plugin-only content changes under `plugins/compound-engineering/**` or `plugins/coding-tutor/**`
- Root CLI implementation changes in `src/**`
### Multi-Component Rules
- A single merged PR may trigger multiple components when it changes files owned by each of those components.
- A plugin content change plus a CLI behavior change should release both the plugin and `cli`.
- Adding a new plugin should release at least the new plugin and `marketplace`; it should release `cli` only if the CLI behavior, plugin discovery logic, or install UX also changed.
- Root `CHANGELOG.md` should not itself be used as the primary signal for component detection; it is a release output, not an input.
- Release-owned metadata writes generated by the release flow should not recursively cause unrelated component bumps on subsequent runs.
### Release Intent Rules
- The repo should continue to require conventional release intent markers such as `feat:`, `fix:`, and explicit breaking change notation.
- Component scopes such as `feat(coding-tutor): ...` are optional and should remain optional.
- When a scope is present, it should be treated as advisory metadata that can improve release note grouping or mismatch detection.
- When no scope is present, release automation should still work correctly by using changed-file ownership to determine affected components.
- Docs-only, planning-only, or maintenance-only titles such as `docs:` or `chore:` should remain parseable even when they do not imply a releasable component bump.
### Manual Override Rules
- Automatic bump inference remains the default for all components.
- The manual CI workflow should support override values of at least `patch`, `minor`, and `major`.
- Overrides should be selectable per component rather than only as one repo-wide override.
- Overrides should be treated as exceptional operational controls, not the normal release path.
- When an override is present, release output should show both:
- inferred bump
- override-applied bump
- Overrides should affect the prepared release state without requiring maintainers to add fake commits to `main`.
### Ambiguity Resolution Rules
- If a file exists primarily to support one plugin's content or fixtures, map it to that plugin rather than to `cli`.
- If a shared utility in `src/` changes behavior for all installs/conversions, treat it as a `cli` change even if the immediate motivation came from one plugin.
- If a change only updates docs, brainstorms, plans, or repo instructions, default to no release unless the repo intentionally adds docs-only release semantics later.
- When a new plugin is introduced in the future, add it as its own explicit component rather than folding it into `marketplace` or `cli`.
## Release Workflow Behavior
The release flow should have three distinct modes that share the same component-detection and metadata-rendering logic.
### Release PR Maintenance
- Runs automatically on pushes to `main`.
- Creates one release PR for the repo if none exists.
- Updates the existing open release PR when additional releasable changes land on `main`.
- Includes only components selected by release-intent parsing plus file ownership rules.
- Updates release-owned files only on the release PR branch, not directly on `main`.
- Never publishes npm, creates final GitHub releases, or tags versions as part of this maintenance step.
The maintained release PR should make these outputs visible:
- component version bumps
- draft root changelog entries
- release-owned metadata changes such as plugin version fields and computed counts
### Manual Dry Run
- Runs only through `workflow_dispatch`.
- Computes the same release result the current open release PR would contain, or would create if none exists.
- Produces a human-readable summary in workflow output and optionally an artifact.
- Validates component ownership, conventional release intent, metadata sync, count updates, and root changelog rendering.
- Does not push commits, create or update branches, merge PRs, publish packages, create tags, or create GitHub releases.
The dry-run summary should include:
- detected releasable components
- current version -> proposed version for each component
- draft root changelog entries
- metadata files that would change
- blocking validation failures and non-blocking warnings
### Actual Release Execution
- Happens only when the generated release PR is intentionally merged.
- The merge writes the release-owned version and changelog changes into `main`.
- Post-merge release automation then performs publish steps only for components included in that merged release.
- npm publish runs only when the `cli` component is part of the merged release.
- Non-CLI component releases still update canonical version surfaces and release notes even when no npm publish occurs.
### Safety Rules
- Ordinary feature merges to `main` must never publish by themselves.
- Dry run must remain side-effect free.
- Release PR maintenance, dry run, and post-merge release must use the same underlying release-state computation.
- Release-generated version and metadata writes must not recursively trigger a follow-up release that contains only its own generated churn.
- The release PR merge remains the auditable manual boundary; do not replace it with direct-to-main release commits from a manual workflow.
## Open Questions
### Resolved During Planning
- **Should release timing remain manual?** Yes. The release PR may be maintained automatically, but release happens only when the generated release PR is intentionally merged.
- **Should the release PR update automatically as more merges land on `main`?** Yes. This is a core batching behavior and should remain automatic.
- **Should release preview be distinct from release execution?** Yes. Dry run should be a side-effect-free manual workflow that previews the same release state without mutating branches or publishing anything.
- **Should root changelog history stay centralized?** Yes. The root `CHANGELOG.md` remains canonical to avoid fragmented history.
- **What changelog structure best fits the centralized model?** Top-level component-version entries in the root changelog are the preferred format. This keeps the file centralized while making independent version history readable.
- **What should drive component bumps?** Explicit file-to-component ownership rules. `src/**` drives `cli`, each `plugins/<name>/**` tree drives its own plugin, and `.claude-plugin/marketplace.json` drives `marketplace`.
- **How strict should conventional formatting be?** Conventional type should be required strongly enough for release tooling and release-note generation, but component scope should remain optional to match the repo's work style.
- **Should exceptional manual bumping be supported?** Yes. The release workflow should expose per-component patch/minor/major override controls rather than forcing synthetic commits to manipulate inferred versions.
- **Should marketplace version bump when only a listed plugin version changes?** No. Marketplace bumps are reserved for marketplace-level changes.
- **Should `release-docs` remain part of release authority?** No. It should be retired and replaced with narrow scripts.
### Deferred to Implementation
- What exact combination of `release-please` config and custom post-processing yields the chosen root changelog output without fighting the tool too hard?
- Should conventional-format enforcement happen on PR titles, squash-merge titles, commit messages, or a combination of them?
- Should `plugins/compound-engineering/CHANGELOG.md` be deleted outright or replaced with a short pointer note after the migration is stable?
- Should release preview be implemented by invoking `release-please` in dry-run mode directly, or by a repo-owned script that computes the same summary from component rules and current git state?
- Should final post-merge release execution live in a dedicated publish workflow keyed off merged release PR state, or remain in a renamed/adapted version of the current `publish.yml`?
- Should override inputs be encoded directly into release workflow inputs only, or also persisted into the generated release PR body for auditability?
## Implementation Units
- [x] **Unit 1: Define the new release component model and config scaffolding**
**Goal:** Replace the single-line semantic-release configuration with release-please-oriented repo configuration that expresses the four release components and their version surfaces.
**Requirements:** R1, R3, R4, R5, R15, R16, R17, R20
**Dependencies:** None
**Files:**
- Create: `.release-please-config.json`
- Create: `.release-please-manifest.json`
- Modify: `package.json`
- Modify: `.github/workflows/publish.yml`
- Delete or freeze: `.releaserc.json`
**Approach:**
- Define components for `cli`, `compound-engineering`, `coding-tutor`, and `marketplace`.
- Use manifest configuration so version lines are independent and untouched components do not bump.
- Rework the existing publish workflow so it no longer releases on every push to `main` and instead supports the release-please-driven model.
- Add package scripts for release preview, metadata sync, and validation so CI can call stable entrypoints instead of embedding release logic inline.
- Define the repo's release-intent contract: conventional type required, breaking changes explicit, component scope optional, file ownership authoritative.
- Define the override contract: per-component `auto | patch | minor | major`, with `auto` as the default.
**Patterns to follow:**
- Existing repo-level config files at the root (`package.json`, `.releaserc.json`, `.github/workflows/*.yml`)
- Current release ownership documented in `docs/solutions/plugin-versioning-requirements.md`
**Test scenarios:**
- A plugin-only change maps to that plugin component without implying CLI or marketplace bump.
- A marketplace metadata/catalog change maps to marketplace only.
- A `src/` CLI behavior change maps to the CLI component.
- A combined change yields multiple component updates inside one release PR.
- A title like `fix: adjust ce:plan-beta wording` remains valid without component scope and still produces the right component mapping from files.
- A manual override can promote an inferred patch bump for one component to minor without affecting unrelated components.
**Verification:**
- The repo contains a single authoritative release configuration model for all versioned components.
- The old automatic-on-push semantic-release path is removed or inert.
- Package scripts exist for preview/sync/validate entrypoints.
- Release intent rules are documented without forcing repetitive component scoping on routine CE work.
- [x] **Unit 2: Build repo-owned release scripts for metadata sync, counts, and preview**
**Goal:** Replace `release-docs` and ad-hoc release bookkeeping with explicit scripts that compute release-owned metadata updates and produce dry-run summaries.
**Requirements:** R10, R11, R12, R13, R14, R18, R19
**Dependencies:** Unit 1
**Files:**
- Create: `scripts/release/sync-metadata.ts`
- Create: `scripts/release/render-root-changelog.ts`
- Create: `scripts/release/preview.ts`
- Create: `scripts/release/validate.ts`
- Modify: `package.json`
**Approach:**
- `sync-metadata.ts` should own count calculation and synchronized writes to release-owned metadata fields such as manifest descriptions and version mirrors.
- `render-root-changelog.ts` should generate the centralized root changelog entries in the agreed component-version format.
- `preview.ts` should summarize proposed component bumps, generated changelog entries, affected files, and validation blockers without mutating the repo or publishing anything.
- `validate.ts` should provide a stable CI check for component counts, manifest consistency, and changelog formatting expectations.
- `preview.ts` should accept optional per-component overrides and display both inferred and effective bump levels in its summary output.
**Patterns to follow:**
- TypeScript/Bun scripting already used elsewhere in the repo
- Root package scripts as stable repo entrypoints
**Test scenarios:**
- Count calculation updates plugin descriptions correctly when agents/skills change.
- Preview output includes only changed components.
- Preview mode performs no file writes.
- Validation fails when manifest counts or version ownership rules drift.
- Root changelog renderer produces component-version entries with stable ordering and headings.
- Preview output clearly distinguishes inferred bump from override-applied bump when an override is used.
**Verification:**
- `release-docs` responsibilities are covered by explicit scripts.
- Dry run can run in CI without side effects.
- Metadata/count drift can be detected deterministically before release.
- [x] **Unit 3: Wire release PR maintenance and manual release execution in CI**
**Goal:** Establish one standing release PR for the repo that updates automatically as new releasable work lands, while keeping the actual release action manual.
**Requirements:** R1, R2, R3, R13, R14, R19
**Dependencies:** Units 1-2
**Files:**
- Create: `.github/workflows/release-pr.yml`
- Create: `.github/workflows/release-preview.yml`
- Modify: `.github/workflows/ci.yml`
- Modify: `.github/workflows/publish.yml`
**Approach:**
- `release-pr.yml` should run on push to `main` and maintain the standing release PR for the whole repo.
- The actual release event should remain merge of that generated release PR; no automatic publish should happen on ordinary merges to `main`.
- `release-preview.yml` should use `workflow_dispatch` with explicit dry-run inputs and publish a human-readable summary to workflow logs and/or artifacts.
- Decide whether npm publish remains in `publish.yml` or moves into the release-please-driven workflow, but ensure it runs only when the CLI component is actually releasing.
- Keep normal `ci.yml` focused on verification, not publishing.
- Add lightweight validation for release-intent formatting on PR or merge titles, without requiring component scopes.
- Ensure release PR maintenance, dry run, and post-merge publish all call the same underlying release-state computation so they cannot drift.
- Add workflow inputs for per-component bump overrides and ensure they can shape the prepared release state when explicitly invoked by a maintainer or AI agent.
**Patterns to follow:**
- Existing GitHub workflow layout in `.github/workflows/`
- Current manual `workflow_dispatch` presence in `publish.yml`
**Test scenarios:**
- A normal merge to `main` updates or creates the release PR but does not publish.
- A manual dry-run workflow produces a summary with no tags, commits, or publishes.
- Merging the release PR results in release creation for changed components only.
- A release that excludes CLI does not attempt npm publish.
- A PR titled `feat: add new plan-beta handoff guidance` passes validation without a component scope.
- A PR titled with an explicit contradictory scope can be surfaced as a warning or failure if file ownership clearly disagrees.
- A second releasable merge to `main` updates the existing open release PR instead of creating a competing release PR.
- A dry run executed while a release PR is open reports the same proposed component set and versions as the PR contents.
- Merging a release PR does not immediately create a follow-up release PR containing only release-generated metadata churn.
- A manual workflow can override one component to `major` while leaving other components on inferred `auto`.
**Verification:**
- Maintainers can inspect the current release PR to see the pending release batch.
- Dry-run and actual-release paths are distinct and safe.
- The release system is triggerable through CI without local maintainer-only tooling.
- The same proposed release state is visible consistently across release PR maintenance, dry run, and post-merge release execution.
- Exceptional release overrides are possible without synthetic commits on `main`.
- [x] **Unit 4: Centralize changelog ownership and retire plugin-local canonical release history**
**Goal:** Make the root changelog the only canonical changelog while preserving history and preventing future fragmentation.
**Requirements:** R6, R7, R8, R9
**Dependencies:** Units 1-3
**Files:**
- Modify: `CHANGELOG.md`
- Modify or replace: `plugins/compound-engineering/CHANGELOG.md`
- Optionally create: `plugins/coding-tutor/CHANGELOG.md` only if needed as a non-canonical pointer or future placeholder
**Approach:**
- Add a migration note near the top of the root changelog clarifying that it is the canonical changelog for the repo and future releases.
- Render future canonical entries into the root file as top-level component-version entries using the agreed heading shape.
- Stop writing future canonical entries into `plugins/compound-engineering/CHANGELOG.md`.
- Replace the plugin-local changelog with either a short pointer note or a frozen historical file, depending on the least confusing path discovered during implementation.
- Keep existing root changelog entries intact; do not attempt to rewrite historical releases into a new structure retroactively.
**Patterns to follow:**
- Existing Keep a Changelog-style root file
- Brainstorm decision favoring centralized history over fragmented per-plugin changelogs
**Test scenarios:**
- Historical root changelog entries remain intact after migration.
- New generated entries appear in the root changelog in the intended component-version format.
- Multiple components released on the same day appear as separate adjacent entries rather than being merged into one release-event block.
- Component-specific notes do not leak unrelated changes into the wrong entry.
- Plugin-local CE changelog no longer acts as a live release target.
**Verification:**
- A maintainer reading the repo can identify one canonical changelog without ambiguity.
- No history is lost or silently rewritten.
- [x] **Unit 5: Remove legacy release guidance and replace it with the new authority model**
**Goal:** Update repo instructions and docs so contributors follow the new release system rather than obsolete semantic-release or `release-docs` guidance.
**Requirements:** R10, R11, R12, R19, R20
**Dependencies:** Units 1-4
**Files:**
- Modify: `AGENTS.md`
- Modify: `CLAUDE.md`
- Modify: `plugins/compound-engineering/AGENTS.md`
- Modify: `docs/solutions/plugin-versioning-requirements.md`
- Delete: `.claude/commands/release-docs.md` or replace with a deprecation stub
**Approach:**
- Update all contributor-facing docs so they describe release PR maintenance, manual release merge, centralized root changelog ownership, and the new scripts for sync/preview/validate.
- Remove references that tell contributors to run `release-docs` or to rely on stale docs-generation assumptions.
- Keep the contributor rule that release-owned metadata should not be hand-bumped in ordinary PRs, but point that rule at release automation rather than a local maintainer slash command.
- Document the release-intent policy explicitly: conventional type required, component scope optional, breaking changes explicit.
**Patterns to follow:**
- Existing contributor guidance files already used as authoritative workflow docs
**Test scenarios:**
- No user-facing doc still points to `release-docs` as a required release workflow.
- No contributor guidance still claims plugin-local changelog authority for CE.
- Release ownership guidance is consistent across root and plugin-level instruction files.
**Verification:**
- A new maintainer can understand the release process from docs alone without hidden local workflows.
- Docs no longer encode obsolete repo structure or stale release surfaces.
- [x] **Unit 6: Add automated coverage for component detection, metadata sync, and release preview**
**Goal:** Protect the new release model against regression by testing the component rules, metadata updates, and preview behavior.
**Requirements:** R4, R5, R12, R13, R14, R15, R16, R17
**Dependencies:** Units 1-5
**Files:**
- Create: `tests/release-metadata.test.ts`
- Create: `tests/release-preview.test.ts`
- Create: `tests/release-components.test.ts`
- Modify: `package.json`
**Approach:**
- Add fixture-driven tests for file-change-to-component mapping.
- Snapshot or assert dry-run summaries for representative release cases.
- Verify metadata sync updates only expected files and counts.
- Cover the marketplace-specific rule so plugin-only version changes do not trigger marketplace bumps.
- Encode ambiguity-resolution cases explicitly so future contributors can add new plugins without guessing which component should bump.
- Add validation coverage for release-intent parsing so conventional titles remain required but optional scopes remain non-blocking when omitted.
- Add override-path coverage so manual bump overrides remain scoped, visible, and side-effect free in preview mode.
**Patterns to follow:**
- Existing top-level Bun test files under `tests/`
- Current fixture-driven testing style used by converters and writers
**Test scenarios:**
- Change only `plugins/coding-tutor/**` and confirm only `coding-tutor` bumps.
- Change only `plugins/compound-engineering/**` and confirm only CE bumps.
- Change only marketplace catalog metadata and confirm only marketplace bumps.
- Change only `src/**` and confirm only CLI bumps.
- Combined `src/**` + plugin change yields both component bumps.
- Change docs only and confirm no component bumps by default.
- Add a new plugin directory plus marketplace catalog entry and confirm new-plugin + marketplace bump without forcing unrelated existing plugin bumps.
- Dry-run preview lists the same components that the component detector identifies.
- Conventional `fix:` / `feat:` titles without scope pass validation.
- Explicit breaking-change markers are recognized.
- Optional scopes, when present, can be compared against file ownership without becoming mandatory.
- Override one component in preview and confirm only that component's effective bump changes.
- Override does not create phantom bumps for untouched components.
**Verification:**
- The release model is covered by automated tests rather than only CI trial runs.
- Future plugin additions can follow the same component-detection pattern with low risk.
## System-Wide Impact
- **Interaction graph:** Release config, CI workflows, metadata-bearing JSON files, contributor docs, and changelog generation are all coupled. The plan deliberately separates configuration, scripting, release PR maintenance, and documentation cleanup so one layer can change without obscuring another.
- **Error propagation:** Release metadata drift should fail in preview/validation before a release PR or publish path proceeds. CI needs clear failure reporting because release mistakes affect user-facing version surfaces.
- **State lifecycle risks:** Partial migration is risky. Running old and new release authorities simultaneously could double-write changelog entries, version fields, or publish flows. The migration should explicitly disable the old path before trusting the new one.
- **API surface parity:** Contributor-facing workflows in `AGENTS.md`, `CLAUDE.md`, and plugin-level instructions must all describe the same release authority model or maintainers will continue using legacy local commands.
- **Integration coverage:** Unit tests for scripts are not enough. The workflow interaction between release PR maintenance, dry-run preview, and conditional CLI publish needs at least one integration-level verification path in CI.
## Risks & Dependencies
- `release-please` may not natively express the exact root changelog shape you want; custom rendering may be required.
- If old semantic-release and new release-please flows overlap during migration, duplicate or conflicting release writes are likely.
- The distinction between version-bearing metadata and descriptive/count-bearing metadata must stay explicit; otherwise scripts may overwrite user-edited documentation that should remain manual.
- Release preview quality matters. If dry run is vague or noisy, maintainers will bypass it and the manual batching goal will weaken.
- Removing `release-docs` may expose other hidden docs/deploy assumptions, especially if GitHub Pages or docs generation still depend on stale paths.
## Documentation / Operational Notes
- Document one canonical release path: release PR maintenance on push to `main`, dry-run preview on manual dispatch, actual release on merge of the generated release PR.
- Document one canonical changelog: root `CHANGELOG.md`.
- Document one rule for contributors: ordinary feature PRs do not hand-bump release-owned versions or changelog entries.
- Add a short migration note anywhere old release instructions are likely to be rediscovered, especially around `plugins/compound-engineering/CHANGELOG.md` and the removed `release-docs` command.
- After merge, run one live GitHub Actions validation pass to confirm `release-please` tag/output wiring and conditional CLI publish behavior end to end.
## Sources & References
- **Origin document:** [docs/brainstorms/2026-03-17-release-automation-requirements.md](docs/brainstorms/2026-03-17-release-automation-requirements.md)
- Existing release workflow: `.github/workflows/publish.yml`
- Existing semantic-release config: `.releaserc.json`
- Existing release-owned guidance: `docs/solutions/plugin-versioning-requirements.md`
- Legacy repo-maintenance command to retire: `.claude/commands/release-docs.md`
- Install behavior reference: `src/commands/install.ts`
- External docs: `release-please` manifest and release PR documentation, GitHub Actions `workflow_dispatch`

View File

@@ -650,13 +650,12 @@ Use this checklist when adding a new target provider:
### Documentation
- [ ] Create `docs/specs/{target}.md` with format specification
- [ ] Update `README.md` with target in list and usage examples
- [ ] Update `CHANGELOG.md` with new target
- [ ] Do not hand-add a release entry; release automation owns canonical changelog updates
### Version Bumping
- [ ] Use a `feat(...)` conventional commit so semantic-release cuts the next minor root CLI release on `main`
- [ ] Do not hand-start a separate root CLI version line in `package.json`; the root package follows the repo `v*` tags and semantic-release writes that version back after release
- [ ] Update plugin.json description if component counts changed
- [ ] Verify CHANGELOG entry is clear
- [ ] Use a conventional `feat:` or `fix:` title so release automation can infer the right bump
- [ ] Do not hand-start or hand-bump release-owned version lines in `package.json` or plugin manifests
- [ ] Run `bun run release:validate` if component counts or descriptions changed
---

View File

@@ -13,20 +13,20 @@ component: plugin-development
When making changes to the compound-engineering plugin, documentation can get out of sync with the actual components (agents, commands, skills). This leads to confusion about what's included in each version and makes it difficult to track changes over time.
This document applies to the embedded marketplace plugin metadata, not the root CLI package release version. The root CLI package (`package.json`, root `CHANGELOG.md`, repo `v*` tags) is managed by semantic-release and follows the repository tag line.
This document applies to release-owned plugin metadata and changelog surfaces, not ordinary feature work. The repo now treats `cli`, `compound-engineering`, `coding-tutor`, and `marketplace` as separate release components prepared by release automation.
## Solution
**Routine PRs should not cut plugin releases.**
The embedded plugin version is release-owned metadata. The maintainer uses a local slash command to choose the next version and generate release changelog entries after deciding which merged changes ship together. Because multiple PRs may merge before release, contributors should not guess release versions inside individual PRs.
Embedded plugin versions are release-owned metadata. Release automation prepares the next versions and changelog entries after deciding which merged changes ship together. Because multiple PRs may merge before release, contributors should not guess release versions inside individual PRs.
Contributors should:
1. **Avoid release bookkeeping in normal PRs**
- Do not manually bump `.claude-plugin/plugin.json`
- Do not manually bump `.claude-plugin/marketplace.json`
- Do not cut release sections in `CHANGELOG.md`
- Do not cut release sections in the root `CHANGELOG.md`
2. **Keep substantive docs accurate**
- Verify component counts match actual files
@@ -49,7 +49,7 @@ Before committing changes to compound-engineering plugin:
## File Locations
- Version is release-owned: `.claude-plugin/plugin.json` and `.claude-plugin/marketplace.json`
- Changelog release sections are release-owned: `CHANGELOG.md`
- Changelog release sections are release-owned: root `CHANGELOG.md`
- Readme: `README.md`
## Example Workflow
@@ -60,7 +60,7 @@ When adding a new agent:
2. Update README agent table
3. Update README component count
4. Update plugin metadata description with new counts if needed
5. Leave version selection and release changelog generation to the maintainer's release command
5. Leave version selection and release changelog generation to release automation
## Prevention
@@ -73,7 +73,6 @@ This documentation serves as a reminder. When Claude Code works on this plugin,
## Related Files
- `plugins/compound-engineering/.claude-plugin/plugin.json`
- `plugins/compound-engineering/CHANGELOG.md`
- `plugins/compound-engineering/README.md`
- `package.json`
- `CHANGELOG.md`

View File

@@ -112,7 +112,7 @@ Detailed instructions...
- Markdown files in `.kiro/steering/`.
- Always loaded into every agent session's context.
- Equivalent to Claude Code's CLAUDE.md.
- Equivalent to the repo instruction file used by Claude-oriented workflows; in this repo `AGENTS.md` is canonical and `CLAUDE.md` may exist only as a compatibility shim.
- Used for project-wide instructions, coding standards, and conventions.
## MCP server configuration
@@ -166,6 +166,6 @@ Detailed instructions...
| Generated agents (JSON + prompt) | Overwrite | Generated, not user-authored |
| Generated skills (from commands) | Overwrite | Generated, not user-authored |
| Copied skills (pass-through) | Overwrite | Plugin is source of truth |
| Steering files | Overwrite | Generated from CLAUDE.md |
| Steering files | Overwrite | Generated from `AGENTS.md` when present, otherwise `CLAUDE.md` |
| `mcp.json` | Merge with backup | User may have added their own servers |
| User-created agents/skills | Preserved | Don't delete orphans |

View File

@@ -17,7 +17,9 @@
"list": "bun run src/index.ts list",
"cli:install": "bun run src/index.ts install",
"test": "bun test",
"release:dry-run": "semantic-release --dry-run"
"release:preview": "bun run scripts/release/preview.ts",
"release:sync-metadata": "bun run scripts/release/sync-metadata.ts --write",
"release:validate": "bun run scripts/release/validate.ts"
},
"dependencies": {
"citty": "^0.1.6",

View File

@@ -2,7 +2,7 @@
"name": "compound-engineering",
"displayName": "Compound Engineering",
"version": "2.33.0",
"description": "AI-powered development tools. 28 agents, 22 commands, 19 skills, 1 MCP server for code review, research, design, and workflow automation.",
"description": "AI-powered development tools. 29 agents, 44 skills, 1 MCP server for code review, research, design, and workflow automation.",
"author": {
"name": "Kieran Klaassen",
"email": "kieran@every.to",

View File

@@ -9,13 +9,13 @@ They supplement the repo-root `AGENTS.md`.
**IMPORTANT**: Routine PRs should not cut releases for this plugin.
The repo uses an automatied release process to prepare plugin releases, including version selection and changelog generation. Because multiple PRs may merge before the next release, contributors cannot know the final released version from within an individual PR.
The repo uses an automated release process to prepare plugin releases, including version selection and changelog generation. Because multiple PRs may merge before the next release, contributors cannot know the final released version from within an individual PR.
### Contributor Rules
- Do **not** manually bump `.claude-plugin/plugin.json` version in a normal feature PR.
- Do **not** manually bump `.claude-plugin/marketplace.json` plugin version in a normal feature PR.
- Do **not** cut a release section in `CHANGELOG.md` for a normal feature PR.
- Do **not** cut a release section in the canonical root `CHANGELOG.md` for a normal feature PR.
- Do update substantive docs that are part of the actual change, such as `README.md`, component tables, usage instructions, or counts when they would otherwise become inaccurate.
### Pre-Commit Checklist
@@ -24,7 +24,7 @@ Before committing ANY changes:
- [ ] No manual release-version bump in `.claude-plugin/plugin.json`
- [ ] No manual release-version bump in `.claude-plugin/marketplace.json`
- [ ] No manual release entry added to `CHANGELOG.md`
- [ ] No manual release entry added to the root `CHANGELOG.md`
- [ ] README.md component counts verified
- [ ] README.md tables accurate (agents, commands, skills)
- [ ] plugin.json description matches current counts
@@ -116,42 +116,14 @@ grep -E '`(references|assets|scripts)/[^`]+`' skills/*/SKILL.md
grep -E '^description:' skills/*/SKILL.md
```
## Adding Components
- **New skill:** Create `skills/<name>/SKILL.md` with required YAML frontmatter (`name`, `description`). Reference files go in `skills/<name>/references/`.
- **New agent:** Create `agents/<category>/<name>.md` with frontmatter. Categories: `review`, `research`, `design`, `docs`, `workflow`.
## Beta Skills
Beta skills are experimental versions of core workflow skills, published as separate skills with a `-beta` suffix (e.g., `ce-plan-beta`, `deepen-plan-beta`). They live alongside the stable versions and are invoked directly.
See `docs/solutions/skill-design/beta-skills-framework.md` for the full pattern.
### Beta Skill Rules
- Beta skills use `-beta` suffix in directory name, skill name, and description prefix (`[BETA]`)
- Beta skills set `disable-model-invocation: true` to prevent accidental auto-triggering — users invoke them manually
- Beta skill descriptions should be the intended stable description prefixed with `[BETA]`, so promotion is a simple prefix removal
- Beta skills must reference other beta skills by their beta names (e.g., `/deepen-plan-beta`, not `/deepen-plan`)
- Beta plan output files use `-beta-plan.md` suffix to avoid clobbering stable plan files
- Beta skills are not wired into `lfg`/`slfg` orchestration — invoke them directly
### Beta Skill Validation
After creating or modifying a beta skill, search its SKILL.md for any reference to the stable skill name it replaces. Occurrences of the stable name without `-beta` are missed renames that would cause output collisions or misrouting. Check for:
- Output file paths using the stable naming convention instead of the `-beta` variant
- Cross-skill references pointing to stable names instead of beta counterparts
- User-facing text (questions, confirmations) mentioning stable paths or names
### Promoting Beta to Stable
When replacing a stable skill with its beta version:
- [ ] Replace stable `SKILL.md` content with beta skill content
- [ ] Restore stable frontmatter: remove `[BETA]` prefix from description, restore stable `name:` (e.g., `ce:plan` not `ce:plan-beta`)
- [ ] Remove `disable-model-invocation: true` so the model can auto-trigger the skill
- [ ] Update all internal references back to stable names (`/deepen-plan` not `/deepen-plan-beta`)
- [ ] Restore stable plan file naming (remove `-beta` from `-beta-plan.md` convention)
- [ ] Delete the beta skill directory
- [ ] Update README.md: remove from Beta Skills section, verify counts
- [ ] Verify `lfg`/`slfg` still work with the updated stable skill
- [ ] Verify `ce:work` consumes plans from the promoted skill correctly
Beta skills use a `-beta` suffix and `disable-model-invocation: true` to prevent accidental auto-triggering. See `docs/solutions/skill-design/beta-skills-framework.md` for naming, validation, and promotion rules.
## Documentation

View File

@@ -1,5 +1,9 @@
# Changelog
This file is no longer the canonical changelog for compound-engineering releases.
Historical entries are preserved below, but new release history is recorded in the root [`CHANGELOG.md`](../../CHANGELOG.md).
All notable changes to the compound-engineering plugin will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

View File

@@ -244,7 +244,7 @@ Set `CONTEXT7_API_KEY` in your environment to authenticate. Or add it globally i
## Version History
See [CHANGELOG.md](CHANGELOG.md) for detailed version history.
See the repo root [CHANGELOG.md](../../CHANGELOG.md) for canonical release history.
## License

View File

@@ -65,7 +65,7 @@ You are an expert design-to-code synchronization specialist with deep expertise
- Move any width constraints and horizontal padding to wrapper divs in parent HTML/ERB
- Update component props or configuration
- Adjust layout structures if needed
- Ensure changes follow the project's coding standards from CLAUDE.md
- Ensure changes follow the project's coding standards from AGENTS.md
- Use mobile-first responsive patterns (e.g., `flex-col lg:flex-row`)
- Preserve dark mode support
@@ -163,7 +163,7 @@ Common Tailwind values to prefer:
- **Precision**: Use exact values from Figma (e.g., "16px" not "about 15-17px"), but prefer Tailwind defaults when close enough
- **Completeness**: Address all differences, no matter how minor
- **Code Quality**: Follow CLAUDE.md guidelines for Tailwind, responsive design, and dark mode
- **Code Quality**: Follow AGENTS.md guidance for project-specific frontend conventions
- **Communication**: Be specific about what changed and why
- **Iteration-Ready**: Design your fixes to allow the agent to run again for verification
- **Responsive First**: Always implement mobile-first responsive designs with appropriate breakpoints

View File

@@ -32,7 +32,7 @@ You are an expert repository research analyst specializing in understanding code
**Core Responsibilities:**
1. **Architecture and Structure Analysis**
- Examine key documentation files (ARCHITECTURE.md, README.md, CONTRIBUTING.md, CLAUDE.md)
- Examine key documentation files (ARCHITECTURE.md, README.md, CONTRIBUTING.md, AGENTS.md, and CLAUDE.md only if present for compatibility)
- Map out the repository's organizational structure
- Identify architectural patterns and design decisions
- Note any project-specific conventions or standards
@@ -121,7 +121,7 @@ Structure your findings as:
**Important Considerations:**
- Respect any CLAUDE.md or project-specific instructions found
- Respect any AGENTS.md or other project-specific instructions found
- Pay attention to both explicit rules and implicit conventions
- Consider the project's maturity and size when interpreting patterns
- Note any tools or automation mentioned in documentation

View File

@@ -69,4 +69,4 @@ When analyzing code:
- Provide actionable recommendations, not just criticism
- Consider the project's maturity and technical debt tolerance
If you encounter project-specific patterns or conventions (especially from CLAUDE.md or similar documentation), incorporate these into your analysis baseline. Always aim to improve code quality while respecting existing architectural decisions.
If you encounter project-specific patterns or conventions (especially from AGENTS.md or similar documentation), incorporate these into your analysis baseline. Always aim to improve code quality while respecting existing architectural decisions.

View File

@@ -40,7 +40,7 @@ When you receive a comment or review feedback, you will:
- Maintaining consistency with the existing codebase style and patterns
- Ensuring the change doesn't break existing functionality
- Following any project-specific guidelines from CLAUDE.md
- Following any project-specific guidelines from AGENTS.md (or CLAUDE.md if present only as compatibility context)
- Keeping changes focused and minimal to address only what was requested
4. **Verify the Resolution**: After making changes:

View File

@@ -83,7 +83,7 @@ Scan the repo before substantive brainstorming. Match depth to scope:
**Standard and Deep** — Two passes:
*Constraint Check* — Check project instruction files (`AGENTS.md`, `CLAUDE.md`) for workflow, product, or scope constraints that affect the brainstorm. If these add nothing, move on.
*Constraint Check* — Check project instruction files (`AGENTS.md`, and `CLAUDE.md` only if retained as compatibility context) for workflow, product, or scope constraints that affect the brainstorm. If these add nothing, move on.
*Topic Scan* — Search for relevant terms. Read the most relevant existing artifact if one exists (brainstorm, plan, spec, skill, feature doc). Skim adjacent examples covering similar behavior.

View File

@@ -103,7 +103,7 @@ Run agents in parallel in the **foreground** (do not use background dispatch —
1. **Quick context scan** — dispatch a general-purpose sub-agent with this prompt:
> Read the project's CLAUDE.md (or AGENTS.md / README.md if CLAUDE.md is absent), then discover the top-level directory layout using the native file-search/glob tool (e.g., `Glob` with pattern `*` or `*/*` in Claude Code). Return a concise summary (under 30 lines) covering:
> Read the project's AGENTS.md (or CLAUDE.md only as compatibility fallback, then README.md if neither exists), then discover the top-level directory layout using the native file-search/glob tool (e.g., `Glob` with pattern `*` or `*/*` in Claude Code). Return a concise summary (under 30 lines) covering:
> - project shape (language, framework, top-level directory layout)
> - notable patterns or conventions
> - obvious pain points or gaps

View File

@@ -150,7 +150,7 @@ Run these agents in parallel:
Collect:
- Existing patterns and conventions to follow
- Relevant files, modules, and tests
- CLAUDE.md or AGENTS.md guidance that materially affects the plan
- AGENTS.md guidance that materially affects the plan, with CLAUDE.md used only as compatibility fallback when present
- Institutional learnings from `docs/solutions/`
#### 1.2 Decide on External Research
@@ -545,7 +545,7 @@ If running with ultrathink enabled, or the platform's reasoning/effort level is
## Issue Creation
When the user selects "Create Issue", detect their project tracker from `CLAUDE.md` or `AGENTS.md`:
When the user selects "Create Issue", detect their project tracker from `AGENTS.md` or, if needed for compatibility, `CLAUDE.md`:
1. Look for `project_tracker: github` or `project_tracker: linear`
2. If GitHub:
@@ -562,7 +562,7 @@ When the user selects "Create Issue", detect their project tracker from `CLAUDE.
4. If no tracker is configured:
- Ask which tracker they use using the platform's blocking question tool when available (see Interaction Method)
- Suggest adding the tracker to `CLAUDE.md` or `AGENTS.md` for future runs
- Suggest adding the tracker to `AGENTS.md` for future runs
After issue creation:
- Display the issue URL

View File

@@ -87,7 +87,7 @@ Run these agents **in parallel** to gather local context:
- Task compound-engineering:research:learnings-researcher(feature_description)
**What to look for:**
- **Repo research:** existing patterns, CLAUDE.md guidance, technology familiarity, pattern consistency
- **Repo research:** existing patterns, AGENTS.md guidance, technology familiarity, pattern consistency
- **Learnings:** documented solutions in `docs/solutions/` that might apply (gotchas, patterns, lessons learned)
These findings inform the next step.
@@ -98,7 +98,7 @@ Based on signals from Step 0 and findings from Step 1, decide on external resear
**High-risk topics → always research.** Security, payments, external APIs, data privacy. The cost of missing something is too high. This takes precedence over speed signals.
**Strong local context skip external research.** Codebase has good patterns, CLAUDE.md has guidance, user knows what they want. External research adds little value.
**Strong local context -> skip external research.** Codebase has good patterns, AGENTS.md has guidance, user knows what they want. External research adds little value.
**Uncertainty or unfamiliar territory → research.** User is exploring, codebase has no examples, new technology. External perspective is valuable.
@@ -125,7 +125,7 @@ After all research steps complete, consolidate findings:
- **Include relevant institutional learnings** from `docs/solutions/` (key insights, gotchas to avoid)
- Note external documentation URLs and best practices (if external research was done)
- List related issues or PRs discovered
- Capture CLAUDE.md conventions
- Capture AGENTS.md conventions
**Optional validation:** Briefly summarize findings and ask if anything looks off or missing before proceeding to planning.
@@ -611,9 +611,9 @@ Loop back to options after Simplify or Other changes until user selects `/ce:wor
## Issue Creation
When user selects "Create Issue", detect their project tracker from CLAUDE.md:
When user selects "Create Issue", detect their project tracker from AGENTS.md:
1. **Check for tracker preference** in user's CLAUDE.md (global or project):
1. **Check for tracker preference** in the user's AGENTS.md (global or project). If AGENTS.md is absent, fall back to CLAUDE.md:
- Look for `project_tracker: github` or `project_tracker: linear`
- Or look for mentions of "GitHub Issues" or "Linear" in their workflow section
@@ -633,7 +633,7 @@ When user selects "Create Issue", detect their project tracker from CLAUDE.md:
4. **If no tracker configured:**
Ask user: "Which project tracker do you use? (GitHub/Linear/Other)"
- Suggest adding `project_tracker: github` or `project_tracker: linear` to their CLAUDE.md
- Suggest adding `project_tracker: github` or `project_tracker: linear` to their AGENTS.md
5. **After creation:**
- Display the issue URL

View File

@@ -176,7 +176,7 @@ This command takes a work document (plan, specification, or todo file) and execu
- The plan should reference similar code - read those files first
- Match naming conventions exactly
- Reuse existing components where possible
- Follow project coding standards (see CLAUDE.md)
- Follow project coding standards (see AGENTS.md; use CLAUDE.md only if the repo still keeps a compatibility shim)
- When in doubt, grep for similar implementations
4. **Test Continuously**
@@ -220,7 +220,7 @@ This command takes a work document (plan, specification, or todo file) and execu
# Run full test suite (use project's test command)
# Examples: bin/rails test, npm test, pytest, go test, etc.
# Run linting (per CLAUDE.md)
# Run linting (per AGENTS.md)
# Use linting-agent before pushing to origin
```

View File

@@ -93,7 +93,7 @@ argument-hint: "[what arguments the command accepts]"
## Tips for Effective Commands
- **Use $ARGUMENTS** placeholder for dynamic inputs
- **Reference CLAUDE.md** patterns and conventions
- **Reference AGENTS.md** patterns and conventions
- **Include verification steps** - tests, linting, visual checks
- **Be explicit about constraints** - don't modify X, use pattern Y
- **Use XML tags** for structured prompts: `<task>`, `<requirements>`, `<constraints>`
@@ -114,7 +114,7 @@ Implement #$ARGUMENTS following these steps:
3. Implement
- Follow existing code patterns (reference specific files)
- Write tests first if doing TDD
- Ensure code follows CLAUDE.md conventions
- Ensure code follows AGENTS.md conventions
4. Verify
- Run tests: `bin/rails test`

View File

@@ -131,9 +131,10 @@ Determine the dev server port using this priority order:
**Priority 1: Explicit argument**
If the user passed a port number (e.g., `/test-browser 5000` or `/test-browser --port 5000`), use that port directly.
**Priority 2: CLAUDE.md / project instructions**
**Priority 2: AGENTS.md / project instructions**
```bash
# Check CLAUDE.md for port references
# Check AGENTS.md first for port references, then CLAUDE.md as compatibility fallback
grep -Eio '(port\s*[:=]\s*|localhost:)([0-9]{4,5})' AGENTS.md 2>/dev/null | grep -Eo '[0-9]{4,5}' | head -1
grep -Eio '(port\s*[:=]\s*|localhost:)([0-9]{4,5})' CLAUDE.md 2>/dev/null | grep -Eo '[0-9]{4,5}' | head -1
```
@@ -158,7 +159,10 @@ Store the result in a `PORT` variable for use in all subsequent steps.
# Combined detection (run this)
PORT="${EXPLICIT_PORT:-}"
if [ -z "$PORT" ]; then
PORT=$(grep -Eio '(port\s*[:=]\s*|localhost:)([0-9]{4,5})' CLAUDE.md 2>/dev/null | grep -Eo '[0-9]{4,5}' | head -1)
PORT=$(grep -Eio '(port\s*[:=]\s*|localhost:)([0-9]{4,5})' AGENTS.md 2>/dev/null | grep -Eo '[0-9]{4,5}' | head -1)
if [ -z "$PORT" ]; then
PORT=$(grep -Eio '(port\s*[:=]\s*|localhost:)([0-9]{4,5})' CLAUDE.md 2>/dev/null | grep -Eo '[0-9]{4,5}' | head -1)
fi
fi
if [ -z "$PORT" ]; then
PORT=$(grep -Eo '\-\-port[= ]+[0-9]{4,5}' package.json 2>/dev/null | grep -Eo '[0-9]{4,5}' | head -1)

View File

@@ -0,0 +1,92 @@
#!/usr/bin/env bun
import { buildReleasePreview } from "../../src/release/components"
import type { BumpOverride, ReleaseComponent } from "../../src/release/types"
function parseArgs(argv: string[]): {
title: string
files: string[]
overrides: Partial<Record<ReleaseComponent, BumpOverride>>
json: boolean
} {
let title = ""
const files: string[] = []
const overrides: Partial<Record<ReleaseComponent, BumpOverride>> = {}
let json = false
for (let index = 0; index < argv.length; index += 1) {
const arg = argv[index]
if (arg === "--title") {
title = argv[index + 1] ?? ""
index += 1
continue
}
if (arg === "--file") {
const file = argv[index + 1]
if (file) files.push(file)
index += 1
continue
}
if (arg === "--override") {
const raw = argv[index + 1] ?? ""
const [component, value] = raw.split("=")
if (component && value) {
overrides[component as ReleaseComponent] = value as BumpOverride
}
index += 1
continue
}
if (arg === "--json") {
json = true
}
}
return { title, files, overrides, json }
}
function formatPreview(preview: Awaited<ReturnType<typeof buildReleasePreview>>): string {
const lines: string[] = []
lines.push(`Release intent: ${preview.intent.raw || "(missing title)"}`)
if (preview.intent.type) {
lines.push(
`Parsed as: type=${preview.intent.type}${preview.intent.scope ? `, scope=${preview.intent.scope}` : ""}${preview.intent.breaking ? ", breaking=true" : ""}`,
)
}
if (preview.warnings.length > 0) {
lines.push("", "Warnings:")
for (const warning of preview.warnings) {
lines.push(`- ${warning}`)
}
}
if (preview.components.length === 0) {
lines.push("", "No releasable components detected.")
return lines.join("\n")
}
lines.push("", "Components:")
for (const component of preview.components) {
lines.push(`- ${component.component}`)
lines.push(` current: ${component.currentVersion}`)
lines.push(` inferred bump: ${component.inferredBump ?? "none"}`)
lines.push(` override: ${component.override}`)
lines.push(` effective bump: ${component.effectiveBump ?? "none"}`)
lines.push(` next: ${component.nextVersion ?? "unchanged"}`)
lines.push(` files: ${component.files.join(", ")}`)
}
return lines.join("\n")
}
const args = parseArgs(process.argv.slice(2))
const preview = await buildReleasePreview({
title: args.title,
files: args.files,
overrides: args.overrides,
})
if (args.json) {
console.log(JSON.stringify(preview, null, 2))
} else {
console.log(formatPreview(preview))
}

View File

@@ -0,0 +1,33 @@
#!/usr/bin/env bun
import { updateRootChangelog } from "../../src/release/metadata"
import type { ReleaseComponent } from "../../src/release/types"
type EntryInput = {
component: ReleaseComponent
version: string
date: string
sections: Record<string, string[]>
}
function parseEntries(argv: string[]): EntryInput[] {
const jsonIndex = argv.findIndex((arg) => arg === "--entries")
if (jsonIndex === -1) return []
const raw = argv[jsonIndex + 1]
if (!raw) return []
return JSON.parse(raw) as EntryInput[]
}
const write = process.argv.includes("--write")
const entries = parseEntries(process.argv.slice(2))
if (entries.length === 0) {
console.error("No changelog entries provided. Pass --entries '<json>'.")
process.exit(1)
}
const result = await updateRootChangelog({
entries,
write,
})
console.log(`${result.changed ? "update" : "keep"} ${result.path}`)

View File

@@ -0,0 +1,24 @@
#!/usr/bin/env bun
import { syncReleaseMetadata } from "../../src/release/metadata"
const write = process.argv.includes("--write")
const versionArgs = process.argv
.slice(2)
.filter((arg) => arg.startsWith("--version:"))
.map((arg) => arg.replace("--version:", ""))
const componentVersions = Object.fromEntries(
versionArgs.map((entry) => {
const [component, version] = entry.split("=")
return [component, version]
}),
)
const result = await syncReleaseMetadata({
componentVersions,
write,
})
for (const update of result.updates) {
console.log(`${update.changed ? "update" : "keep"} ${update.path}`)
}

View File

@@ -0,0 +1,16 @@
#!/usr/bin/env bun
import { syncReleaseMetadata } from "../../src/release/metadata"
const result = await syncReleaseMetadata({ write: false })
const changed = result.updates.filter((update) => update.changed)
if (changed.length === 0) {
console.log("Release metadata is in sync.")
process.exit(0)
}
console.error("Release metadata drift detected:")
for (const update of changed) {
console.error(`- ${update.path}`)
}
process.exit(1)

View File

@@ -56,7 +56,7 @@ export function convertClaudeToKiro(
// Convert MCP servers (stdio and remote)
const mcpServers = convertMcpServers(plugin.mcpServers)
// Build steering files from CLAUDE.md
// Build steering files from repo instruction files, preferring AGENTS.md.
const steeringFiles = buildSteeringFiles(plugin, agentNames)
// Warn about hooks
@@ -196,12 +196,12 @@ function convertMcpServers(
}
function buildSteeringFiles(plugin: ClaudePlugin, knownAgentNames: string[]): KiroSteeringFile[] {
const claudeMdPath = path.join(plugin.root, "CLAUDE.md")
if (!existsSync(claudeMdPath)) return []
const instructionPath = resolveInstructionPath(plugin.root)
if (!instructionPath) return []
let content: string
try {
content = readFileSync(claudeMdPath, "utf8")
content = readFileSync(instructionPath, "utf8")
} catch {
return []
}
@@ -212,6 +212,16 @@ function buildSteeringFiles(plugin: ClaudePlugin, knownAgentNames: string[]): Ki
return [{ name: "compound-engineering", content: transformed }]
}
function resolveInstructionPath(root: string): string | null {
const agentsPath = path.join(root, "AGENTS.md")
if (existsSync(agentsPath)) return agentsPath
const claudePath = path.join(root, "CLAUDE.md")
if (existsSync(claudePath)) return claudePath
return null
}
function normalizeName(value: string): string {
const trimmed = value.trim()
if (!trimmed) return "item"

229
src/release/components.ts Normal file
View File

@@ -0,0 +1,229 @@
import { readJson } from "../utils/files"
import type {
BumpLevel,
BumpOverride,
ComponentDecision,
ParsedReleaseIntent,
ReleaseComponent,
ReleasePreview,
} from "./types"
const RELEASE_COMPONENTS: ReleaseComponent[] = [
"cli",
"compound-engineering",
"coding-tutor",
"marketplace",
]
const FILE_COMPONENT_MAP: Array<{ component: ReleaseComponent; prefixes: string[] }> = [
{
component: "cli",
prefixes: ["src/", "package.json", "bun.lock", "tests/cli.test.ts"],
},
{
component: "compound-engineering",
prefixes: ["plugins/compound-engineering/"],
},
{
component: "coding-tutor",
prefixes: ["plugins/coding-tutor/"],
},
{
component: "marketplace",
prefixes: [".claude-plugin/marketplace.json", ".cursor-plugin/marketplace.json"],
},
]
const SCOPES_TO_COMPONENTS: Record<string, ReleaseComponent> = {
cli: "cli",
compound: "compound-engineering",
"compound-engineering": "compound-engineering",
"coding-tutor": "coding-tutor",
marketplace: "marketplace",
}
const NON_RELEASABLE_TYPES = new Set(["docs", "chore", "test", "ci", "build", "style"])
const PATCH_TYPES = new Set(["fix", "perf", "refactor", "revert"])
type VersionSources = Record<ReleaseComponent, string>
type RootPackageJson = {
version: string
}
type PluginManifest = {
version: string
}
type MarketplaceManifest = {
metadata: {
version: string
}
}
export function parseReleaseIntent(rawTitle: string): ParsedReleaseIntent {
const trimmed = rawTitle.trim()
const match = /^(?<type>[a-z]+)(?:\((?<scope>[^)]+)\))?(?<bang>!)?:\s+(?<description>.+)$/.exec(trimmed)
if (!match?.groups) {
return {
raw: rawTitle,
type: null,
scope: null,
description: null,
breaking: false,
}
}
return {
raw: rawTitle,
type: match.groups.type ?? null,
scope: match.groups.scope ?? null,
description: match.groups.description ?? null,
breaking: match.groups.bang === "!",
}
}
export function inferBumpFromIntent(intent: ParsedReleaseIntent): BumpLevel | null {
if (intent.breaking) return "major"
if (!intent.type) return null
if (intent.type === "feat") return "minor"
if (PATCH_TYPES.has(intent.type)) return "patch"
if (NON_RELEASABLE_TYPES.has(intent.type)) return null
return null
}
export function detectComponentsFromFiles(files: string[]): Map<ReleaseComponent, string[]> {
const componentFiles = new Map<ReleaseComponent, string[]>()
for (const component of RELEASE_COMPONENTS) {
componentFiles.set(component, [])
}
for (const file of files) {
for (const mapping of FILE_COMPONENT_MAP) {
if (mapping.prefixes.some((prefix) => file === prefix || file.startsWith(prefix))) {
componentFiles.get(mapping.component)!.push(file)
}
}
}
for (const [component, matchedFiles] of componentFiles.entries()) {
if (component === "cli" && matchedFiles.length === 0) continue
if (component !== "cli" && matchedFiles.length === 0) continue
}
return componentFiles
}
export function resolveComponentWarnings(
intent: ParsedReleaseIntent,
detectedComponents: ReleaseComponent[],
): string[] {
const warnings: string[] = []
if (!intent.type) {
warnings.push("Title does not match the expected conventional format: <type>(optional-scope): description")
return warnings
}
if (intent.scope) {
const normalized = intent.scope.trim().toLowerCase()
const expected = SCOPES_TO_COMPONENTS[normalized]
if (expected && detectedComponents.length > 0 && !detectedComponents.includes(expected)) {
warnings.push(
`Optional scope "${intent.scope}" does not match the detected component set: ${detectedComponents.join(", ")}`,
)
}
}
if (detectedComponents.length === 0 && inferBumpFromIntent(intent) !== null) {
warnings.push("No releasable component files were detected for this change")
}
return warnings
}
export function applyOverride(
inferred: BumpLevel | null,
override: BumpOverride,
): BumpLevel | null {
if (override === "auto") return inferred
return override
}
export function bumpVersion(version: string, bump: BumpLevel | null): string | null {
if (!bump) return null
const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(version)
if (!match) {
throw new Error(`Unsupported version format: ${version}`)
}
const major = Number(match[1])
const minor = Number(match[2])
const patch = Number(match[3])
switch (bump) {
case "major":
return `${major + 1}.0.0`
case "minor":
return `${major}.${minor + 1}.0`
case "patch":
return `${major}.${minor}.${patch + 1}`
}
}
export async function loadCurrentVersions(cwd = process.cwd()): Promise<VersionSources> {
const root = await readJson<RootPackageJson>(`${cwd}/package.json`)
const ce = await readJson<PluginManifest>(`${cwd}/plugins/compound-engineering/.claude-plugin/plugin.json`)
const codingTutor = await readJson<PluginManifest>(`${cwd}/plugins/coding-tutor/.claude-plugin/plugin.json`)
const marketplace = await readJson<MarketplaceManifest>(`${cwd}/.claude-plugin/marketplace.json`)
return {
cli: root.version,
"compound-engineering": ce.version,
"coding-tutor": codingTutor.version,
marketplace: marketplace.metadata.version,
}
}
export async function buildReleasePreview(options: {
title: string
files: string[]
overrides?: Partial<Record<ReleaseComponent, BumpOverride>>
cwd?: string
}): Promise<ReleasePreview> {
const intent = parseReleaseIntent(options.title)
const inferredBump = inferBumpFromIntent(intent)
const componentFilesMap = detectComponentsFromFiles(options.files)
const currentVersions = await loadCurrentVersions(options.cwd)
const detectedComponents = RELEASE_COMPONENTS.filter(
(component) => (componentFilesMap.get(component) ?? []).length > 0,
)
const warnings = resolveComponentWarnings(intent, detectedComponents)
const components: ComponentDecision[] = detectedComponents.map((component) => {
const override = options.overrides?.[component] ?? "auto"
const effectiveBump = applyOverride(inferredBump, override)
const currentVersion = currentVersions[component]
return {
component,
files: componentFilesMap.get(component) ?? [],
currentVersion,
inferredBump,
effectiveBump,
override,
nextVersion: bumpVersion(currentVersion, effectiveBump),
}
})
return {
intent,
warnings,
components,
}
}

218
src/release/metadata.ts Normal file
View File

@@ -0,0 +1,218 @@
import { promises as fs } from "fs"
import path from "path"
import { readJson, readText, writeJson, writeText } from "../utils/files"
import type { ReleaseComponent } from "./types"
type ClaudePluginManifest = {
version: string
description?: string
mcpServers?: Record<string, unknown>
}
type CursorPluginManifest = {
version: string
description?: string
}
type MarketplaceManifest = {
metadata: {
version: string
description?: string
}
plugins: Array<{
name: string
version?: string
description?: string
}>
}
type SyncOptions = {
root?: string
componentVersions?: Partial<Record<ReleaseComponent, string>>
write?: boolean
}
type FileUpdate = {
path: string
changed: boolean
}
export type MetadataSyncResult = {
updates: FileUpdate[]
}
export async function countMarkdownFiles(root: string): Promise<number> {
const entries = await fs.readdir(root, { withFileTypes: true })
let total = 0
for (const entry of entries) {
const fullPath = path.join(root, entry.name)
if (entry.isDirectory()) {
total += await countMarkdownFiles(fullPath)
continue
}
if (entry.isFile() && entry.name.endsWith(".md")) {
total += 1
}
}
return total
}
export async function countSkillDirectories(root: string): Promise<number> {
const entries = await fs.readdir(root, { withFileTypes: true })
let total = 0
for (const entry of entries) {
if (!entry.isDirectory()) continue
const skillPath = path.join(root, entry.name, "SKILL.md")
try {
await fs.access(skillPath)
total += 1
} catch {
// Ignore non-skill directories.
}
}
return total
}
export async function countMcpServers(pluginRoot: string): Promise<number> {
const mcpPath = path.join(pluginRoot, ".mcp.json")
const manifest = await readJson<{ mcpServers?: Record<string, unknown> }>(mcpPath)
return Object.keys(manifest.mcpServers ?? {}).length
}
export async function buildCompoundEngineeringDescription(root: string): Promise<string> {
const pluginRoot = path.join(root, "plugins", "compound-engineering")
const agents = await countMarkdownFiles(path.join(pluginRoot, "agents"))
const skills = await countSkillDirectories(path.join(pluginRoot, "skills"))
const mcpServers = await countMcpServers(pluginRoot)
return `AI-powered development tools. ${agents} agents, ${skills} skills, ${mcpServers} MCP server${mcpServers === 1 ? "" : "s"} for code review, research, design, and workflow automation.`
}
export async function syncReleaseMetadata(options: SyncOptions = {}): Promise<MetadataSyncResult> {
const root = options.root ?? process.cwd()
const write = options.write ?? false
const versions = options.componentVersions ?? {}
const updates: FileUpdate[] = []
const compoundDescription = await buildCompoundEngineeringDescription(root)
const compoundClaudePath = path.join(root, "plugins", "compound-engineering", ".claude-plugin", "plugin.json")
const compoundCursorPath = path.join(root, "plugins", "compound-engineering", ".cursor-plugin", "plugin.json")
const codingTutorClaudePath = path.join(root, "plugins", "coding-tutor", ".claude-plugin", "plugin.json")
const codingTutorCursorPath = path.join(root, "plugins", "coding-tutor", ".cursor-plugin", "plugin.json")
const marketplaceClaudePath = path.join(root, ".claude-plugin", "marketplace.json")
const compoundClaude = await readJson<ClaudePluginManifest>(compoundClaudePath)
const compoundCursor = await readJson<CursorPluginManifest>(compoundCursorPath)
const codingTutorClaude = await readJson<ClaudePluginManifest>(codingTutorClaudePath)
const codingTutorCursor = await readJson<CursorPluginManifest>(codingTutorCursorPath)
const marketplaceClaude = await readJson<MarketplaceManifest>(marketplaceClaudePath)
let changed = false
if (versions["compound-engineering"] && compoundClaude.version !== versions["compound-engineering"]) {
compoundClaude.version = versions["compound-engineering"]
changed = true
}
if (compoundClaude.description !== compoundDescription) {
compoundClaude.description = compoundDescription
changed = true
}
updates.push({ path: compoundClaudePath, changed })
if (write && changed) await writeJson(compoundClaudePath, compoundClaude)
changed = false
if (versions["compound-engineering"] && compoundCursor.version !== versions["compound-engineering"]) {
compoundCursor.version = versions["compound-engineering"]
changed = true
}
if (compoundCursor.description !== compoundDescription) {
compoundCursor.description = compoundDescription
changed = true
}
updates.push({ path: compoundCursorPath, changed })
if (write && changed) await writeJson(compoundCursorPath, compoundCursor)
changed = false
if (versions["coding-tutor"] && codingTutorClaude.version !== versions["coding-tutor"]) {
codingTutorClaude.version = versions["coding-tutor"]
changed = true
}
updates.push({ path: codingTutorClaudePath, changed })
if (write && changed) await writeJson(codingTutorClaudePath, codingTutorClaude)
changed = false
if (versions["coding-tutor"] && codingTutorCursor.version !== versions["coding-tutor"]) {
codingTutorCursor.version = versions["coding-tutor"]
changed = true
}
updates.push({ path: codingTutorCursorPath, changed })
if (write && changed) await writeJson(codingTutorCursorPath, codingTutorCursor)
changed = false
if (versions.marketplace && marketplaceClaude.metadata.version !== versions.marketplace) {
marketplaceClaude.metadata.version = versions.marketplace
changed = true
}
for (const plugin of marketplaceClaude.plugins) {
if (plugin.name === "compound-engineering") {
if (versions["compound-engineering"] && plugin.version !== versions["compound-engineering"]) {
plugin.version = versions["compound-engineering"]
changed = true
}
if (plugin.description !== `AI-powered development tools that get smarter with every use. Make each unit of engineering work easier than the last. Includes ${await countMarkdownFiles(path.join(root, "plugins", "compound-engineering", "agents"))} specialized agents and ${await countSkillDirectories(path.join(root, "plugins", "compound-engineering", "skills"))} skills.`) {
plugin.description = `AI-powered development tools that get smarter with every use. Make each unit of engineering work easier than the last. Includes ${await countMarkdownFiles(path.join(root, "plugins", "compound-engineering", "agents"))} specialized agents and ${await countSkillDirectories(path.join(root, "plugins", "compound-engineering", "skills"))} skills.`
changed = true
}
}
if (plugin.name === "coding-tutor" && versions["coding-tutor"] && plugin.version !== versions["coding-tutor"]) {
plugin.version = versions["coding-tutor"]
changed = true
}
}
updates.push({ path: marketplaceClaudePath, changed })
if (write && changed) await writeJson(marketplaceClaudePath, marketplaceClaude)
return { updates }
}
export async function updateRootChangelog(options: {
root?: string
entries: Array<{ component: ReleaseComponent; version: string; date: string; sections: Record<string, string[]> }>
write?: boolean
}): Promise<{ path: string; changed: boolean; content: string }> {
const root = options.root ?? process.cwd()
const changelogPath = path.join(root, "CHANGELOG.md")
const existing = await readText(changelogPath)
const renderedEntries = options.entries
.map((entry) => renderChangelogEntry(entry.component, entry.version, entry.date, entry.sections))
.join("\n\n")
const next = `${existing.trimEnd()}\n\n${renderedEntries}\n`
const changed = next !== existing
if (options.write && changed) {
await writeText(changelogPath, next)
}
return { path: changelogPath, changed, content: next }
}
export function renderChangelogEntry(
component: ReleaseComponent,
version: string,
date: string,
sections: Record<string, string[]>,
): string {
const lines = [`## ${component} v${version} - ${date}`]
for (const [section, items] of Object.entries(sections)) {
if (items.length === 0) continue
lines.push("", `### ${section}`)
for (const item of items) {
lines.push(`- ${item}`)
}
}
return lines.join("\n")
}

43
src/release/types.ts Normal file
View File

@@ -0,0 +1,43 @@
export type ReleaseComponent = "cli" | "compound-engineering" | "coding-tutor" | "marketplace"
export type BumpLevel = "patch" | "minor" | "major"
export type BumpOverride = BumpLevel | "auto"
export type ConventionalReleaseType =
| "feat"
| "fix"
| "perf"
| "refactor"
| "docs"
| "chore"
| "test"
| "ci"
| "build"
| "revert"
| "style"
| string
export type ParsedReleaseIntent = {
raw: string
type: ConventionalReleaseType | null
scope: string | null
description: string | null
breaking: boolean
}
export type ComponentDecision = {
component: ReleaseComponent
files: string[]
currentVersion: string
inferredBump: BumpLevel | null
effectiveBump: BumpLevel | null
override: BumpOverride
nextVersion: string | null
}
export type ReleasePreview = {
intent: ParsedReleaseIntent
warnings: string[]
components: ComponentDecision[]
}

View File

@@ -1,3 +1,6 @@
import { mkdtempSync, rmSync, writeFileSync } from "fs"
import os from "os"
import path from "path"
import { describe, expect, test } from "bun:test"
import { convertClaudeToKiro, transformContentForKiro } from "../src/converters/claude-to-kiro"
import { parseFrontmatter } from "../src/utils/frontmatter"
@@ -274,7 +277,7 @@ describe("convertClaudeToKiro", () => {
expect(warnings.some((w) => w.includes("Kiro"))).toBe(true)
})
test("steering file not generated when CLAUDE.md missing", () => {
test("steering file not generated when repo instruction files are missing", () => {
const plugin: ClaudePlugin = {
...fixturePlugin,
root: "/tmp/nonexistent-plugin-dir",
@@ -287,6 +290,27 @@ describe("convertClaudeToKiro", () => {
expect(bundle.steeringFiles).toHaveLength(0)
})
test("steering file prefers AGENTS.md over CLAUDE.md", () => {
const root = mkdtempSync(path.join(os.tmpdir(), "kiro-steering-"))
writeFileSync(path.join(root, "AGENTS.md"), "# AGENTS\nUse AGENTS instructions.")
writeFileSync(path.join(root, "CLAUDE.md"), "# CLAUDE\nUse CLAUDE instructions.")
const plugin: ClaudePlugin = {
...fixturePlugin,
root,
agents: [],
commands: [],
skills: [],
}
const bundle = convertClaudeToKiro(plugin, defaultOptions)
rmSync(root, { recursive: true, force: true })
expect(bundle.steeringFiles).toHaveLength(1)
expect(bundle.steeringFiles[0].content).toContain("Use AGENTS instructions.")
expect(bundle.steeringFiles[0].content).not.toContain("Use CLAUDE instructions.")
})
test("name normalization handles various inputs", () => {
const plugin: ClaudePlugin = {
...fixturePlugin,

View File

@@ -0,0 +1,102 @@
import { describe, expect, test } from "bun:test"
import {
applyOverride,
bumpVersion,
detectComponentsFromFiles,
inferBumpFromIntent,
parseReleaseIntent,
resolveComponentWarnings,
} from "../src/release/components"
describe("release component detection", () => {
test("maps plugin-only changes to the matching plugin component", () => {
const components = detectComponentsFromFiles([
"plugins/compound-engineering/skills/ce-plan/SKILL.md",
])
expect(components.get("compound-engineering")).toEqual([
"plugins/compound-engineering/skills/ce-plan/SKILL.md",
])
expect(components.get("cli")).toEqual([])
expect(components.get("coding-tutor")).toEqual([])
expect(components.get("marketplace")).toEqual([])
})
test("maps cli and plugin changes independently", () => {
const components = detectComponentsFromFiles([
"src/commands/install.ts",
"plugins/coding-tutor/.claude-plugin/plugin.json",
])
expect(components.get("cli")).toEqual(["src/commands/install.ts"])
expect(components.get("coding-tutor")).toEqual([
"plugins/coding-tutor/.claude-plugin/plugin.json",
])
})
test("maps marketplace metadata without bumping plugin components", () => {
const components = detectComponentsFromFiles([".claude-plugin/marketplace.json"])
expect(components.get("marketplace")).toEqual([".claude-plugin/marketplace.json"])
expect(components.get("compound-engineering")).toEqual([])
expect(components.get("coding-tutor")).toEqual([])
})
})
describe("release intent parsing", () => {
test("parses conventional titles with optional scope and breaking marker", () => {
const parsed = parseReleaseIntent("feat(coding-tutor)!: add tutor reset flow")
expect(parsed.type).toBe("feat")
expect(parsed.scope).toBe("coding-tutor")
expect(parsed.breaking).toBe(true)
expect(parsed.description).toBe("add tutor reset flow")
})
test("supports conventional titles without scope", () => {
const parsed = parseReleaseIntent("fix: adjust ce:plan-beta wording")
expect(parsed.type).toBe("fix")
expect(parsed.scope).toBeNull()
expect(parsed.breaking).toBe(false)
})
test("infers bump levels from parsed intent", () => {
expect(inferBumpFromIntent(parseReleaseIntent("feat: add release preview"))).toBe("minor")
expect(inferBumpFromIntent(parseReleaseIntent("fix: correct preview output"))).toBe("patch")
expect(inferBumpFromIntent(parseReleaseIntent("docs: update requirements"))).toBeNull()
expect(inferBumpFromIntent(parseReleaseIntent("refactor!: break compatibility"))).toBe("major")
})
})
describe("override handling", () => {
test("keeps inferred bump when override is auto", () => {
expect(applyOverride("patch", "auto")).toBe("patch")
})
test("promotes inferred bump when override is explicit", () => {
expect(applyOverride("patch", "minor")).toBe("minor")
expect(applyOverride(null, "major")).toBe("major")
})
test("increments semver versions", () => {
expect(bumpVersion("2.42.0", "patch")).toBe("2.42.1")
expect(bumpVersion("2.42.0", "minor")).toBe("2.43.0")
expect(bumpVersion("2.42.0", "major")).toBe("3.0.0")
})
})
describe("scope mismatch warnings", () => {
test("does not require scope when omitted", () => {
const warnings = resolveComponentWarnings(
parseReleaseIntent("fix: update ce plan copy"),
["compound-engineering"],
)
expect(warnings).toEqual([])
})
test("warns when explicit scope contradicts detected files", () => {
const warnings = resolveComponentWarnings(
parseReleaseIntent("fix(cli): update coding tutor text"),
["coding-tutor"],
)
expect(warnings[0]).toContain('Optional scope "cli" does not match')
})
})

View File

@@ -0,0 +1,23 @@
import { describe, expect, test } from "bun:test"
import { buildCompoundEngineeringDescription, renderChangelogEntry } from "../src/release/metadata"
describe("release metadata", () => {
test("builds the current compound-engineering manifest description from repo counts", async () => {
const description = await buildCompoundEngineeringDescription(process.cwd())
expect(description).toBe(
"AI-powered development tools. 29 agents, 44 skills, 1 MCP server for code review, research, design, and workflow automation.",
)
})
test("renders root changelog entries with component-version headings", () => {
const entry = renderChangelogEntry("compound-engineering", "2.43.0", "2026-04-10", {
Features: ["Add release preview"],
Fixes: ["Correct changelog rendering"],
})
expect(entry).toContain("## compound-engineering v2.43.0 - 2026-04-10")
expect(entry).toContain("### Features")
expect(entry).toContain("- Add release preview")
expect(entry).toContain("### Fixes")
})
})

View File

@@ -0,0 +1,41 @@
import { describe, expect, test } from "bun:test"
import { buildReleasePreview } from "../src/release/components"
describe("release preview", () => {
test("uses changed files to determine affected components and next versions", async () => {
const preview = await buildReleasePreview({
title: "fix: adjust ce:plan-beta wording",
files: ["plugins/compound-engineering/skills/ce-plan-beta/SKILL.md"],
})
expect(preview.components).toHaveLength(1)
expect(preview.components[0].component).toBe("compound-engineering")
expect(preview.components[0].inferredBump).toBe("patch")
expect(preview.components[0].nextVersion).toBe("2.42.1")
})
test("supports per-component overrides without affecting unrelated components", async () => {
const preview = await buildReleasePreview({
title: "fix: update coding tutor prompts",
files: ["plugins/coding-tutor/README.md"],
overrides: {
"coding-tutor": "minor",
},
})
expect(preview.components).toHaveLength(1)
expect(preview.components[0].component).toBe("coding-tutor")
expect(preview.components[0].inferredBump).toBe("patch")
expect(preview.components[0].effectiveBump).toBe("minor")
expect(preview.components[0].nextVersion).toBe("1.3.0")
})
test("docs-only changes remain non-releasable by default", async () => {
const preview = await buildReleasePreview({
title: "docs: update release planning notes",
files: ["docs/plans/2026-03-17-001-feat-release-automation-migration-beta-plan.md"],
})
expect(preview.components).toHaveLength(0)
})
})