Skip to content

About

Config-driven maintainer skills for Claude Code — a plugin marketplace of audits, daily ops, doc validation, and an autonomous issue→PR pipeline that drop into any repo via a small config contract.

Resources

Stars

3 stars

Watchers

0 watching

Forks

Latest commit

 

History

239 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Maintainerd

Maintainerd is a Claude Code plugin marketplace of config-driven maintainer skills — the nightly audits, daily changelog, doc validation, PR flow, and autonomous issue→PR pipeline that a solo maintainer wants on every repo, extracted so they live in one place instead of being copy-pasted (and drifting) across projects.

Every skill reads a small per-repo config contract — .claude/maintainerd.json plus a few .claude/guidelines/*.md files — checked into the consuming repo. The same skill runs unchanged in a Python repo and a TypeScript repo; only the config differs. The bootstrap skill generates that contract for any repo.

Plugins

Plugin Skills Install when
maintainerd-core bootstrap, doctor, new-repo Always — bootstrap generates the config every other plugin needs; doctor validates it; new-repo brings a repo to a fleet's standard.
repo-ops create-pr, address-review, release, daily-changelog, daily-update (plus three PreToolUse Bash hooks, pr-template-guard, skip-label-race-guard and merge-guard, and an opt-in PostToolUse hook, review-reply-postcondition) You want the baseline PR + changelog dev flow.
audits audit-architecture, audit-tests, audit-security, audit-deps, audit-design-docs, audit-product-docs You want scheduled tech-debt / test / security / dependency / doc sweeps.
research research-radar You want proactive research surfaced — a periodic arXiv scan for papers relevant to this repo.
journal worklog You want a day's shipped work captured into your Obsidian vault (user-scoped — spans all your repos).
auto-dev create-issue, auto-dev, review-queue You want the autonomous issue→PR pipeline (from issue intake through build to review).
deps-flow dependabot You want the Dependabot queue drained unattended — and you're willing to let a skill merge.

A repo installs only the plugins it wants. auto-dev works standalone (with maintainerd-core for config); the audits and repo-ops compose but don't require each other.

deps-flow is the exception to the suite's "never auto-merges" rule. Every other skill stops at the merge gate; dependabot merges dependency PRs that pass a strict gate (all checks concluded green, no requested changes, bump level within the repo's policy). It requires an explicit depsFlow.enabled: true — an absent config block means off, never defaults. Run /dependabot dry-run first.

Setup in a new repo

  1. Add the marketplace (once per machine):

    claude plugin marketplace add Vycari/maintainerd
    # or, for local development:
    claude plugin marketplace add /path/to/your/checkout/maintainerd
  2. Install the plugins you want, starting with core:

    /plugin   # then install maintainerd-core, repo-ops, audits, research, auto-dev as desired
    
  3. Generate the config by running the bootstrap skill in the target repo:

    /bootstrap
    

    It inspects the repo (language, repo slug, default branch, source/test dirs, lint/test commands), confirms anything ambiguous with you, and writes:

    • .claude/maintainerd.json — the structured config (see plugins/core/references/config-schema.md).
    • .claude/guidelines/coding.md, testing.md, invariants.md — starter guideline files seeded from your CLAUDE.md/AGENTS.md, with TODOs for the repo-specific invariants the audits should enforce.
  4. Fill in invariants.md — this is the one file that needs real human judgment. It holds the load-bearing, repo-specific rules the audit-architecture checks (e.g. "secrets are SecretStr", "use plugin.logger, never console").

  5. Commit .claude/maintainerd.json and .claude/guidelines/ to the repo so the skills (and any scheduled cloud agents) pick them up.

How the config contract works

  • Structured scalars → JSON. Repo slug, default branch, language, source/test/doc paths, lint/format/build/test commands, label names, per-run caps, the daily-update roster, and the auto:* state-machine label names all live in .claude/maintainerd.json.
  • Free-form repo rules → markdown. Coding standards, test conventions, and load-bearing invariants live in .claude/guidelines/*.md, which the skills read at runtime. This keeps the JSON scannable and lets the prose diff cleanly.
  • Some of it is policy, not description. A few keys don't describe the repo, they set a house rule the skills enforce. createPr.requireIssueForDeferredWork (default off) is the clearest case: turn it on and create-pr refuses to open a PR whose body promises follow-up work without naming an issue, and address-review won't post a review reply that defers without one — the rule "a follow-up that lives only in a PR dies with the PR", mechanized. It's a lint over prose, with the limits that implies; the schema reference documents them, and an explicit <!-- no-deferred-work --> marker in the body bypasses it for a PR whose "later" is prose rather than a promise.

Most skills begin by reading .claude/maintainerd.json; if it's missing, the skill tells you to run /bootstrap. The canonical schema and the shared "read your repo config" preamble live in plugins/core/references/config-schema.md.

  • User-scoped exception. A few settings are the same across every repo you work in (the journal category's Obsidian vault). Those live in a user-level ~/.claude/maintainerd.json, read once regardless of repo. worklog reads the vault from there and an optional per-repo pointer from the repo config. See "User-level config" in the schema reference.

Workspaces: one config, many repos

A maintainer with several repos usually has an umbrella repo that holds them — the shared CLAUDE.md, the house rules, the checkouts. Give that repo's .claude/maintainerd.json a top-level workspace block and it becomes the one repo list:

"workspace": {
  "contractVersion": 1,
  "org": "my-org",
  "repos": [
    { "name": "app",     "repo": "my-org/app",     "language": "python-service", "role": "product" },
    { "name": "toolkit", "repo": "my-org/toolkit", "language": "none", "role": "tooling", "clone": false }
  ]
}

The block's presence is what makes a directory a workspace. clone: false marks a repo tracked but not checked out. workspace.repos is a versioned contract — tools that aren't maintainerd read it directly rather than re-deriving the list, so contractVersion tells them whether they still understand its shape.

Three skills then take a --workspace flag, which runs them once per cloned repo and prints one combined report — no new skill, and per-repo behavior unchanged:

Skill --workspace does
doctor Validates the block itself (unique names, resolvable slugs, language values known to the profile), then runs the full check in every cloned repo.
review-queue Gathers the auto-dev queue across every cloned repo into one inbox; items are repo#number.
daily-update Runs each repo's own daily roster in its own tree — one PR per repo.

The full schema, including the compatibility rules for the contract, is in plugins/core/references/config-schema.md, with a worked example at plugins/core/references/example-workspace.json.

Repository layout

maintainerd/
  .claude-plugin/marketplace.json
  scripts/sync-references.sh
  scripts/bump-version.py
  scripts/test-coverage.sh
  scripts/test-wait-tools.sh
  scripts/test-renumber-migration.sh
  plugins/
    core/      .claude-plugin/plugin.json  plugin.json  skills/{bootstrap,doctor,new-repo}/  references/{config-schema,model-tiers,profile-schema,gh-rest-fallbacks}.md  scripts/{coverage-adapt,coverage-check,profile-resolve,settings-diff}.sh
    repo-ops/  .claude-plugin/plugin.json  plugin.json  skills/{create-pr,address-review,release,daily-changelog,daily-update}/  hooks/{hooks.json,scripts/{pr-template-guard,skip-label-race-guard,merge-guard,review-reply-postcondition}.sh}  scripts/{wait-for-review,wait-for-checks,renumber-migration}.sh
    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 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, model-tiers.md and gh-rest-fallbacks.md are authored once in plugins/core/references/ and vendored into every plugin that links them. A skill can only reach files inside its own plugin: a relative link that climbs out resolves in this source tree but not in an installed marketplace layout, which interposes a version segment and uses the plugin name rather than the source directory name —

source:     plugins/audits/skills/audit-tests/SKILL.md
installed:  <cache>/maintainerd/audits/0.1.0/skills/audit-tests/SKILL.md

So a link like ../../../core/references/config-schema.md resolves from the source path and lands nowhere from the installed one — it misses on both the extra version segment and the directory name (maintainerd-core, not core). Intra-plugin links are the one form that resolves identically in both layouts and in the clone-and-read path scheduled cloud routines use, so every skill links its own plugin's copy.

Edit the canonical file in plugins/core/references/, then run:

./scripts/sync-references.sh

The copies carry a generated-file banner and CI fails if they drift.

Versioning

A plugin's version is the only signal Claude Code has that an installed copy is stale. The install cache is keyed by it —

<cache>/maintainerd/audits/0.2.0/skills/audit-tests/SKILL.md

— so a merge that rewrites a skill but leaves the version alone reaches nobody who already installed the plugin. They keep running the old content indefinitely.

So versions are not a release ceremony here; they are the delivery mechanism, and they are automatic. .github/workflows/version-bump.yml runs on every push to main, patch-bumps each plugin whose files the push touched, commits that back to main, and pushes a <plugin>--v<version> tag (the convention claude plugin tag uses). Plugins 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 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:

    ./scripts/bump-version.py --level minor repo-ops
  • A hand-written bump wins. For a change that deserves a minor or major, bump it in the PR; on merge the workflow sees the version already moved in that range, leaves it alone, and just tags it. The automatic patch is the default, not an override. A hand-written version has to be plain X.Y.Z and has to increase — the script refuses anything else rather than publish a --vNone tag or a downgrade that the version-keyed cache would read as a fresh release.

  • [skip bump] opts out. A merge or squash commit message containing that marker skips the workflow entirely, for a change under plugins/** that shouldn't reach anyone — a typo in a comment, say. It's also what the workflow stamps on its own commits so they don't re-trigger it. Use it sparingly: a skipped bump means the change ships to nobody until the next one.

Pulling updates into a local install

Claude Code refreshes the marketplace clone on its own schedule, which can leave a checkout well behind main. To force it:

claude plugin marketplace update maintainerd

Then update the plugins themselves — this is what re-reads the version and re-downloads:

claude plugin update maintainerd-core@maintainerd --scope project

Both halves of that command matter, and each fails in its own way:

  • Use the full <plugin>@<marketplace> id. A bare claude plugin update maintainerd-core fails with Plugin "maintainerd-core" not found, even though claude plugin list shows it installed.
  • Pass the scope it was installed with. update defaults to --scope user; these are usually installed per-repo, and the user-scoped lookup won't find a project-scoped install. claude plugin list reports the scope of each.

A restart is required for either to take effect. If a skill looks like it's running an old version, compare the version in claude plugin list against marketplace.json — and check the version segment of the cache path, since that is what the skill is actually being read from.

One standard, many repos

A workspace's repos should be configured the same way, and "the same way" should be a file rather than a habit. A repo profile is that file: one versioned JSON holding the standard — private or public, merge methods, branch protection, required checks by name, labels, the CI shape per language, the coverage policy — with per-language blocks and a per-repo override valve.

Maintainerd ships the mechanism and no values. The profile is an argument; nothing here names an org.

  • new-repo creates or --adopts a repo against a profile: scaffolds the files, runs bootstrap, creates the labels, and applies the GitHub settings — showing every gh api call first, and refusing the mutating half outside an interactive session with a human's own token.
  • doctor --profile reports drift and never fixes it: files vs profile, GitHub settings vs profile (each difference with the call that fixes it), and a producer for every required check. The cadence it is built for is a weekly issue per drifted repo, updated in place and closed on conformance — a human pastes the calls.

The contract is plugins/core/references/profile-schema.md.

Roadmap

Planned and candidate skills — what's shipped, what's ready to extract from an existing repo, and what's net-new — live in docs/roadmap.md.

License

MIT

About

Config-driven maintainer skills for Claude Code — a plugin marketplace of audits, daily ops, doc validation, and an autonomous issue→PR pipeline that drop into any repo via a small config contract.

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages