refactor: flatten skills/ and adopt Agent Plugins 1.0

The Agent Plugins standard reads only the immediate children of
skills/ and cannot be pointed elsewhere, so a bucketed tree is
invisible to every conformant client.

Rather than generate a flat copy of the repo into a package
directory — a builder, a validator, an allowlist, a drift CI job,
and every skill committed twice — remove the buckets.

- skills/ is flat and holds exactly the promoted 25
- in-progress/ -> drafts/, misc/ -> extras/, deprecated/ deleted
- new root plugin.json (Agent Plugins 1.0)
- .claude-plugin/plugin.json loses its 25-entry skills array
- docs/ keeps its category folders; it is now the only place a
  skill's category lives

Reasoning in .agents/adr/0003.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matt Pocock
2026-08-07 16:50:39 +01:00
co-authored by Claude Opus 5
parent 84fdeffd12
commit a16a2674bc
113 changed files with 176 additions and 165 deletions
+3 -2
View File
@@ -5,7 +5,8 @@ set -euo pipefail
# It is not a supported installer. Modifications to it — or requests for
# modifications — will not be approved.
#
# Links all skills in the repository into the local skill directories used by
# Links every skill in the repository — promoted (`skills/`) as well as
# unpromoted (`drafts/`, `extras/`) — into the local skill directories used by
# each agent harness:
# - ~/.claude/skills — Claude Code
# - ~/.agents/skills — Codex and other Agent Skills-compatible harnesses
@@ -22,7 +23,7 @@ while IFS= read -r -d '' skill_md; do
src="$(dirname "$skill_md")"
names+=("$(basename "$src")")
srcs+=("$src")
done < <(find "$REPO/skills" -name SKILL.md -not -path '*/node_modules/*' -not -path '*/deprecated/*' -print0)
done < <(find "$REPO/skills" "$REPO/drafts" "$REPO/extras" -maxdepth 2 -name SKILL.md -not -path '*/node_modules/*' -print0)
for DEST in "${DESTS[@]}"; do
# If $DEST is a symlink that resolves into this repo, we'd end up writing the
+41 -27
View File
@@ -1,41 +1,55 @@
#!/usr/bin/env node
// Copies package.json's version into .claude-plugin/plugin.json.
// Copies package.json's version into every plugin manifest:
// plugin.json — the Agent Plugins 1.0 manifest
// .claude-plugin/plugin.json — the Claude Code manifest
// Runs as part of `npm run version`, immediately after `changeset version`.
// With --check it changes nothing and exits 1 if the two versions differ.
// With --check it changes nothing and exits 1 if any version differs.
import { readFileSync, writeFileSync } from "node:fs";
import { dirname, join } from "node:path";
import { dirname, join, relative } 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 manifests = [
join(repo, "plugin.json"),
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);
const check = process.argv.includes("--check");
let failed = false;
if (plugin.version === version) {
console.log(`plugin.json version is ${version} — already in sync`);
process.exit(0);
}
for (const path of manifests) {
const name = relative(repo, path);
const source = readFileSync(path, "utf8");
const manifest = JSON.parse(source);
if (process.argv.includes("--check")) {
console.error(
`plugin.json version is ${plugin.version}, package.json is ${version}. Run \`node scripts/sync-plugin-version.mjs\`.`,
if (manifest.version === version) {
console.log(`${name} version is ${version} — already in sync`);
continue;
}
if (check) {
console.error(
`${name} version is ${manifest.version}, package.json is ${version}. Run \`node scripts/sync-plugin-version.mjs\`.`,
);
failed = true;
continue;
}
// Rewrite only the version line, to keep the key order and the formatting.
const updated = source.replace(
/("version"\s*:\s*")[^"]*(")/,
`$1${version}$2`,
);
process.exit(1);
if (JSON.parse(updated).version !== version) {
console.error(`Could not find a version field to replace in ${path}.`);
process.exit(1);
}
writeFileSync(path, updated);
console.log(`${name} version ${manifest.version} -> ${version}`);
}
// 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}`);
process.exit(failed ? 1 : 0);