Tend allows open-source projects to have an agent as a dutiful junior maintainer. The agent can review PRs, triage issues, fix CI, help out with research, maintain a changelog, sweep the repo for improvements, refine documentation, etc.
Current status: Tend is in its early days. It has been working extremely well in Worktrunk for the past couple of months, such that folks suggested I generalize it into its own project.
To use Tend, a project needs:
- A GitHub account for the agent (for example this project's is @tend-agent)
- One of:
- A Claude Max subscription (harness = "claude")
- A ChatGPT Plus or Pro subscription (experimental), or an OpenAI API key (harness = "codex") β see Codex (experimental alternative).
Tend offers the default code & guidance for the agent. Specifically that means:
- A set of workflow templates
- A very particular set of Skills
- ...skills it has acquired over a very long career (two months)
Each project's agent remains completely under its control, and runs only in the project's Github Actions environment. The Tend project never sees any tokens / keys / etc.
The easiest way to get started is to install the Tend plugin into a local Claude
Code session, and run the /install-tend skill:
claude plugin marketplace add max-sixty/tend
claude plugin install install-tend@tend
claude /install-tendIt'll take 5-15 minutes to set up the config, workflow generation, bot account,
secrets, and branch protection. Tend is configured through a config
file and a repo-local /running-tend skill.
- Tend uses lots of tokens. A Claude subscription, an Anthropic API key,
an OpenAI API key, or a ChatGPT plan is needed to fund the runs.
- Maintainers of sizeable OSS projects get a 20x Claude Max subscription for free from Anthropic.
- A compromise of the runner or credential proxy could expose the bot PAT or long-lived model credential. The agent cannot read those during normal operation; subscription-mode Codex receives only an expiring access token. The merge restriction, environment gate, and immutable releases limit what a stolen bot credential can do.
| Workflow | Trigger | What happens |
|---|---|---|
| review | PR opened/updated | Reviews for correctness and duplication. Traces error paths. Monitors CI. Pushes fixes to bot-authored PRs. |
| mention | @bot mention, review | Responds to requests in PR and issue conversations. |
| triage | Issue opened | Classifies the issue, checks for duplicates, reproduces bugs, attempts conservative fixes. |
| ci-fix | CI fails or is cancelled | Diagnoses the unsuccessful default-branch run, searches for the same pattern elsewhere, and opens a fix PR when needed. |
| nightly | Daily | Resolves conflicts on open PRs, reviews recent commits, surveys ~10 files for bugs and stale docs, closes resolved issues, regenerates tend workflow files. |
| weekly | Weekly | Reviews dependency PRs, approves safe patch and minor updates (the bot never merges β a merge restriction is the security boundary). |
| notifications | Every 15 minutes | Drains unread notifications as a recovery queue and repairs conflicts on bot-authored PRs. |
| review-runs | Daily | Reviews recent CI runs for behavioral problems and proposes skill/config improvements. |
| codex-auth-refresh | Weekly | When any workflow uses Codex, renews experimental Plus/Pro auth through its single-writer credential; no-ops for API-key installs. |
The bot reacts π while a session is working: on an issue when it opens, on a PR whenever a review starts, and on a comment that mentions the bot. The reaction comes off when the session ends.
Scheduled workflows also support manual dispatch for testing. GitHub runs
schedule triggers on a best-effort basis and drops ticks under load, so
the intervals above are the requested cadence rather than a guarantee β
observed gaps between runs are routinely longer. All are generated by
default except ci-fix, which requires watched_workflows to be
configured. Any can be omitted on the next regeneration:
workflows:
weekly:
enabled: falseuvx tend@latest init reads .config/tend.yaml and writes tend-*.yaml workflow
files into .github/workflows/. Each workflow handles triggers, skip
conditions, concurrency, and permissions β then calls the composite action
for the configured harness, pinned to the released generator version
(max-sixty/tend/claude@X.Y.Z for Claude, max-sixty/tend/codex@X.Y.Z for Codex).
The nightly regen restamps a newer tag when a new tend version ships.
When the review workflow is generated, init also merges one ignore into
.github/actionlint.yaml: the workflow's concurrency.queue is valid GitHub
syntax that actionlint's schema rejects. The ignore applies only to generated
workflows and preserves the rest of the adopter-owned config.
Both actions run the same security and rate-limit preflight checks and resolve bot identity. They differ in how the agent runs:
- Claude harness β runs the official
claudebinary headless (claude -p) as a non-sudo user inside Anthropic Sandbox Runtime, behind a local credential-injecting proxy, so the bot token and Anthropic credential never enter the agent's environment. Each workflow's prompt is a slash command (/tend-ci-runner:review) that loads the matching skill. - Codex harness β installs the
@openai/codexCLI, then runscodex execinside the same boundary. GitHub calls use Tend's exact-host proxy. API-key model calls use OpenAI's narrow Responses API proxy; subscription sessions receive only an expiring access token. An AGENTS.md staged into$CODEX_HOMEteaches Codex to resolve/tend-ci-runner:NAMEreferences to the bundled skill markdown.
Edit the config or the generator β not the workflow files. They're regenerated
on every tend@latest init.
Tend gives an agent write access to a repository. The security model has seven layers:
Merge restriction is the primary boundary. A GitHub ruleset prevents the
bot from merging to protected branches β bot-authored PRs require human
approval. The bot proves this against itself on every run: preflight asks
GitHub whether the bot's own credentials can bypass any ruleset covering the
default branch (current_user_can_bypass β GitHub's evaluation, so teams,
custom roles, and org-level rulesets are all accounted for) and refuses to
start unless the answer is no. tend check verifies the setup;
tend check --fix creates the ruleset.
Immutable releases lock each release published after the setting is
enabled, including its assets and tag. tend check requires the setting and
--fix enables it before the next release.
Environment-gated credentials β a workflow the bot can cause to run
reaches no credential: not the bot token, not the model auth, not a release
token or a trusted-publishing identity. The bot token and model auth live in
the repo's tend GitHub Environment, whose deployment policy admits only
the refs tend check confirmed the merge restriction covers, so a workflow
pushed to any other branch is refused them before its first step. The same
check sweeps every other credential-holding environment β one that stores a
secret, or that a job requesting id-token: write deploys to β for a gate
the bot cannot pass: a non-bot required reviewer, or a policy naming only
verified refs that no workflow reaches on a trigger the bot both fires and
steers. It flags any repo-level secret not explicitly listed in
secrets.allowed, where the operational names are refused outright.
tend check --fix creates the environment and sets its policy; moving the
secrets into it stays manual β their values can't be read back.
Disposable execution boundary β both harnesses create an independent event
checkout and run sandbox_setup: plus the whole agent turn as one process tree
inside the pinned Anthropic Sandbox Runtime, under a separate non-sudo user.
The stable Actions checkout remains runner-owned for setup and POST cleanup.
Tend's exact-host proxy holds the bot token; Claude model auth uses the
same mechanism, while API-key Codex auth uses OpenAI's proxy that forwards only
Responses API calls upstream. The proxies authenticate the agent without
placing the PAT or API credentials in its environment. Subscription-mode Codex
instead receives an expiring access-only token, never the rotating refresh
token. GitHub authentication applies to any repository the bot account can
access, including repositories other than the one that started the run.
Config pinning β before the agent starts, both harnesses restore every
CLAUDE.md, CLAUDE.local.md, AGENTS.md, .claude/, and .agents/ in the
tree, at any depth, from the base branch. Both harnesses also restore
.mcp.json, .claude.json, .gitmodules, .ripgreprc, and .husky, blocking
startup-time code execution and prompt injection from a PR's own copy of these
files.
Rate limiting β Burst detection (10 PRs and 10 issues per 20 minutes, checked independently) and daily spike detection halt the bot before runaway loops cause damage.
Fixed prompts β Workflow prompts come from the action, not from attacker-controlled input like PR descriptions or comments.
Full threat model: docs/security-model.md.
.config/tend.yaml β only bot_name is required. The default harness runs
Claude; harness: codex selects OpenAI Codex (see
Harnesses below).
bot_name: my-project-bot
# Optional runtime switch β every new job skips before checkout or setup
# enabled: false
# Codex installs pin both values; omit both to use Claude.
# harness: codex
# model: gpt-5.6-sol
# effort: medium # low | medium | high | xhigh; Claude Opus/Sonnet also support max
# args: [--max-turns, "40"] # exact additional CLI argumentsTop-level enabled: false pauses tend from the default branch without removing
its workflows. Setting it back to true (or removing it) resumes new jobs
without regeneration.
The secrets, stored in the repo's tend environment (install-tend creates
it; tend check verifies it), depend on the harness:
| Harness | Required secrets |
|---|---|
claude |
TEND_BOT_TOKEN + one of CLAUDE_CODE_OAUTH_TOKEN (subscription) or ANTHROPIC_API_KEY (API-billed) |
codex |
TEND_BOT_TOKEN + either OPENAI_API_KEY, or the subscription trio CODEX_AUTH_JSON, CODEX_REFRESH_AUTH_JSON, and CODEX_REFRESH_PAT |
TEND_BOT_TOKEN is the bot account's PAT β see
example config for scopes.
CLAUDE_CODE_OAUTH_TOKEN is from claude setup-token. The API keys are
from console.anthropic.com and platform.openai.com. See
Codex (experimental alternative) for the
subscription trio.
docs/security-model.md has the full leak
breakdown.
All other options β setup steps, protected branches, workflow overrides,
schedules β are documented in
docs/tend.example.yaml.
Tend reads CLAUDE.md like any Claude Code session β build commands, test
commands, project conventions all go there.
For tend-specific guidance, add a skill overlay at
.claude/skills/running-tend/SKILL.md. Common uses: recording which CI
workflow names tend-ci-fix watches, PR title conventions, label policies.
Tend supports Claude and Codex. Pick whichever fits the credentials and billing path that already work for you; both run the same workflows and skills.
Runs the official claude binary headless (claude -p) in the shared SRT
boundary behind a local credential-injecting proxy: the bot token and
the Anthropic credential live only in the proxy, never in the agent's
environment. Two auth modes:
CLAUDE_CODE_OAUTH_TOKEN(recommended with a Claude subscription) β Claude Code OAuth token fromclaude setup-token, funded by the subscription's usage limits.ANTHROPIC_API_KEYβ standard API key from console.anthropic.com, billed per token against the Console org. Pick this when there's no Claude subscription, when the bot should bill against a dedicated Console org, or when per-key revocation matters.
The proxy injects whichever you set into requests to api.anthropic.com; the agent itself only ever holds a dummy.
Installs @openai/codex and invokes codex exec in the shared SRT boundary.
GitHub access goes through Tend's exact-host proxy. Under API auth, the OpenAI
key is read from stdin by OpenAI's narrow Responses API proxy and is never
placed in the agent's environment. Under subscription auth, the sandbox gets
an expiring access-only auth.json, never the rotating refresh token. A bundled
AGENTS.md teaches Codex to resolve tend's slash commands to skill markdown.
Codex's own nested sandbox is disabled. The pinned Anthropic Sandbox Runtime is the single filesystem, network, seccomp, and process-lifetime boundary for both harnesses.
Two auth modes:
- ChatGPT Plus or Pro (experimental): concurrent jobs receive
CODEX_AUTH_JSON, an access-only bundle that Codex cannot refresh. A single serialized weekly workflow holdsCODEX_REFRESH_AUTH_JSON, rotates it, then publishes the next access-only bundle usingCODEX_REFRESH_PAT. - API:
OPENAI_API_KEYis a standard pay-per-token key from platform.openai.com.
The split fixes the old race: no consumer receives the rotating refresh token,
so concurrent jobs cannot invalidate one another's refresh state. This is
experimental because it uses Codex's internal chatgptAuthTokens mode. The
weekly job runs Codex's built-in refresh and persists the updated full bundle.
Tend pins and tests the Codex version, but an OpenAI change can still break the
weekly refresh until Tend updates.
A badge signals the repo is maintained with tend:
[](https://github.com/max-sixty/tend)The install-tend skill adds this automatically during setup; the kickoff prompt lets you opt out.
MIT
