From b123eba639a730ff668d22a92b8be5d940f55dcb Mon Sep 17 00:00:00 2001 From: Allen Hutchison Date: Mon, 24 Aug 2026 12:12:25 -0700 Subject: [PATCH 1/2] feat: conform to Agent Plugins v1.0.0 alongside Claude Code's format Each plugin's existing skills/*/SKILL.md layout already matches what the open spec (agent-plugins.org) expects, so the only gap was a manifest at the plugin root instead of .claude-plugin/. Add that plugin.json to every plugin, keep it in lockstep with the Claude Code manifest and marketplace entry via bump-version.py, and add a CI check that enforces the spec's closed field schema. --- .github/workflows/validate.yml | 29 +++++++++++++++++++++++++++++ README.md | 28 ++++++++++++++++++---------- plugins/audits/plugin.json | 22 ++++++++++++++++++++++ plugins/auto-dev/plugin.json | 21 +++++++++++++++++++++ plugins/core/plugin.json | 21 +++++++++++++++++++++ plugins/deps-flow/plugin.json | 21 +++++++++++++++++++++ plugins/journal/plugin.json | 20 ++++++++++++++++++++ plugins/repo-ops/plugin.json | 21 +++++++++++++++++++++ plugins/research/plugin.json | 20 ++++++++++++++++++++ scripts/bump-version.py | 23 ++++++++++++++++++++--- 10 files changed, 213 insertions(+), 13 deletions(-) create mode 100644 plugins/audits/plugin.json create mode 100644 plugins/auto-dev/plugin.json create mode 100644 plugins/core/plugin.json create mode 100644 plugins/deps-flow/plugin.json create mode 100644 plugins/journal/plugin.json create mode 100644 plugins/repo-ops/plugin.json create mode 100644 plugins/research/plugin.json diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 3da7319..295223b 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -37,6 +37,35 @@ jobs: sys.exit(1 if fail else 0) EOF + - name: Agent Plugins v1.0.0 manifests match and stay within the closed schema + run: | + python3 - <<'EOF' + import json, sys, pathlib + ALLOWED = { + '$schema', 'name', 'version', 'description', 'author', + 'homepage', 'repository', 'license', 'keywords', 'extensions', + } + SCHEMA_URL = 'https://agent-plugins.org/schemas/1.0.0/plugin.schema.json' + mp = json.loads(pathlib.Path('.claude-plugin/marketplace.json').read_text()) + fail = False + for entry in mp['plugins']: + src = entry['source'].lstrip('./') + manifest = pathlib.Path(src) / 'plugin.json' + if not manifest.exists(): + continue # optional: not every plugin has to opt into the open spec + pj = json.loads(manifest.read_text()) + extra = set(pj) - ALLOWED + if extra: + print(f"FAIL {manifest}: fields outside the closed schema: {sorted(extra)}"); fail = True + if pj.get('$schema') != SCHEMA_URL: + print(f"FAIL {manifest}: \\$schema is {pj.get('$schema')!r}, expected {SCHEMA_URL!r}"); fail = True + if pj.get('name') != entry['name']: + print(f"FAIL {manifest}: name {pj.get('name')!r} != marketplace {entry['name']!r}"); fail = True + if pj.get('version') != entry.get('version'): + print(f"FAIL {manifest}: version {pj.get('version')!r} != marketplace {entry.get('version')!r}"); fail = True + sys.exit(1 if fail else 0) + EOF + - name: Every skill has frontmatter with name and description run: | python3 - <<'EOF' diff --git a/README.md b/README.md index 97a8972..de9ab60 100644 --- a/README.md +++ b/README.md @@ -95,15 +95,22 @@ maintainerd/ scripts/sync-references.sh scripts/bump-version.py plugins/ - core/ .claude-plugin/plugin.json skills/{bootstrap,doctor}/ references/{config-schema,model-tiers}.md - repo-ops/ .claude-plugin/plugin.json skills/{create-pr,address-review,release,daily-changelog,daily-update}/ - audits/ .claude-plugin/plugin.json skills/{audit-architecture,audit-tests,audit-security,audit-deps,audit-design-docs,audit-product-docs}/ references/pattern-promotion.md - research/ .claude-plugin/plugin.json skills/{research-radar}/ - journal/ .claude-plugin/plugin.json skills/{worklog}/ - auto-dev/ .claude-plugin/plugin.json skills/{create-issue,auto-dev,review-queue}/ - deps-flow/ .claude-plugin/plugin.json skills/{dependabot}/ + core/ .claude-plugin/plugin.json plugin.json skills/{bootstrap,doctor}/ references/{config-schema,model-tiers}.md + repo-ops/ .claude-plugin/plugin.json plugin.json skills/{create-pr,address-review,release,daily-changelog,daily-update}/ + audits/ .claude-plugin/plugin.json plugin.json skills/{audit-architecture,audit-tests,audit-security,audit-deps,audit-design-docs,audit-product-docs}/ references/pattern-promotion.md + research/ .claude-plugin/plugin.json plugin.json skills/{research-radar}/ + journal/ .claude-plugin/plugin.json plugin.json skills/{worklog}/ + auto-dev/ .claude-plugin/plugin.json plugin.json skills/{create-issue,auto-dev,review-queue}/ + deps-flow/ .claude-plugin/plugin.json plugin.json skills/{dependabot}/ ``` +Each plugin ships two manifests: `.claude-plugin/plugin.json` is Claude Code's own format; +the root-level `plugin.json` conforms to the [Agent Plugins v1.0.0](https://agent-plugins.org/specification) +open spec, for clients that speak that instead. Both describe the same plugin — same `name`, +same `version` — and both read the same `skills/` directory, since that layout already matches +what the open spec expects. `bump-version.py` and `validate.yml` keep them in lockstep; edit the +`.claude-plugin` copy as the source of truth and let the script propagate the version. + ### Shared reference docs `config-schema.md` and `model-tiers.md` are authored once in `plugins/core/references/` and @@ -151,9 +158,10 @@ the push didn't touch don't move, so installs of those stay put. Two things to know when working in this repo: -- **The version lives in two files that must agree** — `plugins//.claude-plugin/plugin.json` - and the plugin's entry in `.claude-plugin/marketplace.json`. `validate.yml` fails the build if - they drift. Use the script rather than editing either by hand: +- **The version lives in every manifest that must agree** — `plugins//.claude-plugin/plugin.json`, + the plugin's entry in `.claude-plugin/marketplace.json`, and — where present — + `plugins//plugin.json`. `validate.yml` fails the build if any of them drift. Use the + script rather than editing any of them by hand: ```bash ./scripts/bump-version.py --level minor repo-ops diff --git a/plugins/audits/plugin.json b/plugins/audits/plugin.json new file mode 100644 index 0000000..2e3300d --- /dev/null +++ b/plugins/audits/plugin.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json", + "name": "audits", + "version": "0.2.5", + "description": "Scheduled findings that open PRs/issues, deliberately capped and conservative: audit-architecture (tech-debt sweep), audit-tests (test-suite health), audit-security (vulnerable deps, committed secrets incl. git history, permissive defaults — code patterns are left to Claude Code's built-in /security-review), audit-deps (dependency health: outdated, deprecated, unused, lockfile drift, licenses), audit-design-docs (validate design docs against code), and audit-product-docs (validate user-facing docs against code). Detection rules and invariants come from the repo's .claude/maintainerd.json and guidelines files.", + "author": { + "name": "Allen Hutchison", + "url": "https://github.com/allenhutchison" + }, + "homepage": "https://github.com/Vycari/maintainerd/tree/main/plugins/audits#readme", + "repository": "https://github.com/Vycari/maintainerd", + "license": "MIT", + "keywords": [ + "audit", + "tech-debt", + "tests", + "security", + "dependencies", + "documentation", + "maintainer" + ] +} diff --git a/plugins/auto-dev/plugin.json b/plugins/auto-dev/plugin.json new file mode 100644 index 0000000..0499c76 --- /dev/null +++ b/plugins/auto-dev/plugin.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json", + "name": "auto-dev", + "version": "0.2.7", + "description": "Autonomous issue-to-PR pipeline. create-issue is the front door — it turns a rough request into a well-formed, buildable issue by investigating the codebase. auto-dev runs one tick of triage -> plan -> build -> address-review against an auto:* GitHub label state machine and never merges; review-queue is the maintainer's console for approving plans, answering questions, and merging. Label names, comment markers, and branch prefixes are configurable via .claude/maintainerd.json so the pipeline drops into any repo.", + "author": { + "name": "Allen Hutchison", + "url": "https://github.com/allenhutchison" + }, + "homepage": "https://github.com/Vycari/maintainerd/tree/main/plugins/auto-dev#readme", + "repository": "https://github.com/Vycari/maintainerd", + "license": "MIT", + "keywords": [ + "automation", + "issues", + "issue-intake", + "pull-request", + "pipeline", + "maintainer" + ] +} diff --git a/plugins/core/plugin.json b/plugins/core/plugin.json new file mode 100644 index 0000000..968c963 --- /dev/null +++ b/plugins/core/plugin.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json", + "name": "maintainerd-core", + "version": "0.2.4", + "description": "Foundation for the maintainerd maintainer toolkit. Ships bootstrap (generates a repo's .claude/maintainerd.json config contract and starter guidelines) and doctor (validates that setup — config, paths, commands, labels, guidelines, rosters — and reports what's wrong), plus the canonical config schema reference that every other maintainerd plugin reads.", + "author": { + "name": "Allen Hutchison", + "url": "https://github.com/allenhutchison" + }, + "homepage": "https://github.com/Vycari/maintainerd/tree/main/plugins/core#readme", + "repository": "https://github.com/Vycari/maintainerd", + "license": "MIT", + "keywords": [ + "config", + "bootstrap", + "doctor", + "diagnostics", + "maintainer", + "scaffolding" + ] +} diff --git a/plugins/deps-flow/plugin.json b/plugins/deps-flow/plugin.json new file mode 100644 index 0000000..b3b7b96 --- /dev/null +++ b/plugins/deps-flow/plugin.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json", + "name": "deps-flow", + "version": "0.2.4", + "description": "Autonomous Dependabot queue management — the one maintainerd plugin that merges. The dependabot skill gates every bot-authored dependency PR on all checks green, no requested changes, and a configured semver policy (patch/minor by default, majors held for the human), merges one PR per non-overlapping file group per pass, waits for Dependabot to rebase the rest (nudging with @dependabot rebase only when a rebase is genuinely stuck), and repeats until the queue drains. It never fixes a broken update: it diagnoses the CI failure, files one issue with the evidence, labels the PR blocked, and moves on. Install only in repos where a bot is allowed to merge; every knob lives in .claude/maintainerd.json's depsFlow block.", + "author": { + "name": "Allen Hutchison", + "url": "https://github.com/allenhutchison" + }, + "homepage": "https://github.com/Vycari/maintainerd/tree/main/plugins/deps-flow#readme", + "repository": "https://github.com/Vycari/maintainerd", + "license": "MIT", + "keywords": [ + "dependabot", + "dependencies", + "merge", + "automation", + "pull-request", + "maintainer" + ] +} diff --git a/plugins/journal/plugin.json b/plugins/journal/plugin.json new file mode 100644 index 0000000..2131fff --- /dev/null +++ b/plugins/journal/plugin.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json", + "name": "journal", + "version": "0.2.5", + "description": "User-scoped narration and record-keeping that spans repos. worklog captures a day's shipped work — from merged PRs enriched by the live session — into the user's Obsidian vault as a session-summary note, a project-hub link, and a daily-note line. Unlike the repo-scoped plugins, its vault setting lives in a user-level ~/.claude/maintainerd.json, with an optional per-repo project pointer. A home for future journal skills (weekly-review, decision-log).", + "author": { + "name": "Allen Hutchison", + "url": "https://github.com/allenhutchison" + }, + "homepage": "https://github.com/Vycari/maintainerd/tree/main/plugins/journal#readme", + "repository": "https://github.com/Vycari/maintainerd", + "license": "MIT", + "keywords": [ + "journal", + "worklog", + "obsidian", + "notes", + "maintainer" + ] +} diff --git a/plugins/repo-ops/plugin.json b/plugins/repo-ops/plugin.json new file mode 100644 index 0000000..61dfa06 --- /dev/null +++ b/plugins/repo-ops/plugin.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json", + "name": "repo-ops", + "version": "0.2.4", + "description": "Baseline maintainer dev flow as config-driven skills: create-pr (opens PRs only after running the repo's format/lint/build/test pre-flight), address-review (drive the iterative response loop on bot + human PR feedback to approval — fix, push, reply to every inline thread and a PR-level summary), release (cut a versioned release: gather changes since the last tag, update notes, run the gate, bump, tag, publish), daily-changelog (one markdown file per day from merged PRs), and daily-update (runs the repo's per-day housekeeping skills and bundles their output into one PR). Reads .claude/maintainerd.json.", + "author": { + "name": "Allen Hutchison", + "url": "https://github.com/allenhutchison" + }, + "homepage": "https://github.com/Vycari/maintainerd/tree/main/plugins/repo-ops#readme", + "repository": "https://github.com/Vycari/maintainerd", + "license": "MIT", + "keywords": [ + "pull-request", + "review-feedback", + "release", + "changelog", + "automation", + "maintainer" + ] +} diff --git a/plugins/research/plugin.json b/plugins/research/plugin.json new file mode 100644 index 0000000..9a693ce --- /dev/null +++ b/plugins/research/plugin.json @@ -0,0 +1,20 @@ +{ + "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json", + "name": "research", + "version": "0.2.4", + "description": "Proactive research skills that surface relevant outside knowledge and open a PR or digest. research-radar periodically scans arXiv for papers relevant to the work in this repo and writes a dated digest. A home for future research tooling (deep research, GitHub/code search, prior-art scans). Themes and output paths come from .claude/maintainerd.json.", + "author": { + "name": "Allen Hutchison", + "url": "https://github.com/allenhutchison" + }, + "homepage": "https://github.com/Vycari/maintainerd/tree/main/plugins/research#readme", + "repository": "https://github.com/Vycari/maintainerd", + "license": "MIT", + "keywords": [ + "research", + "arxiv", + "digest", + "discovery", + "maintainer" + ] +} diff --git a/scripts/bump-version.py b/scripts/bump-version.py index 6662ab6..93218d5 100755 --- a/scripts/bump-version.py +++ b/scripts/bump-version.py @@ -10,9 +10,11 @@ install keeps serving the old content forever. Bumping is therefore not a release ceremony here, it is how the change reaches anyone. -The version lives in two places that CI (`.github/workflows/validate.yml`) requires to -agree: `plugins//.claude-plugin/plugin.json` and the plugin's entry in -`.claude-plugin/marketplace.json`. This script edits both or neither. +The version lives in the places that CI (`.github/workflows/validate.yml`) requires to +agree: `plugins//.claude-plugin/plugin.json` (Claude Code's manifest), the plugin's +entry in `.claude-plugin/marketplace.json`, and — where present — `plugins//plugin.json` +(the Agent Plugins v1.0.0 manifest, kept for clients that speak the open spec instead of +Claude Code's format). This script edits all of them or none. scripts/bump-version.py core repo-ops # bump these (patch) scripts/bump-version.py --level minor audits # bump one, minor @@ -101,6 +103,7 @@ def plugin_index(marketplace): "dir": os.path.basename(source), "version": entry.get("version"), "manifest": os.path.join(source, ".claude-plugin", "plugin.json"), + "manifest_open_spec": os.path.join(source, "plugin.json"), } index[record["name"]] = record index[record["dir"]] = record @@ -277,6 +280,18 @@ def main(): f"{record['version']!r} — fix the mismatch before bumping" ) + open_spec_path = os.path.join(ROOT, record["manifest_open_spec"]) + has_open_spec = os.path.exists(open_spec_path) + if has_open_spec: + with open(open_spec_path) as handle: + open_spec_current = json.load(handle).get("version") + if open_spec_current != current: + fail( + f"{record['name']}: {record['manifest_open_spec']} version " + f"{open_spec_current!r} != {record['manifest']} {current!r} — " + "fix the mismatch before bumping" + ) + # An author who deliberately bumped in the PR (a minor or major that this # script's default patch would flatten) already published a new version for this # range. Bumping again on merge would silently turn their 0.2.0 into 0.2.1. @@ -313,6 +328,8 @@ def main(): new = bump(current, args.level) edits[manifest_path] = replace_plugin_version(manifest_path, current, new) + if has_open_spec: + edits[open_spec_path] = replace_plugin_version(open_spec_path, current, new) marketplace_text = replace_marketplace_version( marketplace_text, record["name"], current, new ) From 1e30e5df3cd60157ce5abe69f10b8a2881c53469 Mon Sep 17 00:00:00 2001 From: Allen Hutchison Date: Mon, 24 Aug 2026 13:03:03 -0700 Subject: [PATCH 2/2] fix(ci): stage the open-spec plugin.json in the automated bump commit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit bump-version.py already edits plugins//plugin.json in lockstep with the Claude Code manifest, but version-bump.yml's commit step staged files by explicit name and didn't know about the new one — so the automated post-merge bump would leave it at the old version, failing validate.yml's new closed-schema check on main. --- .github/workflows/version-bump.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/version-bump.yml b/.github/workflows/version-bump.yml index b1c24e4..d63ecd7 100644 --- a/.github/workflows/version-bump.yml +++ b/.github/workflows/version-bump.yml @@ -104,8 +104,9 @@ jobs: # maintainerd-core--v0.1.1 -> "maintainerd-core 0.1.1", joined with commas. SUMMARY=$(printf '%s\n' "${TAGS[@]}" | sed 's/--v/ /' | paste -sd, - | sed 's/,/, /g') # Stage the manifests by name — the script touches nothing else, and `-A` would - # sweep in anything a future step happens to leave in the tree. - git add .claude-plugin/marketplace.json plugins/*/.claude-plugin/plugin.json + # sweep in anything a future step happens to leave in the tree. plugins/*/plugin.json + # is the Agent Plugins v1.0.0 manifest bump-version.py also edits where present. + git add .claude-plugin/marketplace.json plugins/*/.claude-plugin/plugin.json plugins/*/plugin.json # No staged change means every version here was bumped by hand in the PR. That # still wants a tag, so fall through to tagging rather than treating it as a