@gsimone/pr-health
Flag your open PRs that are stuck: red CI, merge conflicts, unanswered review-bot comments, or gone quiet.
AGENTS.md
73 lines · 2.5 KB · MarkdownPR Health — agent notes
Imports
import checkPrHealth from 'kody:@gsimone/pr-health'
import dailyCheck from 'kody:@gsimone/pr-health/daily-check'src/classify.ts holds the pure rules (classify, formatDigest,
REVIEW_BOTS, ABANDONED_DAYS). It is internal — imported by the two exports
above, not published as its own entry point, since it has no default export.
Smoke tests
On-demand check (read-only, safe to run any time):
import checkPrHealth from 'kody:@gsimone/pr-health'
export default async function main() {
const { viewer, examined, findings, accessWarning } = await checkPrHealth()
return { viewer, examined, stuck: findings.length, accessWarning }
}Job wrapper — sends real email when the finding set changed since the last run. Run it once to verify, then enable the schedule:
import dailyCheck from 'kody:@gsimone/pr-health/daily-check'
export default async function main() {
return await dailyCheck()
}Calling dailyCheck() twice in a row is the useful test: the second call should
report notified: false, proving the quiet-unless-changed behavior works.
Secrets
Uses {{secret:githubPat}} in the Authorization header. The value is
substituted at the network boundary — no code here ever reads it. Requires
api.github.com on the secret's approved hosts.
Storage
packageStorage() holds one key, last-seen-fingerprints: a map of
owner/repo#number → sorted reason kinds. It is what makes the job quiet. Clear
it to force the next run to report everything:
import { packageStorage } from 'kody:runtime'
// inside a package export
await packageStorage().delete('last-seen-fingerprints')Note packageStorage() throws in ad hoc execute — it needs package
provenance, so exercise it through a package export.
Edge cases
- Partial GraphQL failures are normal. GitHub returns
dataanderrorswhen an org refuses the token (long-lived fine-grained PAT, SAML, IP allow list).graphql()keeps the data;describeAccessErrorssummarizes the rest intoaccessWarning. Do not make these fatal. - Bot list is a heuristic.
REVIEW_BOTSinclassify.tsmatches on login prefix and strips a[bot]suffix. Add new bots there. - The absent stale rule is intentional. See README. Do not add one back without re-testing the hit rate against the real account first.
ABANDONED_DAYS = 120suppresses a PR entirely, before any other rule. A PR idle for years will never be reported, by design.