Skip to content

Repository files navigation

gardener

CI

gardener is a safety-gated Python CLI that dispatches Claude Code against a fleet of software repos, in two distinct ways:

  • align checks one target repo at a time against a conventions repo you supply — your own engineering conventions and alignment checklists, in a git repo gardener only ever reads — and, if authorized, fixes what's missing.
  • tend/garden/overnight make real, broader progress on a repo (or a whole opt-in list of them, unattended overnight) by dispatching that repo's own <slug>-dev-loop Claude Code skill — triage, implement, test, PR — never merging without an explicit, separate per-repo opt-in. Those skills are generated by create-dev-loop, gardener's open-source companion project; tend bootstraps one automatically if the target repo doesn't have one yet.

gardener's own job is orchestration and safety-gating, in plain Python. The actual reading/analysis/implementation judgment is delegated to a dispatched, safety-gated claude CLI invocation in every mode — gardener never itself decides what "aligned" means or what a fix should look like; it only decides how much a dispatched Claude run is allowed to do about it. See Usage below for the full command set.

Description

  • gardener align --repo <owner/repo>: clones the target repo read-only, clones (or refreshes) a local cache of your conventions repo (the source of truth for what "aligned" means — gardener is only the tool that consumes it), builds a prompt combining that repo's ALIGNMENT_PROMPT.md with the target repo's identity and the requested mode's constraints, and dispatches one headless claude -p run to produce a gap checklist — or, if explicitly authorized, to act on it.
  • gardener tend --repo <owner/repo>: dispatches the target repo's own <slug>-dev-loop skill instead — real triage/implement/test/PR work, not a conventions gap-check. See gardener tend in the usage docs.
  • gardener garden + gardener overnight: an opt-in list of repos and the unattended batch dispatcher that tends them one after another overnight. See docs/OVERNIGHT.md.
  • gardener dashboard: a local, read-only web UI over gardener status's own run history plus every tend/overnight log still being written to, so an unattended overnight run doesn't require polling the CLI by hand to see what it's doing. See docs/DASHBOARD.md.
  • gardener update: fast-forwards gardener's own checkout to origingardener overnight does this automatically before each run (opt out with --no-self-update), so a box running it unattended stays current without anyone needing to notice new commits and git pull by hand. See Self-update.

Installation

First Time Setup

Requires Python 3.10+, and the git, gh, and claude CLIs already installed and authenticated (gh auth status, and a working claude login) — gardener shells out to all three rather than reimplementing git hosting, auth, or the agent loop itself.

git clone https://github.com/dmccoystephenson/gardener.git
cd gardener
pip install -e .

This installs the gardener console script (via pyproject.toml's [project.scripts] entry point) and leaves the source editable — which is also what makes gardener update/overnight's self-update work at all (see Self-update); a non-editable install has no .git checkout to fast-forward, so it degrades to a no-op.

Conventions repo

Only align needs this. tend, garden, and overnight dispatch each repo's own dev-loop skill and work without one.

align audits a target repo against your engineering conventions, so you have to tell it where those live. gardener deliberately ships no default — a built-in one would mean silently auditing your repos against somebody else's opinions — so align fails fast with setup instructions until one is configured:

# Either, for every run:
export GARDENER_CONVENTIONS_URL=https://github.com/you/your-conventions.git

# Or, per invocation:
gardener align --repo <owner/repo> --conventions-repo <git-url>

Any git repo works as long as it contains these files. gardener checks only that they exist — the contents are entirely yours:

File What it's for
README.md Orientation for a reader of the conventions repo itself
ALIGNMENT_PROMPT.md What a run should audit for — the substance of your conventions
ALIGNMENT_CHECKLIST.md The checklist shape a run reports its findings back in
docs/CLAUDE_MD_STRUCTURE.md What a CLAUDE.md should contain
docs/README_STRUCTURE.md What a README.md should contain
docs/CONTRIBUTING_STANDARDS.md What a CONTRIBUTING.md should contain
docs/ISSUE_TEMPLATES.md Expected .github/ISSUE_TEMPLATE/ shape
docs/CODEOWNERS.md Expected CODEOWNERS conventions
docs/CI_STRUCTURE.md Expected CI workflow shape
docs/COMMIT_PR_CONVENTIONS.md Branch naming, commit style, PR conventions
docs/REVIEW_PROMPTING.md How code review should be requested and conducted
docs/DEV_LOOP_PATTERNS.md Dev-loop conventions for repos that have one

The dispatched run reads every one of them before forming a judgment, which is why all twelve are required: a missing file means auditing against an incomplete rubric, so gardener refuses rather than reporting a confidently-wrong result. Stub files are fine while you're building the set out.

The checkout is cached at ~/.cache/gardener/conventions (override with $GARDENER_CACHE_DIR) and refreshed each run — --no-refresh-conventions reuses it as-is. Pointing gardener at a different conventions repo re-points and refreshes that cache even under --no-refresh-conventions, since reusing the previous repo's checkout would produce a wrong answer rather than a stale one. gardener never commits or pushes into this cache.

Alerting (optional)

By default gardener alerts nowhere except its own local run history — you have to run gardener status or watch terminal output to see how a run went. To get a Discord notification on every run's outcome instead, configure a webhook one of two ways (checked in this order):

# 1. Environment variable (simplest — set it wherever gardener runs)
export GARDENER_DISCORD_WEBHOOK_URL="https://discord.com/api/webhooks/XXX/YYY"

# 2. A gitignored config file, for a persistent/cron context where
#    exporting an env var per-invocation isn't practical — a plain
#    KEY=VALUE dotenv file, mode 600):
mkdir -p ~/.local/state/gardener   # or $GARDENER_STATE_DIR if overridden
umask 077
echo 'DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/XXX/YYY' \
  > ~/.local/state/gardener/notify.env

No webhook configured (neither of the above) means notifications are a clean no-op — gardener align still works exactly the same, nothing prints or fails because of it. See docs/ALERTING.md for how this is implemented.

Usage

gardener align --repo <owner/repo> [--implement] [--file-issue] [--conventions-repo <git-url>]
gardener tend --repo <owner/repo> [--allow-merge]
gardener allowlist list | add --repo <owner/repo> | remove --repo <owner/repo>
gardener garden list | add --repo <owner/repo> | remove --repo <owner/repo>
gardener overnight [--hours N] [--concurrency N] [--strategy round-robin|issue-count|random] [--no-self-update]
gardener status [--repo <owner/repo>]
gardener tail-transcript <path> [-f]
gardener dashboard [--port N]
gardener update [--check]

See docs/USAGE.md for the full command reference: every flag (--implement, --file-issue, --model, --timeout, --conventions-repo, --no-refresh-*), how tend bootstraps and dispatches a target repo's own dev-loop skill, orphaned-work recovery, concurrent-dispatch safety, the merge allow-list, live session/transcript visibility, and run logs.

For the unattended "tend to my garden while I sleep" flow (the garden opt-in list, overnight's batching/budget/resume-cursor design, and the per-device wiring recipes it's actually been deployed with), see docs/OVERNIGHT.md.

For the dashboard's garden view (the table/plant-plot of every repo's health), see docs/DASHBOARD.md.

Support

Experiencing a bug?

Please file a bug report here.

Contributing

See CONTRIBUTING.md for how to propose a change, branch naming, and running the test suite. Community participation is governed by the Code of Conduct.

Testing

PYTHONPATH=. python3 -m unittest discover -s tests -v

A passing run ends with OK. None of the automated tests hit the network, invoke a real claude/git/gh process, or mutate a real repo. See docs/TESTING.md for exactly what each test module covers and for the manual/end-to-end verification steps required before trusting a change to the dispatch layer.

Safety model

gardener never invokes claude with bypassPermissions or any equivalent auto-approve-everything mode, for any mode, under any flag combination — enforced in dispatch.py, which raises rather than silently proceeding if it's ever reached. See docs/SAFETY.md for the full three-layer tool-scoping model, how headless tend dispatch handles the "ask the user before merging" problem with nobody there to ask, and the merge allow-list mechanics. See SECURITY.md to report a vulnerability or to review the trust model before pointing gardener at a repo you don't fully trust.

Alerting design

See docs/ALERTING.md for the Notifier abstraction, DiscordNotifier/NullNotifier/CompositeNotifier, and how _notify_run maps a run's outcome to a severity.

Development

No build step — this is a stdlib-only Python CLI (see Architecture below). Clone it, pip install -e ., edit, re-run the tests.

Project Status

Working end to end, real-verified against live repos in every dispatch mode (align report-only, tend with and without --allow-merge, overnight including its resume cursor and --concurrency, live transcript visibility, and the create-dev-loop bootstrap path). See docs/PROJECT_STATUS.md for the full history of what was run, when, and what was confirmed afterward.

Architecture

Stdlib-only Python — no third-party pip dependencies; gardener shells out to git, gh, and claude rather than reimplementing git hosting, GitHub API auth, or an agent loop. See docs/ARCHITECTURE.md for the full module tree and gardener's relationship to the conventions repo it consumes.

Related projects

  • create-dev-loop — the open-source Claude Code skill that generates the per-repo <slug>-dev-loop skills gardener tend dispatches. gardener orchestrates and safety-gates when those skills run across a fleet; create-dev-loop decides what each one knows about its repo. Either is usable without the other: create-dev-loop's skills run standalone as a /<slug>-dev-loop slash command, and gardener will bootstrap one on demand for any repo in its garden that lacks one.

About

Safety-gated CLI that dispatches Claude Code to align repos against conventions, or tend a whole fleet of repos unattended overnight via each one's own dev-loop skill — with a local dashboard for live visibility, and never merges without an explicit per-repo opt-in.

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Contributors

Languages