/assess-pr
The /assess end-of-run offers - open a PR with the report, track the Top 3 Actions in the user's issue tracker, freeze the assessment into a CI gate, and file tool feedback. TRIGGER when the /assess orchestrator reaches the end-of-run offers; not a standalone user command.
Claude Code
$ npx -y skills add bjcoombs/ai-native-toolkit --skill assess-pr --agent claude-code
Codex
$ npx skills add bjcoombs/ai-native-toolkit --skill assess-pr -a codex -y
Cursor
$ npx skills add bjcoombs/ai-native-toolkit --skill assess-pr -a cursor -y
Gemini CLI
$ npx skills add bjcoombs/ai-native-toolkit --skill assess-pr -a gemini-cli -y
GitHub Copilot
$ npx skills add bjcoombs/ai-native-toolkit --skill assess-pr -a github-copilot -y
OpenCode
$ npx skills add bjcoombs/ai-native-toolkit --skill assess-pr -a opencode -y
Windsurf
$ npx skills add bjcoombs/ai-native-toolkit --skill assess-pr -a windsurf -y
SKILL.md
assess-pr.SKILL.mdname: assess-pr
description: The /assess end-of-run offers - open a PR with the report, track the Top 3 Actions in the user's issue tracker, freeze the assessment into a CI gate, and file tool feedback. TRIGGER when the /assess orchestrator reaches the end-of-run offers; not a standalone user command.
Assess PR and Issues
The end-of-run half of `/assess`. The report (`.assess/assess-report.md`), the complexity heatmap, and the doc graph are written. This step runs the optional end-of-run offers - in order - then closes the loop:
1. **Open a PR** with the report (Step 5). 2. **Track the Top 3 Actions** in the user's issue tracker (Step 6). 3. **Freeze the assessment into a CI gate** (Step 6.5). 4. **Tool feedback** (Step 7) - surface anomalies and offer to file feedback. 5. **Uninstall `/assess`** (Step 8) - the escape hatch: remove everything this run wrote.
Each offer is independent (uninstall excepted - it's mutually exclusive with the write-back offers): a user can accept any subset. The step references back into the written `assess-report.md` artifact (notably the Top 3 Actions table's `Issue` column), not into the scoring prose, so it stays decoupled from how the report was produced.
Phase 2: ask once, batched (and the non-interactive contract)
These are the **write-back phase** of the consent lifecycle (Phase 2; Phase 1 was the tool installs, Phase 3 the mutation pass - see the assess SKILL.md). Do not serialise them into back-to-back modals. Present them as **one batched, multi-select AskUserQuestion**: "Now that the report is written, which of these should I do?" with the options (open a PR, track the Top 3 Actions, freeze a CI gate, file feedback, and - the mutually-exclusive escape hatch - uninstall `/assess` from this repo), pre-filtered by feasibility:
- Drop the **PR** option when the push-capability / remote check below (Step 5) shows no direct or fork PR is possible; on a read-only target, offer the fork variant instead. When the target states it takes no contributions (Step 5's no-contributions scan), offer the **no-contributions** variant instead on a read-only target (READ / TRIAGE) or in a clone of the user's own fork: a PR inside the user's fork, never one against upstream. A user who can push to the non-fork repo itself keeps the direct PR option, with the statement quoted.
- Drop the **CI gate** option when the workflow could never run (no GitHub remote).
- Keep **issue tracking** and **feedback** always (feedback needs no repo write).
- **Uninstall** (Step 8) always appears: it removes what this run wrote. It doesn't compose with the write-back offers (no point opening a PR *and* deleting the report), so treat selecting it as "skip the others and clean up".
Then execute the accepted offers **in order** (PR → issues → gate → feedback), because the later ones cross-link to the PR the first may have opened. Uninstall, if chosen, runs instead of the others. The per-step detail below is the execution recipe for each accepted offer; the *asking* happens once, here.
**Non-interactive contract (headless / CI).** When `run-context.json .interactive` is `false`, **make no AskUserQuestion call at all** - every write-back offer is already recorded as `{type, status: "skipped", reason: "non-interactive"}` in `run-context.json .offers` (`pr`, `issue_tracking`, `ci_gate`, `feedback`, `uninstall`). Write nothing back, open no PR, create no issues, emit no gate, file no feedback, uninstall nothing; the run ends with the local `.assess/` artifacts only. A headless `/assess` completes with zero prompts.
jq '{interactive, offers}' "$REPO_ROOT/.assess/run-context.json"---
Step 5: Ask Whether to Open a PR
After writing the files, first **check whether a direct PR is even possible** before offering one. A user on `READ` or `TRIAGE` access can't push a branch to the target repo, so an unconditional "open a PR?" offer is infeasible and wastes a turn.
# Detect push capability. `gh` returns viewer fields for the current user.
# Push capability is derived from `viewerPermission` (one of ADMIN |
# MAINTAIN | WRITE | TRIAGE | READ | NONE) - GitHub's GraphQL Repository
# type has no `viewerCanPush` field, so don't request it (the CLI errors
# and the whole call returns empty, silently degrading every write-
# accessible repo to the "leave local" branch).
PUSH_INFO=$(gh repo view --json viewerPermission,viewerCanAdminister,nameWithOwner,isFork,owner 2>/dev/null || true)
PERM=$(echo "$PUSH_INFO" | jq -r '.viewerPermission // empty')
case "$PERM" in
ADMIN|MAINTAIN|WRITE) CAN_PUSH=1 ;;
*) CAN_PUSH=0 ;;
esac
# A clone of the user's own fork reports ADMIN, so CAN_PUSH alone cannot tell
# the upstream's maintainer from an outsider working in their fork.
IS_FORK=$(echo "$PUSH_INFO" | jq -r '.isFork // false')
# isFork says the repo has a parent, not whose fork it is: a collaborator with
# push access to someone else's fork also sees isFork=true and WRITE. The
# current repo counts as the user's own fork only when the viewer owns it.
VIEWER_LC=$(gh api user 2>/dev/null | jq -r '.login // empty' | tr '[:upper:]' '[:lower:]')
OWNER_LC=$(echo "$PUSH_INFO" | jq -r '.owner.login // empty' | tr '[:upper:]' '[:lower:]')
if [ "$IS_FORK" = true ] && [ -n "$VIEWER_LC" ] && [ "$VIEWER_LC" = "$OWNER_LC" ]; then
IS_OWN_FORK=true
else
IS_OWN_FORK=false
fi
# If the command failed (no remote, no gh, not a GitHub repo, unauthenticated),
# $PUSH_INFO is empty and $PERM stays empty - fall back to the local-branch
# flow with the reason. Never silently assume push works.
# Detect remote-to-gh redirect: GitHub follows transfers/renames, so gh may
# resolve a different owner/repo than the configured remote implies.
REMOTE_URL=$(git remote get-url origin 2>/dev/null || true)
if [ -n "$REMOTE_URL" ]; then
# Normalise SSH and HTTPS forms (and GitHub Enterprise hosts) to owner/repo.
# SSH: git@host:owner/repo.git -> owner/repo
# HTTPS: https:
Read more
name: assess-pr description: The /assess end-of-run offers - open a PR with the report, track the Top 3 Actions in the user's issue tracker, freeze the assessment into a CI gate, and file tool feedback. TRIGGER when the /assess orchestrator reaches the end-of-run offers; not a standalone user command.
Assess PR and Issues
The end-of-run half of `/assess`. The report (`.assess/assess-report.md`), the complexity heatmap, and the doc graph are written. This step runs the optional end-of-run offers - in order - then closes the loop:
1. **Open a PR** with the report (Step 5). 2. **Track the Top 3 Actions** in the user's issue tracker (Step 6). 3. **Freeze the assessment into a CI gate** (Step 6.5). 4. **Tool feedback** (Step 7) - surface anomalies and offer to file feedback. 5. **Uninstall `/assess`** (Step 8) - the escape hatch: remove everything this run wrote.
Each offer is independent (uninstall excepted - it's mutually exclusive with the write-back offers): a user can accept any subset. The step references back into the written `assess-report.md` artifact (notably the Top 3 Actions table's `Issue` column), not into the scoring prose, so it stays decoupled from how the report was produced.
Phase 2: ask once, batched (and the non-interactive contract)
These are the **write-back phase** of the consent lifecycle (Phase 2; Phase 1 was the tool installs, Phase 3 the mutation pass - see the assess SKILL.md). Do not serialise them into back-to-back modals. Present them as **one batched, multi-select AskUserQuestion**: "Now that the report is written, which of these should I do?" with the options (open a PR, track the Top 3 Actions, freeze a CI gate, file feedback, and - the mutually-exclusive escape hatch - uninstall `/assess` from this repo), pre-filtered by feasibility:
- Drop the **PR** option when the push-capability / remote check below (Step 5) shows no direct or fork PR is possible; on a read-only target, offer the fork variant instead. When the target states it takes no contributions (Step 5's no-contributions scan), offer the **no-contributions** variant instead on a read-only target (READ / TRIAGE) or in a clone of the user's own fork: a PR inside the user's fork, never one against upstream. A user who can push to the non-fork repo itself keeps the direct PR option, with the statement quoted.
- Drop the **CI gate** option when the workflow could never run (no GitHub remote).
- Keep **issue tracking** and **feedback** always (feedback needs no repo write).
- **Uninstall** (Step 8) always appears: it removes what this run wrote. It doesn't compose with the write-back offers (no point opening a PR *and* deleting the report), so treat selecting it as "skip the others and clean up".
Then execute the accepted offers **in order** (PR → issues → gate → feedback), because the later ones cross-link to the PR the first may have opened. Uninstall, if chosen, runs instead of the others. The per-step detail below is the execution recipe for each accepted offer; the *asking* happens once, here.
**Non-interactive contract (headless / CI).** When `run-context.json .interactive` is `false`, **make no AskUserQuestion call at all** - every write-back offer is already recorded as `{type, status: "skipped", reason: "non-interactive"}` in `run-context.json .offers` (`pr`, `issue_tracking`, `ci_gate`, `feedback`, `uninstall`). Write nothing back, open no PR, create no issues, emit no gate, file no feedback, uninstall nothing; the run ends with the local `.assess/` artifacts only. A headless `/assess` completes with zero prompts.
jq '{interactive, offers}' "$REPO_ROOT/.assess/run-context.json"---
Step 5: Ask Whether to Open a PR
After writing the files, first **check whether a direct PR is even possible** before offering one. A user on `READ` or `TRIAGE` access can't push a branch to the target repo, so an unconditional "open a PR?" offer is infeasible and wastes a turn.
# Detect push capability. `gh` returns viewer fields for the current user. # Push capability is derived from `viewerPermission` (one of ADMIN | # MAINTAIN | WRITE | TRIAGE | READ | NONE) - GitHub's GraphQL Repository # type has no `viewerCanPush` field, so don't request it (the CLI errors # and the whole call returns empty, silently degrading every write- # accessible repo to the "leave local" branch). PUSH_INFO=$(gh repo view --json viewerPermission,viewerCanAdminister,nameWithOwner,isFork,owner 2>/dev/null || true) PERM=$(echo "$PUSH_INFO" | jq -r '.viewerPermission // empty') case "$PERM" in ADMIN|MAINTAIN|WRITE) CAN_PUSH=1 ;; *) CAN_PUSH=0 ;; esac # A clone of the user's own fork reports ADMIN, so CAN_PUSH alone cannot tell # the upstream's maintainer from an outsider working in their fork. IS_FORK=$(echo "$PUSH_INFO" | jq -r '.isFork // false') # isFork says the repo has a parent, not whose fork it is: a collaborator with # push access to someone else's fork also sees isFork=true and WRITE. The # current repo counts as the user's own fork only when the viewer owns it. VIEWER_LC=$(gh api user 2>/dev/null | jq -r '.login // empty' | tr '[:upper:]' '[:lower:]') OWNER_LC=$(echo "$PUSH_INFO" | jq -r '.owner.login // empty' | tr '[:upper:]' '[:lower:]') if [ "$IS_FORK" = true ] && [ -n "$VIEWER_LC" ] && [ "$VIEWER_LC" = "$OWNER_LC" ]; then IS_OWN_FORK=true else IS_OWN_FORK=false fi # If the command failed (no remote, no gh, not a GitHub repo, unauthenticated), # $PUSH_INFO is empty and $PERM stays empty - fall back to the local-branch # flow with the reason. Never silently assume push works. # Detect remote-to-gh redirect: GitHub follows transfers/renames, so gh may # resolve a different owner/repo than the configured remote implies. REMOTE_URL=$(git remote get-url origin 2>/dev/null || true) if [ -n "$REMOTE_URL" ]; then # Normalise SSH and HTTPS forms (and GitHub Enterprise hosts) to owner/repo. # SSH: git@host:owner/repo.git -> owner/repo # HTTPS: https:
A Claude Code plugin - and a set of standalone skills for any AI assistant: skills and agents for AI-native development. In Claude Code it runs locally against your own codebase using whichever model you already pay for.
Other skills on ai-native-toolkit.
ab-equivalence
Compare two versions of an LLM-directed document - an original (teacher) and a candidate (student) - across a transfer set and…
assess-findings
Renders the /assess report from the deterministic run-context.json and the layer scorecard - the scorecard, the verbatim…
assess
Assess a codebase's readiness for AI agent contributors using the layered contract model, and generate a complexity hotspot SVG…
More skills from other plugins.
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
ask-matt
Ask which skill or flow fits your situation. A router over the skills in this repo.
Questions about assess-pr.
- Which coding agents can use assess-pr?
- assess-pr installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.

