diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5b057b0..b503eff 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,7 +29,7 @@ jobs: - name: Create Version Pull Request uses: changesets/action@v1 with: - version: npx changeset version + version: npm run version publish: npx changeset tag commit: "chore: version skills" title: "chore: version skills" diff --git a/CLAUDE.md b/CLAUDE.md index 2dfb777..8b87834 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -8,7 +8,7 @@ Skills are organized into bucket folders under `skills/`: Every skill in `engineering/` or `productivity/` (the **promoted** buckets) must have a reference in the top-level `README.md` and an entry in `.claude-plugin/plugin.json`'s `skills` array (the Claude Code plugin ships exactly the promoted set). Skills in `misc/`, `in-progress/`, and `deprecated/` must not appear in either. -Install commands are copied verbatim from [.agents/install-block.md](./.agents/install-block.md). `.claude-plugin/marketplace.json` makes the repo its own single-plugin marketplace — a fallback the install block explains, not the documented route. When bumping the release version, keep `.claude-plugin/plugin.json`'s `version` in sync with `package.json`'s — Claude uses the plugin `version` to decide when installed users see an update. Run `claude plugin validate . --strict` after touching either manifest. Why a Claude plugin but not (yet) a Codex one lives in [.agents/adr/0002-ship-as-a-claude-code-plugin.md](./.agents/adr/0002-ship-as-a-claude-code-plugin.md). +Install commands are copied verbatim from [.agents/install-block.md](./.agents/install-block.md). `.claude-plugin/marketplace.json` makes the repo its own single-plugin marketplace — a fallback the install block explains, not the documented route. Run `claude plugin validate . --strict` after touching either manifest. Why a Claude plugin but not (yet) a Codex one lives in [.agents/adr/0002-ship-as-a-claude-code-plugin.md](./.agents/adr/0002-ship-as-a-claude-code-plugin.md). Each skill entry in the top-level `README.md` must link the skill name to its `SKILL.md`. diff --git a/package.json b/package.json index 6e870b8..c03fe7a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mattpocock-skills", - "version": "1.2.0", + "version": "1.2.1", "private": true, "description": "Matt Pocock's agent skills for real engineering", "repository": { @@ -10,7 +10,8 @@ "license": "MIT", "scripts": { "changeset": "changeset", - "version": "changeset version" + "version": "changeset version && node scripts/sync-plugin-version.mjs", + "check-plugin-version": "node scripts/sync-plugin-version.mjs --check" }, "devDependencies": { "@changesets/changelog-github": "^0.7.0", diff --git a/scripts/sync-plugin-version.mjs b/scripts/sync-plugin-version.mjs new file mode 100644 index 0000000..c9e9907 --- /dev/null +++ b/scripts/sync-plugin-version.mjs @@ -0,0 +1,41 @@ +#!/usr/bin/env node +// Copies package.json's version into .claude-plugin/plugin.json. +// Runs as part of `npm run version`, immediately after `changeset version`. +// With --check it changes nothing and exits 1 if the two versions differ. + +import { readFileSync, writeFileSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; + +const repo = join(dirname(fileURLToPath(import.meta.url)), ".."); +const pluginPath = join(repo, ".claude-plugin", "plugin.json"); + +const { version } = JSON.parse(readFileSync(join(repo, "package.json"), "utf8")); +const source = readFileSync(pluginPath, "utf8"); +const plugin = JSON.parse(source); + +if (plugin.version === version) { + console.log(`plugin.json version is ${version} — already in sync`); + process.exit(0); +} + +if (process.argv.includes("--check")) { + console.error( + `plugin.json version is ${plugin.version}, package.json is ${version}. Run \`node scripts/sync-plugin-version.mjs\`.`, + ); + process.exit(1); +} + +// Rewrite only the version line, to keep the key order and the formatting. +const updated = source.replace( + /("version"\s*:\s*")[^"]*(")/, + `$1${version}$2`, +); + +if (JSON.parse(updated).version !== version) { + console.error(`Could not find a version field to replace in ${pluginPath}.`); + process.exit(1); +} + +writeFileSync(pluginPath, updated); +console.log(`plugin.json version ${plugin.version} -> ${version}`);