원클릭으로
nightly
// Nightly code quality sweep — resolves bot PR conflicts, reviews recent commits, surveys existing code, checks resolved issues, and updates tend workflows.
// Nightly code quality sweep — resolves bot PR conflicts, reviews recent commits, surveys existing code, checks resolved issues, and updates tend workflows.
Generic CI environment rules for GitHub Actions workflows. Use when operating in CI — covers security, CI monitoring, comment formatting, and investigating session logs from other runs.
Polls GitHub notifications and handles items that dedicated workflows miss — fork PR comments, cross-repo mentions, and stale unanswered items. Runs on a schedule.
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 the harness auth token (Claude OAuth or OpenAI API key). Use when setting up tend on a new repo or when asked to install/configure tend.
Investigates a specific tend GitHub Actions run by downloading its session-log artifacts and parsing the JSONL traces. Surfaces which skill tend loaded, what tools it called with what inputs, files it read or wrote, and where decisions went wrong. Use when asked to "debug a tend run", "investigate a tend run", "why did tend do X", "what did the bot do in CI", "look at the session logs", or to reconstruct tend's behavior step-by-step from a run ID, URL, or PR number.
Triages new GitHub issues — classifies, reproduces bugs, attempts conservative fixes, and comments. Use when a new issue is opened and needs automated triage.
Tend-specific guidance for tend CI workflows. Adds non-standard workflow inclusion for usage analysis and repo conventions on top of the generic tend-* skills.
| name | nightly |
| description | Nightly code quality sweep — resolves bot PR conflicts, reviews recent commits, surveys existing code, checks resolved issues, and updates tend workflows. |
| metadata | {"internal":true} |
Resolve conflicts on bot PRs, review recent commits, survey a slice of existing code/docs, and update tend workflows.
Run the scope audit script to check the bot PAT against tend's required classic OAuth scopes (repo, workflow, notifications, write:discussion, gist, user):
${CLAUDE_PLUGIN_ROOT}/scripts/pat-scope-audit.sh
The script prints key=value lines. Act on STATUS:
STATUS=ok: all scopes present. Search open issues for a PAT scope audit tracking issue (gh issue list --state open --search "PAT in:title"); if found, close it with a comment noting the scopes are now granted.STATUS=fine-grained: no X-OAuth-Scopes header. Fine-grained PATs have no documented self-introspection endpoint — skip.STATUS=missing: open or update a tracking issue. Use a title containing "PAT" (e.g. Bot PAT: missing scopes) so future runs can dedup by title search. Before creating, run gh issue list --state open --search "PAT in:title" and update the existing issue with gh issue edit if one is already open. The body lists the values from MISSING= and links step 8 of the install-tend skill for remediation: https://github.com/max-sixty/tend/blob/main/plugins/install-tend/skills/install-tend/SKILL.md#8-bot-token-and-secretRun tend check to verify this repo's tend setup (branch protection, bot
permission, secrets, secret allowlist):
uvx tend@latest check 2>&1 | tee /tmp/tend-check.txt
If every check line is PASS (no FAIL and no SKIP), close any
open drift issue. A run with only SKIP lines (e.g. lost API permission, a
transient gh error) is not a pass — leave the issue untouched, neither
close nor file. Scope to bot-authored issues so a maintainer-filed issue
that happens to contain "configuration drift" is never auto-closed:
gh issue list --state open --author '@me' \
--search '"configuration drift" in:title' \
--json number --jq '.[].number' \
| xargs -r -I {} gh issue close {} --comment 'tend check now passes.'
If any check is FAIL, file or update one tracking issue with title
tend check: configuration drift on <owner>/<repo>. Dedup by title,
scoped to bot-authored issues:
gh issue list --state open --author '@me' \
--search '"configuration drift" in:title' \
--json number,title,body
No labels. Body lists the current FAIL lines (one bullet per check, with
a one-line reason) plus a _Last refreshed: <YYYY-MM-DD>_ footer. Updates:
Find conflicted PRs from this bot and from upstream dependency bots:
BOT_LOGIN=$(gh api user --jq '.login')
for author in "$BOT_LOGIN" app/dependabot app/renovate; do
gh pr list --author "$author" --json number,title,mergeable,headRefName,author \
--jq '.[] | select(.mergeable == "CONFLICTING")'
done
Skip the rest of this step if none of the queries return anything.
dependabot[bot] and renovate[bot] both attempt rebases on their own, but stop once main rewrites a file the bump also touched (Cargo.lock, uv.lock, generated headers). The PR then sits CONFLICTING. Each bot exposes a way to force a fresh rebuild against current main.
For each conflicted PR by one of these bots, first confirm the branch has no human commits — both triggers force-push and would discard local edits. The check compares each commit's author login against the bot's commit-author login (dependabot[bot] or renovate[bot]); note that the PR's .author.login is the App slug (app/dependabot, app/renovate) and does not match — use the literal commit-author login below.
# COMMIT_LOGIN is "dependabot[bot]" or "renovate[bot]" depending on the bot
gh pr view <number> --json commits \
| jq --arg bot "$COMMIT_LOGIN" \
'[.commits[].authors[].login] | unique | map(select(. != $bot))'
--author (PR list) | Commit-author login | Trigger |
|---|---|---|
app/dependabot | dependabot[bot] | Post @dependabot recreate as a comment. |
app/renovate | renovate[bot] | Edit the PR body and replace - [ ] <!-- rebase-check --> with - [x] <!-- rebase-check -->. Renovate has no comment command for rebase. |
Do not check out or rebase manually — the bot owns the branch and will overwrite anything you push.
For each conflicted PR authored by $BOT_LOGIN, dispatch a subagent to:
gh pr checkout <number>git merge origin/maingit add, git commit --no-edit/tend-ci-runner:running-in-cigit merge --abort and comment explaining manual resolution is neededRun subagents in parallel. Each must work in isolation (git worktree add /tmp/pr-<number> <branch>). After all complete, clean up temp worktrees.
git log --since='24 hours ago' --oneline main
If no commits in the past 24 hours, skip this step.
Get the aggregate diff:
OLDEST=$(git log --since='24 hours ago' --format='%H' main | tail -1)
git diff ${OLDEST}^..HEAD
git log --since='24 hours ago' --format='%h %s' main
Read the project's CLAUDE.md before reviewing. Apply the review checklist below to the diff, focusing on changes rather than unchanged code. Also check whether CLAUDE.md itself needs updating to reflect the new code (e.g., new file paths, changed commands, removed patterns).
gh issue list --state open --json number,title
gh pr list --state open --json number,title,headRefName
For each open issue, check whether recent commits or the current codebase state already resolve it. If resolved, comment with the evidence (commits, CI runs, or code state that resolves the issue). Close the issue with gh issue close when:
main is passing. Skip this case if the issue body contains "Do not close manually"; those are recurring tracking issues (e.g., monthly review-runs trackers) with their own lifecycle.running-tend skill) explicitly authorizes closing issues.Otherwise, leave it open for a maintainer to close.
The action's "Report failure" step records only a workflow run link in tend-outage issues — annotations and job logs aren't reliably available while the job is in_progress. Run the enrichment script to fetch failure details for each newly referenced run and post them as a comment. The script is idempotent: it skips runs already marked with <!-- enriched-run:RUN_ID -->.
"${CLAUDE_PLUGIN_ROOT}/scripts/enrich-tend-outage-issues.sh"
Run the survey script to get today's file list (rotating through the full repo over 28 days):
${CLAUDE_PLUGIN_ROOT}/scripts/nightly-survey-files.sh
Skip files that aren't meaningfully reviewable: lock files (uv.lock, Cargo.lock, package-lock.json), binary assets, vendored dependencies, and generated files (build output, compiled protobuf, auto-generated workflow YAML). When unsure, check the file — a quick glance is cheaper than missing something.
Before reviewing files, read the project's CLAUDE.md and any project-specific skills or review criteria it references. Apply the review checklist below to each file in full.
Used by both Step 4 (applied to recent diffs) and Step 6 (applied to full files).
General quality:
Convention compliance (from CLAUDE.md and project skills):
Regenerate the tend workflow files and open a PR if anything changed. The checkout's .github/ directory may be mounted read-only under the sandbox (protecting bots from modifying their own workflows in place), so do the regeneration in a git worktree under /tmp, which is writable. (Don't write $TMPDIR/... — GitHub Actions runners leave $TMPDIR unset, so the path expands to /tend-update-workflows, which the runner user can't create. The bot then falls back to running uvx tend@latest init in the main checkout and has to git checkout -- .github/workflows to revert.)
# Base the worktree on the open update-workflows PR if one exists, so the
# regen produces only the incremental delta. Falls back to HEAD when no PR
# is open (first regen, or after the prior PR merged). `-B` resets a stale
# local branch from a prior failed attempt rather than rejecting it.
git fetch origin tend/update-workflows 2>/dev/null || true
BASE=$(git rev-parse --verify origin/tend/update-workflows 2>/dev/null || git rev-parse HEAD)
git worktree add "/tmp/tend-update-workflows" -B tend/update-workflows "$BASE"
cd "/tmp/tend-update-workflows"
# Capture the stamped tend version before regenerating, so the next bash
# call can report the bump. The generator writes
# `# Generated by tend X.Y.Z. Regenerate with: uvx tend@latest init` into
# every workflow's header — one anchor per file, independent of what
# templates pin for `uvx tend@...` or the action tag. Pre-stamp workflows
# (generated before 0.0.17) have no version on that line, so extraction
# returns an empty string and the renderer below falls back to omitting the
# version line rather than printing `unknown → X.Y.Z`. Shell state doesn't
# persist between bash calls, so the version is stashed to a temp file
# rather than a shell variable.
grep -hoE '^# Generated by tend [0-9]+\.[0-9]+\.[0-9]+' \
.github/workflows/tend-*.yaml 2>/dev/null \
| sed -E 's/^# Generated by tend //' | sort -u | head -1 \
> "/tmp/tend-old-ver"
uvx tend@latest init
# `init` auto-migrates a legacy `.config/tend.toml` → `.yaml` if it finds
# one (verifies parsed equivalence before swapping); `.config/` is checked
# alongside `.github/workflows` so that one-shot upgrade ships in the same
# nightly PR as the regenerated workflows that depend on it.
git status --porcelain .github/workflows .config
If git status shows no changes, clean up and continue:
cd -
git worktree remove "/tmp/tend-update-workflows" --force
If files changed, build the PR title and body with the version bump (when detected) and a git diff --stat summary, then commit, push, and open the PR:
OLD_VER=$(cat "/tmp/tend-old-ver")
NEW_VER=$(grep -hoE '^# Generated by tend [0-9]+\.[0-9]+\.[0-9]+' \
.github/workflows/tend-*.yaml 2>/dev/null \
| sed -E 's/^# Generated by tend //' | sort -u | head -1)
DIFF_STAT=$(git diff --stat .github/workflows .config)
TITLE="chore: update tend workflows"
if [ -n "$OLD_VER" ] && [ -n "$NEW_VER" ] && [ "$OLD_VER" != "$NEW_VER" ]; then
TITLE="chore: update tend workflows ($OLD_VER → $NEW_VER)"
fi
{
echo "Automated nightly regeneration of tend workflow files."
echo
if [ -n "$OLD_VER" ] && [ -n "$NEW_VER" ] && [ "$OLD_VER" != "$NEW_VER" ]; then
echo "**tend version:** $OLD_VER → $NEW_VER"
echo
fi
echo "**Changed files:**"
echo '```'
printf '%s\n' "$DIFF_STAT"
echo '```'
} > "/tmp/tend-update-body.md"
git add -A .github/workflows .config
git commit -m "$TITLE"
git push -u origin tend/update-workflows
gh pr create --title "$TITLE" --body-file "/tmp/tend-update-body.md"
cd -
git worktree remove "/tmp/tend-update-workflows" --force
The version line (and the versions in the title) are omitted when either side of the detection is empty or both sides match — e.g. a template tweak at the same pinned version, or the first regen after the header stamp was added, where the pre-regen workflows still carry the unstamped header.
Before acting on findings, check for duplicates and existing work:
gh issue list --state open --json number,title
gh pr list --state open --json number,title,headRefName
The default action is a PR, not an issue. If there's a plausible fix, make it — explain uncertainty in the PR description.
For each finding:
Not run by default. Only run a step here when the project's running-tend skill explicitly enables it.
Keep the project's changelog up to date with recent changes. The running-tend skill specifies the changelog file and the branch to push to.
Report: commits reviewed, files surveyed, findings, actions taken, assessment (clean / minor issues / needs attention).