Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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'
Expand Down
28 changes: 18 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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/<dir>/.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/<dir>/.claude-plugin/plugin.json`,
the plugin's entry in `.claude-plugin/marketplace.json`, and — where present —
`plugins/<dir>/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
Expand Down
22 changes: 22 additions & 0 deletions plugins/audits/plugin.json
Original file line number Diff line number Diff line change
@@ -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"
]
}
21 changes: 21 additions & 0 deletions plugins/auto-dev/plugin.json
Original file line number Diff line number Diff line change
@@ -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"
]
}
21 changes: 21 additions & 0 deletions plugins/core/plugin.json
Original file line number Diff line number Diff line change
@@ -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"
]
}
21 changes: 21 additions & 0 deletions plugins/deps-flow/plugin.json
Original file line number Diff line number Diff line change
@@ -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"
]
}
20 changes: 20 additions & 0 deletions plugins/journal/plugin.json
Original file line number Diff line number Diff line change
@@ -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"
]
}
21 changes: 21 additions & 0 deletions plugins/repo-ops/plugin.json
Original file line number Diff line number Diff line change
@@ -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"
]
}
20 changes: 20 additions & 0 deletions plugins/research/plugin.json
Original file line number Diff line number Diff line change
@@ -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"
]
}
23 changes: 20 additions & 3 deletions scripts/bump-version.py
Original file line number Diff line number Diff line change
Expand Up @@ -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/<dir>/.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/<dir>/.claude-plugin/plugin.json` (Claude Code's manifest), the plugin's
entry in `.claude-plugin/marketplace.json`, and — where present — `plugins/<dir>/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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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)
Comment thread
greptile-apps[bot] marked this conversation as resolved.
marketplace_text = replace_marketplace_version(
marketplace_text, record["name"], current, new
)
Expand Down
Loading