Skip to main content

install-tend

Sets up tend — an autonomous junior maintainer for a GitHub repo, powered by Claude or OpenAI Codex — that reviews PRs, triages issues, and fixes CI. Creates config, generates workflows, configures secrets and branch protection via API, creates the bot account, and provisions harness authentication. Use when installing tend, when clearing a failing `tend check`, and when changing an installed repo's tend config, generated workflows, secrets, environments, branch protection, or bot access.

Jump to install

Source facts

Repository
max-sixty/tend
Last source activity
September 9, 2026 at 08:23
Detected SKILL.md language
English
Stars
37
Forks
7

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
8 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
install-tend
description
Sets up tend — an autonomous junior maintainer for a GitHub repo, powered by Claude or OpenAI Codex — that reviews PRs, triages issues, and fixes CI. Creates config, generates workflows, configures secrets and branch protection via API, creates the bot account, and provisions harness authentication. Use when installing tend, when clearing a failing `tend check`, and when changing an installed repo's tend config, generated workflows, secrets, environments, branch protection, or bot access.
# Install Tend Set up tend on the current repo, or change an installation it already has. When asking the user questions during these steps, batch known questions into one interaction where the client supports it, and present concrete options when there are clear choices (e.g. secret-migration confirmation, registry token route). When a question requires the user to do something off-screen (visit a URL, run a command, paste a value back), spell the next step out in the question or option description: the exact web link, the exact command. "Generate a token on the registry's site" is not enough — give the URL. The user should not have to ask "where do I do that?". ## Kickoff Read `.config/tend.yaml` first. Its presence says whether this is an install or a change to one, and where it exists it settles the harness: the `harness` key, or Claude when the key is absent, which is how a Claude install is normally written. Derive `REPO` once at the start — the second call resolves a fork clone to its root source, so no remotes need touching (every command below passes `--repo "$REPO"` explicitly): ```bash gh auth status LOCAL=$(gh repo view --json nameWithOwner --jq '.nameWithOwner') REPO=$(gh api "repos/$LOCAL" --jq 'if .fork then .source.full_name else .full_name end') echo "$REPO" ``` When the resolution changes the name, or returns `null` (source deleted or invisible to the token), confirm the target with the user before touching anything — a deliberately maintained hard fork keeps its own name. A config from a finished install makes this a change: take the harness from the config, lay out only the steps the task touches, and start. Finished means `uvx tend@latest check` passes — secrets, bot access, protection — *and* the workflows are live on the default branch, which that check never looks at (step 11 commits without pushing, so an install can stop with everything else in place): ```bash gh api "repos/$REPO/contents/.github/workflows" \ --jq '[.[].name | select(startswith("tend-"))] | length' ``` The summary checklist at the end describes a finished install, so skip it. A preference a step needs (7a's auth mode, 10's bio stance) is asked at that step. Otherwise this is an install, including a resume of one that never finished (config present, later steps missing). Gather every preference at the Kickoff, so the rest of the install stops only where a step genuinely needs the user. First generate three bot-name candidates from the bare repo name (`<repo>-bot`, `<repo>-tend`, `tend-<repo>`) and check their availability in parallel: ```bash for name in cand1 cand2 cand3; do gh api "users/$name" >/dev/null 2>&1 && echo "$name: TAKEN" || echo "$name: available" done ``` Also check whether the repo has a README (names in step 5) — it decides whether the badge appears in question 3 and the customize follow-up. In the message alongside the questions, lay out the install: it targets `$REPO`, runs the section headings below as steps, and typically takes 5–10 minutes of the user's hands-on time (browser logins, OAuth approvals, occasional copy-paste) — the agent drives the rest, ending at a local commit (pushing waits for their go-ahead, step 11). Then ask these three questions together. Answering them is the go-ahead — no separate "ready to start?" confirmation. Drop any question the user's request or an existing config already answers (a supplied bot name, a chosen harness; the auth mode a config-settled harness leaves open is asked at 7a, not here); a fully specified request leaves nothing to ask, and is itself the go-ahead. 1. **Harness** — which model runs the bot and which credential it bills to: - **Claude — OAuth token** (recommended for adopters with a Claude subscription) — draws from the subscription's usage limits. - **Claude — API key** — a console.anthropic.com key, billed per token. Fits when there's no subscription to draw on, or the user wants a dedicated billing surface and per-key revocation. - **Codex — Plus/Pro subscription** — experimental. It needs two browser handoffs: a Codex device approval and a repo-scoped GitHub token form. Concurrent jobs receive an access-only token; one serialized weekly workflow owns renewal. It depends on Codex's internal auth mode; detail in ${CLAUDE_SKILL_DIR}/references/security-model.md. - **Codex — OpenAI API key** — standard pay-per-token path. 2. **Bot name** — the available candidates, recommended first. "Other" takes a custom name; check its availability before using it. The tool needs 2–4 options, so generate more candidates whenever fewer than two come back available. 3. **Defaults** — accept the default setup, or pick areas to change: - **Accept all defaults** (recommended) — no workflow overrides, a placeholder guidance overlay, the badge added, and the bot bio "tend agent for `<owner>/<repo>`. I triage issues and help maintain `<repo>`." Nothing is locked in: every default is an ordinary edit later (`.config/tend.yaml`, the files the install writes) or a re-run of this skill. - **Customize…** — pick the areas in a follow-up question. A **Customize…** answer gets one more multi-select question: which areas to change, defaults applying to whatever is left unselected (an empty submission included), each option naming its default in its description. Both the defaults description and this follow-up list only the areas still open — drop an area the user's request settles (the request, not the default, governs its step: "skip the badge" skips it), an area a previous run already applied, and an area that can't apply (no README → no badge option). The tool caps a question at 4 options, so a new area means grouping, not appending. - **Workflow config** — setup steps, workflow conditions, schedules, job permissions/timeouts, env vars (default: no overrides) - **Bot guidance overlay** — PR title format, labels, review routing, target branch, nightly actions (default: a placeholder overlay) - **README badge** — placement and style, or leaving it out (default: added, matching the README's existing badge style) - **Bot profile bio** — the stance line on the bot's profile (default: "tend agent for `<owner>/<repo>`. I triage issues and help maintain `<repo>`.") Each area selected there is asked about at its step (1, 4, 5, 10); the rest apply the default without asking. Steps that can't be defaulted — migrating a release secret, naming environment reviewers, creating the bot account, approving OAuth — still interact when they arrive. Follow each step in order. Skip steps that are already done — check each prerequisite before acting. ## Browser sessions Step 6 (when the bot account must be created), step 7b's Codex subscription setup, and step 8's mint paths (8a/8b) need a browser session. Check whether `mcp__claude-in-chrome__*` is connected (`tabs_context_mcp`) before the first browser step, or any question that would offer one as an option. When it is, drive the browser steps yourself rather than offering a hand-off choice: hand the user only the prompts automation can't cross (a signup CAPTCHA, a 2FA or password reauth), and resume once they complete the prompt in the open tab. When it isn't but the runtime can open a URL in the user's browser, open each URL as soon as it appears and hand over the exact prompt or code. Otherwise, give the user the URL and wait for confirmation. Driving uses the user's real Chrome profile, so logging in as the bot displaces their own github.com session until they sign back in — tell them when handing the browser back. Before acting as the bot, verify the logged-in user via the avatar menu. ## 1. Create config Create `.config/tend.yaml` with at minimum `bot_name`, plus `harness` if the user chose Codex (the default Claude harness can be omitted). See README.md "Harnesses" for the comparison. ```yaml bot_name: <bot-name> # For Codex: # harness: codex # model: gpt-5.6-sol # Both harnesses optionally accept: # effort: medium # low | medium | high | xhigh; Claude Opus/Sonnet also accept max ``` Write the Codex model into the config. It is the installation's reviewed pin; the raw action deliberately has no model default. List the secrets the repo already holds: ```bash gh secret list --repo "$REPO" --json name --jq '.[].name' ``` Any repo-level secret not in `secrets.allowed` triggers a `tend check` warning. The operational secrets (bot token, harness auth) never belong at repo level — steps 7–8 store them in the `tend` environment, and a repo-level copy from a pre-environment install is exactly the exposure the environment closes, so it gets deleted once the environment copy is in place. Classify each remaining secret and act now — don't defer: - Build/observability tokens (e.g., `CODECOV_TOKEN`, `SENTRY_DSN`) are fine at the repo level. Add them to the allowlist: ```yaml secrets: allowed: ["CODECOV_TOKEN"] ``` - Release secrets (registry tokens like `PYPI_TOKEN`/`NPM_TOKEN`, signing keys, deploy credentials) at the repo level are reachable from any workflow run, including ones a write-access bot can trigger with no merge. Don't allowlist them. Migrate each to a GitHub Environment whose deployment policy pins to the admin-gated refs from §3 (the default branch and/or all tags). The bot can reach neither ref class, so it cannot reach the secret. `tend check` sweeps every credential-holding environment — one that stores a secret, or that an `id-token: write` job deploys to, since trusted publishing stores nothing — and fails on any it cannot confirm gated: no reviewer and no policy, an unverified branch entry, tag entries without §3's all-tags ruleset, or a ref policy on an environment some workflow reaches on `release`, `repository_dispatch`, or a `workflow_dispatch` with inputs, which the bot fires at a ref the policy already admits. A half-migrated environment surfaces on the next check rather than passing silently. Migrate the secret: recreate it on the Environment, delete the repo-level copy (confirm with the user first), and set `environment: <name>` on the publishing job. Configure the deployment policy. Allow whichever ref classes the workflow runs on: ```bash REPO=<owner>/<repo>; ENV=<name> DEFAULT_BRANCH=$(gh api "repos/$REPO" --jq .default_branch) gh api --method PUT "/repos/$REPO/environments/$ENV" \ -F 'deployment_branch_policy[protected_branches]=false' \ -F 'deployment_branch_policy[custom_branch_policies]=true' # Continuous-deploy on default branch: gh api --method POST "/repos/$REPO/environments/$ENV/deployment-branch-policies" \ -f "name=$DEFAULT_BRANCH" -f type=branch # Release on tags (workflow has `on: push: tags:`): gh api --method POST "/repos/$REPO/environments/$ENV/deployment-branch-policies" \ -f 'name=*' -f type=tag ``` Verify: ```bash gh api "/repos/$REPO/environments/$ENV/deployment-branch-policies" \ --jq '.branch_policies | map({name, type})' ``` Each entry must match a ref class from §3 (default branch and/or all tags). Then sweep deploy/publish workflows. Each must trigger on `push: tags:` or `push: branches: [<default-branch>]` (per §3 workflow design) and declare an Environment. The grep below catches the common shapes; it misses reusable workflows in other repos and over-matches `pull_request_target` references in expressions and step inputs, so read each hit: ```bash grep -RniE 'tags:|workflow_dispatch|release:|schedule:|workflow_run|repository_dispatch|deployment:|pull_request_target' .github/workflows ``` An OIDC-to-cloud deploy has no secret to migrate; the Environment with its admin-gated deployment policy plus the cloud provider's trust policy is then the only control on that path. The original repo-level secret value isn't readable (GitHub secrets are write-only), so a fresh token is needed. Ask the user how to obtain it; recommend whichever fits the registry: - **CLI** — if the registry has a token-issuing CLI (e.g., `npm token create`), run it and capture the token. - **Chrome** — drive the registry's token page via `mcp__claude-in-chrome` (most registries — PyPI, crates.io, Docker Hub — only issue tokens via the web UI). Some registries (PyPI in particular) force a 2FA reauth at token-creation time; the user completes it in the open tab, per Browser sessions. - **Manual** — user generates the token themselves on the registry's site and stores it themselves: hand over the environment's `gh secret set` command fully substituted. With neither `--body` nor a pipe it prompts for the value, so the token never sits in the chat transcript. Don't delete the repo-level copy until `gh secret list --repo "$REPO" --env "$ENV" --json name` shows it — the write is theirs on this route, so nothing else tells the agent it landed. Whichever route is chosen, include the exact token-creation URL in the question or option description (and in the follow-up message if manual). Common registries: - PyPI: `https://pypi.org/manage/account/token/` - npm: `https://www.npmjs.com/settings/<user>/tokens/new` (or `npm token create`) - crates.io: `https://crates.io/settings/tokens` - Docker Hub: `https://app.docker.com/settings/personal-access-tokens` - GitHub Packages / deploy: `https://github.com/settings/tokens` For other registries, look up the token page before asking. Accept any other route the user suggests. Never ask the user to dig the old token out of their password manager and re-paste it — issuing a fresh token and revoking the old one is part of the migration's point. Discover existing CI workflows so tend-ci-fix can watch them: ```bash grep -l 'push:\|pull_request' .github/workflows/*.yml .github/workflows/*.yaml 2>/dev/null ``` For each match, extract the workflow `name:` field. These are the workflows that run tests, linting, or builds — tend-ci-fix should watch them. Configure: ```yaml workflows: ci-fix: watched_workflows: ["ci", "lint"] # names of workflows to watch ``` If no CI workflows exist, either skip ci-fix (`enabled: false`) or help the user create one first. If the user picked workflow config at Kickoff, ask which overrides to set in a multi-select question — otherwise set none: - Setup steps and env vars (system deps, language version, pre-build hooks, top-level env vars) - Workflow conditions (e.g., skip review on `tend:dismissed` PRs — see below) - Schedule overrides (cron timing for nightly/weekly) - Permissions / timeouts on specific jobs For each selected category, follow up with a free-text ask, then write the override into `.config/tend.yaml`. See the next subsection for override syntax. ### Customizing generated workflow YAML The generator owns every `tend-*.yaml` file — direct edits are lost on the next `uvx tend@latest init`. Instead, set `workflow_extra` (top-level) or `jobs.<name>` (job-level) overrides in `.config/tend.yaml`. Overrides follow RFC 7396 (JSON Merge Patch): mappings deep-merge, scalars and lists replace. Common example — skip review on PRs labeled `tend:dismissed` (so authors can opt out of re-reviews after the initial pass). Because scalars replace under Merge Patch, the override must duplicate the default draft check: ```yaml workflows: review: jobs: review: if: "github.event.pull_request.draft == false && !contains(github.event.pull_request.labels.*.name, 'tend:dismissed')" ``` See ${CLAUDE_SKILL_DIR}/references/tend.example.yaml for more override examples (extending permissions, timeouts, top-level env vars). ## 2. Generate workflows ```bash uvx tend@latest init --with-install-test ``` `--with-install-test` adds a one-shot `tend-install-test.yaml` workflow that runs on the install PR to verify the committed workflows match the generator's current output. (It cannot see secrets — its `pull_request` run is outside the `tend` environment — so `tend check` is what verifies those.) The next nightly regen runs `uvx tend@latest init` without the flag, and the init cleanup step removes the file from the default branch. Verify workflow files appear in `.github/workflows/tend-*.yaml`. Check for workflows using `anthropics/claude-code-action`: ```bash grep -rl 'anthropics/claude-code-action' .github/workflows/ 2>/dev/null ``` If found, delete them — tend replaces claude-code-action entirely. Remind the user that team members should @-mention the bot account instead of `@claude`. ## 3. Ref protection Two ref classes can land code that reaches a deploy or publish workflow: the default branch (via merge) and tags (via tag push). Restrict both to admin-only operations so every privileged code path chains back to an admin action. The bot has write, which is below every role that can bypass, so it satisfies neither. Survey existing rulesets; skip any slot already covered: ```bash gh api "repos/$REPO/rulesets" --jq '.[] | {name, target, enforcement}' ``` **Merge restriction on the default branch.** Create if missing: ```bash gh api "repos/$REPO/rulesets" --method POST --input - << 'EOF' { "name": "Merge access", "target": "branch", "enforcement": "active", "conditions": { "ref_name": { "include": ["~DEFAULT_BRANCH"], "exclude": [] } }, "rules": [{ "type": "update" }], "bypass_actors": [{ "actor_id": 5, "actor_type": "RepositoryRole", "bypass_mode": "exempt" }] } EOF ``` `actor_id: 5` is the admin role. The base role IDs run maintain 2, write 4, admin 5 — not ordered by privilege, so the plausible guess for maintain is in fact write, the bot's own role, and granting it hands the bot the merge. Before adding any bypass actor, read back what the ruleset actually granted: ```bash gh api graphql -f query='{repository(owner:"<owner>", name:"<repo>") {rulesets(first:10){nodes{name bypassActors(first:10)
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub