ado-open-pr
Skill2 starsChanged 13 days agoMITView on GitHub
Create an Azure DevOps pull request for the current branch using the Azure DevOps MCP. Pushes the branch, generates the description via the generate-pr-notes contract, opens the PR against a target branch (default: the repo's PR target from the shared branch map, else the repo's ADO default branch), and adds reviewers resolved from teams or explicit emails. By default (opt out with --no-sync-spec) scans the committed diff against spec-kit/openspec docs and syncs drifted docs to match the code before opening the PR. Also by default (opt out with --no-review) runs a pre-PR review with lint and function-complexity measurements plus parallel clean-context reviewers (code, security, standards and spec, test coverage, minimal change and duplication), fixes accepted findings with the user, and rates how much human review the PR needs. Use when asked to open a PR, create an Azure DevOps PR, raise a pull request, or ship the current branch for review.
- Reads credentials
- Installs packages
- Commits and pushes
In this repository18 other files it gives its agents.
- ado-pr-code-reviewSkill
- ado-pr-resolve-commentsSkill
- ado-pr-teams-notifySkill
- agent-playtestSkill
- explain-diff-htmlSkill
- generate-pr-notesSkill
- git-commit-conventional-strictSkill
- install-atlassian-mcpSkill
- install-azure-devops-mcpSkill
- install-playwright-mcpSkill
- install-sentry-mcpSkill
- prd-to-sdd-specSkill
- sdd-qa-to-ticketSkill
- setup-project-configSkill
- spec-recalibrateSkill
- sync-api-specSkill
- tech-plan-to-ticketSkill
- tech-plan-to-wikiSkill
The file
Show its rules and text
We split this skill into the rules it gives an agent
39 rules. About 0 are common, 15 are distinctive, and 24 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈15
Distinctive · estimate
This author's own choices. Worth a look.
24
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 28
All optional, any order. Disambiguate each bare (unflagged) token as follows:
- Distinctive: line 40
Default target branch: see Step 1 item 4.
- Very specific: line 64
Abort: if the branch is empty, main, master or develop — a PR must come from a feature branch.
- Very specific: line 65
Check the working tree: git status --porcelain=v1 -b.
- Very specific: line 74
Never fall back to a hard-coded develop: several repos merge into master or main, and some still have a live develop that is not a PR …
- Distinctive: line 76
If the user named a branch that the map does not list for this repo, say so and ask them to confirm.
- Very specific: line 77
If the map's note for this repo says a pipeline opens this kind of PR itself (for example a release/* → main backport), say so and ask …
- Distinctive: line 95
If it returns a PR:
- Very specific: line 98
if the local branch has commits that are not on origin/<current-branch>, ask whether to push them to that PR (git push origin …
- Distinctive: line 115
No SDD layout found → nothing to check, skip silently.
- Very specific: line 118
Resolve the SDD tool and doc roots: Read docs/agents/sdd.md → Tool: if present.
- Very specific: line 131
If you cannot confidently pin a single folder, report that and skip the sync — do not guess-edit an unrelated spec.
- Distinctive: line 138
No drift → report *"spec docs aligned"* and continue to Step 2.
- Very specific: line 139
Before editing a drifted doc, check git status --porcelain -- <doc-path>.
- Distinctive: line 152
Before anything is pushed, a team of reviewers checks the committed diff.
- Distinctive: line 160
Use the first one that applies:
- Very specific: line 184
When Step 1 warned of a conflict and a kept critical or medium finding is in one of the conflicting files, start with *"Rebase first: …
- Distinctive: line 196
After fixing, ask again whether to continue to push and open the PR.
- Very specific: line 201
Before you edit a file, check git status --porcelain -- <path>.
- Very specific: line 204
After the fixes, offer to run the repo's test command from the review profile.
- Very specific: line 219
No upstream → git push -u origin <current-branch>.
- Very specific: line 220
Always push explicitly to origin / the current branch — never a bare git push, so the pushed ref is the same ref the PR's sourceRefName …
- Distinctive: line 221
If the push fails, stop and show the error — do not create a PR against a stale or missing remote branch.
- Very specific: line 248
Use the --title <text> arg if provided.
- Very specific: line 249
If a Jira ticket was resolved and the title does not already start with it, prefix: [TICKET-ID] <title>.
- Very specific: line 254
Choose the template: Look in the target branch for the first file that exists, and read it with git show origin/<target-branch>:<path>:
- Very specific: line 259
no repo template → the shared template references/pull_request_template.md of the generate-pr-notes skill (for example …
- Very specific: line 263
Fill it with a sub-agent: Use the Agent tool (Task in older harnesses) with subagent_type="general-purpose".
- Distinctive: line 266
Keep its headings, their order and every checklist item word for word, and add no sections.
- Very specific: line 270
Use these facts: Jira ticket = <resolved ticket> or none.
- Very specific: line 279
If the returned body is unexpectedly fenced, strip only a leading line exactly equal to ``
markdown `` and a final line exactly equal to … - Very specific: line 296
Never drop a template heading, a checklist item, the Jira: line or the ## Pre-PR Review section.
- Very specific: line 297
Final unconditional clamp: if still over 4000 chars, cut the text before ## Pre-PR Review and append …(truncated) at the cut, so the body …
- Distinctive: line 323
If the file is not found, skip team expansion and use only explicit reviewer args.
- Distinctive: line 333
If that batched call fails, retry once per-id and record which ids succeeded/failed.
- Very specific: line 344
Run scripts/confluence_ticket_log.py from this skill's folder.
- Very specific: line 368
Never edit the page another way: Do not read the page with the Confluence MCP and write it back: confluence_get_page returns a converted …
- Distinctive: line 387
if no PR tools config was found and a team arg was given, note it (team args produced no reviewers)
- Distinctive: line 394
If a new month's page was created, say that its title still has last month's sprint number and must be updated by hand.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Create Azure DevOps Pull Request
⚙️ Dependencies
- Azure DevOps MCP — provides the
repo_*andcore_*tools. Install once with theinstall-azure-devops-mcpskill (writes.mcp.json+~/.env.mcp-azure-devops), then restart your agent. Repository: https://github.com/microsoft/azure-devops-mcp - generate-pr-notes skill — its note-generation contract is reused for the PR description (see Step 4).
Install it with
npx skills add jamesyang124/agent-skills --skill generate-pr-notes -g. - Optional: Azure CLI (
az, logged in) — used only to identify the current user so they are not added as their own reviewer. Falls back togit config user.emailwhenazis unavailable. - Optional: Confluence credentials in
~/.env.mcp-atlassian(CONFLUENCE_URL,CONFLUENCE_USERNAME,CONFLUENCE_API_TOKEN, written byinstall-atlassian-mcp) andpython3— used only by Step 6.5.
Install with npx skills add jamesyang124/agent-skills --skill ado-open-pr -g.
Arguments
All optional, any order. Disambiguate each bare (unflagged) token as follows:
| Token shape | Interpreted as |
matches [A-Z]+-\d+ | Jira ticket |
contains @ | reviewer email / unique name |
matches a team key under teams in the PR tools config | reviewer team |
literally draft | open the PR as a draft |
| anything else | target branch |
Flags: --title <text> (explicit PR title), --ado-project <name>, --ado-repo <name>, --no-sync-spec (skip the Step 1.5 spec-alignment scan, which otherwise runs by default), --no-review (skip the Step 1.7 pre-PR review), --review=<keys> (run only these Step 1.7 reviewers, comma-separated from code,security,standards,tests,minimal; refactor is an old name for minimal), --deep (deep review: run all Step 1.7 reviewers twice and the layer-6 tie-break), --no-release-notes (skip the Step 6.5 release-notes page update), --choice=<preview|fix|auto-fix|open> (answer the
Step 1.7 question in advance), --fast (fast path, below), --yes (with --fast: skip the final confirmation).
Default target branch: see Step 1 item 4.
Fast path (--fast). Choices that change nothing outside this machine take the recommended default without
asking. Every outward action of the run (push, PR create or update, reviewers, posted comments or replies, thread
status, a Confluence write) is collected and confirmed once, at the end, in one list ("Do all of these? (yes /
pick / no)"). --yes skips that one confirmation too. A flag that names a choice always wins over --fast. Never
automatic, even with --fast --yes: resolving a rebase conflict, a force-push, and anything in a non-interactive /
headless run, which still behaves as a preview. For this skill, --fast means:
| Decision | Default with --fast |
| Step 1.5 doc edits | trivial edits applied and committed; larger rewrites listed, not applied |
| Step 1.7 choice | auto-fix (trivial fixes applied and committed); larger and critical findings stay unresolved and go into the PR's Pre-PR Review section. With a critical finding, the final confirmation says so first |
| Step 1.7 tests | the review profile's test command runs (it is local) |
Push, PR create (draft only with draft), reviewers | in the final confirmation |
| Step 6.5 release-notes page | skipped unless --release-page=<id> names the page; then the write is in the final confirmation |
Step 1 — Preflight
- Confirm this is a git repo. Read the current branch:
git branch --show-current. Then rungit fetch originso everyorigin/<branch>read below (branch age, ahead count, the Step 1.5 diff) is current. If the fetch fails, warn that remote refs may be stale, continue, and repeat the warning in the Step 7 report. - Abort if the branch is empty,
main,masterordevelop— a PR must come from a feature branch. Tell the user which branch they are on. Arelease/*orhotfix/*source is allowed: a backport PR (for examplerelease/20261015→main) starts there. - Check the working tree:
git status --porcelain=v1 -b. If there are uncommitted changes, warn: "The PR will only include committed work — uncommitted changes are not part of it." Continue unless the user redirects. Resolve the Azure DevOps repository now with Step 3 (it needs only the git remote): the default-branch lookup below and the existing-PR check after it need the repository ID. Then resolve the target branch:
- an explicit target branch arg wins;
- otherwise the repo's PR target from
references/branch-map.mdin the siblingado-pr-code-reviewskill (same parent folder as this skill, e.g.../ado-pr-code-review/references/branch-map.md); - otherwise the repo's ADO default branch (
repo_repositorywithaction: "get"→defaultBranch). If its last commit is more than 90 days old, ask the user which branch to target instead of guessing. Never fall back to a hard-codeddevelop: several repos merge intomasterormain, and some still have a livedevelopthat is not a PR target. State the chosen target and where it came from before creating the PR. If the user named a branch that the map does not list for this repo, say so and ask them to confirm. If the map's note for this repo says a pipeline opens this kind of PR itself (for example arelease/*→mainbackport), say so and ask whether to open one by hand anyway. Then abort with a clear message if any of these is true:
- the target is the current branch;
origin/<target-branch>does not exist (git rev-parse --verify --quiet origin/<target-branch>fails) — the target branch is not on the remote;git rev-list --count origin/<target-branch>..HEADis0— the branch has no commits to merge.
Then check, as a warning only, whether the branch would conflict with today's target:
git merge-tree --write-tree --name-only origin/<target-branch> HEAD(in memory; it changes no file). On a non-zero exit, tell the user, for example "⚠️<target-branch>moved {n} commits since this branch was cut, and merging now would conflict in {files}" ({n} fromgit rev-list --count $(git merge-base origin/<target-branch> HEAD)..origin/<target-branch>), and repeat it in the Step 7 report. Do not stop, do not ask, and do not rebase: whether to rebase first is the user's choice. Keepgit diff $(git merge-base origin/<target-branch> HEAD) origin/<target-branch> -- <conflicting files>for the Step 1.7 reviewers ("Target moved" in part 1 of../ado-pr-code-review/references/review-findings.md).Then stop if a PR already exists, before any review or push. Call
repo_pull_requestwith{ action: "list", repositoryId, project, status: "Active", sourceRefName: "refs/heads/<current-branch>", targetRefName: "refs/heads/<target-branch>" }. If it returns a PR:- report its id, title and URL (built as in Step 7) — do not create a second PR, and do not change its description or reviewers;
- if the local branch has commits that are not on
origin/<current-branch>, ask whether to push them to that PR (git push origin HEAD:<current-branch>), and push only on approval; - suggest
ado-pr-code-review <PR URL>to review the open PR, then stop. Skip Step 1.5, Step 1.7 and every later step.
- Resolve the Jira ticket in this order — accept the first
[A-Z]+-\d+match found:- explicit ticket arg
- current branch name
- latest commit subject (
git log --format=%s -1)
- Load the PR tools config now (
.github/config/pr-tools.json, see Step 6) so a bare token can be recognized as a team key during argument parsing. A missing or malformed config is fine here — it just means no team keys are known yet. - Capture the title candidate now —
git log --format=%s -1— and hold it for Step 4, together with the pre-sync commitgit rev-parse HEAD(the Step 1.7standardsreviewer reads the spec as it was at that commit). Do this before Step 1.5 can add adocs(spec): sync to codecommit on top ofHEAD; otherwise that commit would become the PR title.
Step 1.5 — Spec/doc alignment (on by default)
Keeps spec-driven docs from drifting behind the code that actually ships in this PR. The code is the source of truth — when a working spec doc disagrees with the committed diff, the doc is synced to the code, never the reverse. This step only ever edits spec docs — it reads the code but never changes the implementation. A code problem it happens to spot is surfaced for a human, never fixed here. Runs before the push (Step 2) so any doc fix rides along in the same PR.
When it runs — by default whenever the repo has an SDD layout (step 1 below). This is opt-out, not opt-in:
--no-sync-spec→ skip this step entirely and go straight to Step 2.- No SDD layout found → nothing to check, skip silently.
- The scan itself always runs when reached — there is no "do you want to scan?" prompt; consent is asked only per doc edit in step 5. A non-interactive / headless run still scans and reports drift but applies no edits (see step 5), so it never hangs.
- Resolve the SDD tool and doc roots. Read
docs/agents/sdd.md→Tool:if present. Otherwise detect from the repo root:.specify/,.speckit, orspec-kit.json→ spec-kit;openspec/,.openspec, oropenspec.json→ openspec. Neither → log "no SDD layout found — skipping spec alignment" and continue to Step 2. Locate the working docs for THIS change (the ones allowed to drift), plus the read-only rubric:
SDD tool Working docs — may be synced to code Read-only rubric — read from the target branch ( git show origin/<target-branch>:<path>), flag violations, never editspec-kit specs/<NNN-name>/{spec,plan,tasks}.md.specify/memory/constitution.mdopenspec openspec/changes/<name>/{proposal.md,design.md,tasks.md}andopenspec/changes/<name>/specs/openspec/project.mdand the top-levelopenspec/specs/treeIdentify
<NNN-name>/<name>in this order:- If the diff already touches an SDD doc (a working-doc file above appears in the diff), that pins the folder directly and is a signal in its own right: the author edited the spec in this PR, so step 4 also checks whether those spec edits actually match the code and, where they don't, corrects the doc the rest of the way — the code stays the source of truth and is never edited, so this is still a doc-only sync, not just a plain code→doc pass.
- Otherwise, the code paths the diff touches (most reliable), then the Jira ticket / branch name.
If you cannot confidently pin a single folder, report that and skip the sync — do not guess-edit an unrelated spec. Stay within this change's spec folder — reconciling drift across the wider repo is
spec-recalibrate's job, not this pre-PR guard.- Compute the diff the PR will contain:
git diff origin/<target-branch>...HEAD(three-dot; committed work only, matching the body built in Step 4). - Detect drift. Compare the diff against the working-doc paths from step 2 and produce a drift report — statements the code now contradicts, behavior renamed/removed/added, stale examples, and
tasks.mditems implemented-but-unchecked. Check every working doc for this change (spec.md,plan.md,tasks.md) — the same fact is often repeated across them, so one inconsistency can surface in several places; record all occurrences, not just the first. When the diff already edited an SDD doc (step 2), also confirm those spec edits actually match the code and finish any the author started but left half-done. Do this inline by default (read the docs yourself) so the step works on every harness. You may offload it to a read-onlygeneral-purposesub-agent (the Agent tool; Task in older harnesses) for token isolation only if your harness supports sub-agents — Claude Code does; harnesses like the VS Code Copilot agent do not, so fall back to inline there. Never make the sub-agent a hard requirement. Either way, treat the rubric files in step 2 and the top-levelopenspec/specs/tree as read-only — you may cite a constitution / source-of-truth violation but must never edit those trees. - Report and sync (code → doc), with consent — mirror the
ado-pr-resolve-commentsmodel:- No drift → report "spec docs aligned" and continue to Step 2.
- Before editing a drifted doc, check
git status --porcelain -- <doc-path>; if it already has uncommitted changes, skip that doc and report it — never fold the user's pre-existing WIP into the sync commit. - Trivial doc edits → show each as a before/after diff and apply on approval (
Edit); with--fast, apply them and list them. - Larger rewrites → present an update plan and apply on approval.
- Fix every occurrence. If an inconsistency appears in more than one place within this change's spec docs (across
spec.md/plan.md/tasks.mdor several sections), update all of them — a partial fix leaves the doc self-contradictory. Keep this bounded to this change's spec folder; propagating a fix across every spec in the repo isspec-recalibrate's job. - Non-interactive / headless run: report the drift but apply no edits — treat the absence of an approver as no approval and never block on a consent prompt, so a headless run never hangs.
- A rubric violation (constitution / source-of-truth spec contradicted by the code) is surfaced as a warning for the human to resolve — fix the code or consciously amend the rubric — and is never auto-synced.
- Commit the synced docs so the PR carries them.
git addonly the specific doc paths you edited (never-A/-u), then commit those same paths explicitly so nothing already in the index rides along:git commit -m "docs(spec): sync to code" -- <the doc paths you edited>. Both the scoped add and the pathspec commit are required to keep the user's unrelated staged/uncommitted work (warned about in Step 1) out of the PR. If the user declines every edit, continue with a note that the drift is unresolved.
This step is best-effort: any failure here (missing docs, ambiguous folder, sub-agent error) is reported and execution continues to Step 2. It must never block PR creation. The PR title still comes from the title candidate captured in Step 1, not from HEAD after this step's commit.
Step 1.7 — Pre-PR review (on by default)
Before anything is pushed, a team of reviewers checks the committed diff. Each reviewer looks for one kind of
problem in a clean context. You collect their findings, check them, fix the accepted ones with the user, and rate
how much human review the PR still needs. The briefs, the finding format and the rating rules are in
references/pre-pr-review.md. Read it before you start this step.
Skip this step with --no-review.
Choose the reviewers. The keys are
code,security,standards,tests,minimal. Use the first one that applies:--review=<keys>;- this repo's row in
references/review-profile.mdin the siblingado-pr-code-reviewskill (../ado-pr-code-review/references/review-profile.md), which also gives focus notes, the lint command and the test command; - all five.
There is no automatic second look. With
--deep, run every chosen reviewer twice (the second time reading the files in reverse order) and allow the layer-6 tie-break (part 2 of../ado-pr-code-review/references/review-findings.md).- Measure, then run the reviewers in parallel. First run the lint and function-complexity measurements in
references/pre-pr-review.mdyourself (scripts/complexity.py; best-effort: a missing linter is skipped and named, a missing lizard is offered for install). Lint problems become findings; the complexity list goes to theminimalreviewer and into the complexity score. Then start one freshgeneral-purposesub-agent per reviewer, all started in the same message (the Agent tool; Task in older harnesses). Do not fork your own context into them: each reviewer must start clean. Build each prompt from the run inputs, the common rules and that reviewer's brief inreferences/pre-pr-review.md. If the harness has no sub-agents, run the briefs one after another inline, and say in the report that the reviews did not have separate contexts. - Verify and merge the results as
references/pre-pr-review.mddescribes, with the shared rules in../ado-pr-code-review/references/review-findings.md: its verification layers (mechanical checks, pre-existing, precedents, an independent disprover per finding, proof by test for a kept critical — ask before running a test), keep / downgrade / drop / pre-existing, duplicates merged, delete-vs-extract conflicts settled, sorted by severity. Show the findings to the user, grouped by severity:
file:line — short title (reviewers), then the confidence and the suggested fix, in the same reviewer names asado-pr-code-review. When Step 1 warned of a conflict and a keptcriticalormediumfinding is in one of the conflicting files, start with "Rebase first: {files} conflict with<target-branch>and hold {n} of these findings; fixing them before the rebase may be redone." It is advice: do not rebase, and continue as the user decides. Then always ask what to do, whether or not there is a critical finding:Choice What happens Preview only No edits, no push, no PR. Write the findings to .git/pre-pr-review/<branch>.mdso the user can come back to them, and stop.Fix together Step 5 one finding at a time, each on approval. Auto-fix Apply every trivialfix at once, show the combined diff, and commit only after one approval (Step 6).largerandcriticalfindings still go one at a time with the user.Open the PR anyway No fixes. Continue to Step 2; the unresolved findings go into the PR's Pre-PR Review section. With a criticalfinding, ask once more before going on.After fixing, ask again whether to continue to push and open the PR; never continue on your own.
- Non-interactive / headless run: behave as Preview only.
- Fix (Fix together and Auto-fix; the same model as Step 1.5 and
ado-pr-resolve-comments):trivial→ show a before/after diff and apply it on approval (Auto-fix: all at once, one combined diff).larger→ show a short plan and apply it on approval.- Before you edit a file, check
git status --porcelain -- <path>. If it has uncommitted changes, skip that fix and say so. Never fold the user's work in progress into a review commit. - The user may decline any finding. Keep it in the list as unresolved.
- After the fixes, offer to run the repo's test command from the review profile. Do not run it without approval
(
--fastruns it).
- Commit the fixes so the PR carries them:
git addonly the paths you edited, thengit commit -m "<type>(<scope>): address pre-PR review" -- <those paths>, with a Conventional Commit type that fits the fixes (fix,refactor,test). The PR title still comes from the title candidate captured in Step 1. - Rate the review confidence (High / Medium / Low) with "Review confidence" in part 5 of
../ado-pr-code-review/references/review-findings.md, and keep the level, its reasons and the unresolved findings for Step 4 and Step 7.
Apart from the choice in step 4, this step is best-effort: if a reviewer fails, report it, count it in the confidence rating, and continue.
Step 2 — Push the branch
- Detect an upstream:
git rev-parse --abbrev-ref --symbolic-full-name @{u}(non-zero exit = no upstream). - No upstream →
git push -u origin <current-branch>. Upstream exists →git push origin HEAD. Always push explicitly toorigin/ the current branch — never a baregit push, so the pushed ref is the same ref the PR'ssourceRefNamewill point at (Step 5), regardless of what the local branch tracks. - If the push fails, stop and show the error — do not create a PR against a stale or missing remote branch.
Step 3 — Resolve the Azure DevOps repository
Azure DevOps remotes carry org / project / repo in the URL. Read it with git remote get-url origin and parse:
git@ssh.dev.azure.com:v3/{org}/{project}/{repo}
https://{org}@dev.azure.com/{org}/{project}/_git/{repo}
https://{org}.visualstudio.com/{project}/_git/{repo}Resolve each value with this precedence:
- org — the parsed value (needed only to build the PR URL in Step 7).
- project —
--ado-projectarg →git config azure.project→ parsed value. - repo —
--ado-repoarg →git config azure.repository→ parsed value.
Confirm the repo and capture its repository ID by calling repo_repository with { action: "get", project, repositoryNameOrId: <repo> }. Use the returned id as repositoryId in later calls.
Abort with a clear message if the Azure DevOps MCP is unavailable or the repo cannot be resolved.
This step runs early, inside Step 1 item 4, so the existing-PR check happens before any review or push. When you reach this heading in order, reuse the values already resolved.
Step 4 — Build the PR title and description
Title (the PR's title field):
- Use the
--title <text>arg if provided; otherwise the title candidate captured in Step 1 (the pre-syncHEADsubject). Do not re-readHEADhere — Step 1.5 may have added adocs(spec): sync to codecommit on top of it. - If a Jira ticket was resolved and the title does not already start with it, prefix:
[TICKET-ID] <title>.
Description (the PR's body) — fill one PR template. Azure DevOps applies a repo's PR template only when a PR is created in the web UI, not through the API, so this skill applies it itself.
- Choose the template. Look in the target branch for the first file that exists, and read it with
git show origin/<target-branch>:<path>:<folder>/pull_request_template/branches/<target-branch>.md, for<folder>in.azuredevops,.vsts,docsand the repo root, in that order (a template for one target branch);<folder>/pull_request_template.md(also.txt), for the same folders in the same order;- no repo template → the shared template
references/pull_request_template.mdof the generate-pr-notes skill (for example~/.claude/skills/generate-pr-notes/; install it withnpx skills add jamesyang124/agent-skills --skill generate-pr-notes -g). State which template was used in the Step 7 report.
Fill it with a sub-agent. Use the Agent tool (Task in older harnesses) with
subagent_type="general-purpose". Author its prompt to:- Fill the chosen template (pass its full text) from the diff
origin/<target-branch>...HEAD(three-dot). Keep its headings, their order and every checklist item word for word, and add no sections. - Follow generate-pr-notes step 4 for how to fill each section (title length, at most 5 changes, API routes and their classification, constitution check, ticking a checklist item only when the diff shows it is true). The skill is the dependency for these rules; a repo template simply has its own set of sections.
- Use these facts: Jira ticket =
<resolved ticket>or none; Jira URL ={{JIRA_BASE_URL}}TICKET-IDfor the template'sJira:line (or the first line of the first section when the template has none); spec docs changed or synced in Step 1.5; the Step 1.7testsfindings and API / constitution findings. - Return ONLY the filled markdown body — no
```markdownfence, no commentary. Skip generate-pr-notes' interactive questions and its/tmpwrite,unameand clipboard steps.
Resolve
JIRA_BASE_URLfrom.github/config/pr-tools.json(keyjiraBaseUrl) → defaulthttps://your-org.atlassian.net/browse/.- Fill the chosen template (pass its full text) from the diff
- If the returned body is unexpectedly fenced, strip only a leading line exactly equal to
```markdownand a final line exactly equal to```. Do not remove first/last lines unconditionally. - Pre-PR review section — if Step 1.7 ran, append it as the last section. Keep it under 600 characters:
list at most 3 unresolved findings and give the count of the rest.
## Pre-PR Review Confidence: **Low** — human review required (unresolved critical in auth middleware) Complexity: high (4) — new route, migration, background job, new env var Reviewers: code, security, standards, tests, minimal + lint, lizard · fixed 5 · unresolved 2 - 🔴 src/auth/session.go:88 — token not checked for expiry (security) - 🟡 api/rooms.go:140 — no test for an empty filter (tests)
Hard limit — 4000 characters. repo_pull_request_write (action: "create") rejects a description longer
than 4000 chars. Before Step 5:
- Shorten prose and sub-bullets first (Summary sentences, then Changes sub-bullets, then evidence sub-bullets).
Never drop a template heading, a checklist item, the
Jira:line or the## Pre-PR Reviewsection. - Final unconditional clamp: if still over 4000 chars, cut the text before
## Pre-PR Reviewand append…(truncated)at the cut, so the body is at most 4000 chars and the review section stays whole.
Fallback — if the sub-agent cannot be run, send the chosen template with only the title and the Jira: line
filled and every checkbox unticked, plus a summary of git diff --stat origin/<target-branch>...HEAD, and tell the
user to complete it. The title is the title candidate captured in Step 1 (not a fresh git log, which Step 1.5
may have topped with docs(spec): sync to code). Still capped at 4000 chars.
Step 5 — Create the PR
Call repo_pull_request_write with:
action:"create"repositoryId: the repo ID from Step 3project: the resolved projectsourceRefName:refs/heads/<current-branch>(full ref form is required)targetRefName:refs/heads/<target-branch>(full ref form is required)title: the title from Step 4description: the ≤4000-char body from Step 4isDraft:trueonly when thedraftarg was passed; otherwise omit (defaults tofalse)
Capture the returned pullRequestId. The PR now exists — everything in Step 6 is best-effort and must never surface as a failure of the whole skill (always reach Step 7 with the PR id/URL).
Step 6 — Add reviewers (best-effort)
- Load the PR tools config:
.github/config/pr-tools.jsonin the consuming repo (keys and an example:references/pr-tools.example.json). Guard the read so a missing or malformed file degrades to "no config" (never aborts):- Team keys are the top-level keys under
teams. - If the file is not found, skip team expansion and use only explicit reviewer args; record that no config was found for the Step 7 report.
- Team keys are the top-level keys under
- Expand each requested team via
teams.<team-key>. If a named team key is absent from the config, record "team<key>not found — skipped" and continue. - Merge expanded team members with explicit reviewer args into one set; de-duplicate case-insensitively.
- String pre-filter — resolve the current user via
az account show --query user.name -o tsv(fallbackgit config user.email) and drop any case-insensitive string match from the set. This is a cheap first pass, not the sole exclusion (see step 6). - Resolve each remaining reviewer to an identity GUID via
core_get_identity_idswith{ searchFilter: <email-or-unique-name> }. It returns[{ id, displayName, descriptor }]— note there is no email field to match against, so use result-count logic:- exactly one identity returned → use its
id - zero or more than one → skip that reviewer and record it as unresolved/ambiguous. Do not guess.
- exactly one identity returned → use its
- Self-exclusion by GUID — resolve the current user's value (from step 4) with
core_get_identity_idstoo, and drop that GUID from the resolved reviewer id set. GUID equality is the reliable identity match; the org UPN and the config email are often different strings, so the string pre-filter alone is not enough. - Add the resolved GUIDs in one call:
repo_pull_request_writewith{ action: "update_reviewers", repositoryId, project, pullRequestId, reviewerIds: [<id>...], reviewerAction: "add" }. If that batched call fails, retry once per-id and record which ids succeeded/failed. If it still fails, report "PR created; adding reviewers failed:<error>— add them manually" — never abort.
Step 6.5 — Add the ticket to the release-notes page (best-effort)
Adds the Jira ticket to this repo's row in a Confluence release-notes page (for example
2026-10XX Release Notes or 2026-1019 Release Notes). The user picks the page
at runtime, because a PR can belong to the monthly page or to a specific release.
Skip this step when any of these is true: --no-release-notes was passed; --fast without --release-page=<id>; no Jira ticket was resolved in Step 1;
this run stopped at the existing-PR check in Step 1; the run is non-interactive / headless (nobody can pick a page).
Run scripts/confluence_ticket_log.py from this skill's folder. It needs the Confluence space, a phrase that the
release-notes page titles contain, and the heading above the table with one row per service. Set them in the
PR tools config (the releaseNotes key, see references/pr-tools.example.json) and pass them as --space, --title-contains and --heading on every call below.
Without this key, skip this step:
"releaseNotes": { "space": "ENG", "titleContains": "Release Notes", "heading": "Services" }- List pages:
python3 -I scripts/confluence_ticket_log.py --list. It prints the 5 most recently changed release-notes pages (id, last change date, title) and whether this month'sYYYY-MMXXpage exists. - Ask which page this PR belongs to. Options: each listed page; "Create this month's page" only when the list says it does not exist yet; "Skip".
Dry run on the chosen page, with
<repo>= the repo name from Step 3:- listed page:
python3 -I scripts/confluence_ticket_log.py --page-id <id> --service <repo> --ticket <TICKET-ID> - new month:
python3 -I scripts/confluence_ticket_log.py --create-month <YYYY-MM> --service <repo> --ticket <TICKET-ID>. It copies last month'sYYYY-MMXXpage (same parent, content and attachments kept, onlyMMXXchanged in the title; the sprint number is not changed).
It prints the exact storage it would insert, or that the ticket is already in that cell.
- listed page:
- Show the dry-run output and ask before writing. On approval, run the same command with
--apply. - Exit
2(page, table or service row not found) or any other error: report it and continue to Step 7. The PR already exists.
Never edit the page another way. Do not read the page with the Confluence MCP and write it back:
confluence_get_page returns a converted view even with convert_to_markdown=false (it turns <ac:image> into
<img>). Writing that view back breaks images, Jira macros and layout. The script reads and writes raw storage
through the REST API, inserts only one <li> with a Jira macro per new ticket, and checks that nothing else changed.
Step 7 — Report
The repo_pull_request_write create response has no web URL — construct it:
https://dev.azure.com/{org}/{project}/_git/{repo}/pullrequest/{pullRequestId}Then report:
- PR id and URL
- title
- source branch → target branch
- draft status
- reviewers added and reviewers skipped (with the reason for each)
- if no PR tools config was found and a team arg was given, note it (team args produced no reviewers)
- the PR template used (repo template path, or the shared generate-pr-notes template)
- the conflict warning from Step 1, if any (target moved {n} commits; conflicting files), and the rebase-first advice when findings sit in those files
- pre-PR review: the choice made in Step 1.7 (preview only stops here and reports the path of the saved findings), the reviewers that ran (and any that did not finish), findings fixed and unresolved, and the review confidence level with its reasons and what a human reviewer should check (or "skipped" with the reason)
- release-notes page: the page and version written, "already present", skipped (with the reason), or the error. If a new month's page was created, say that its title still has last month's sprint number and must be updated by hand.
Notes
- Tool names here are the Azure DevOps MCP short names. The full name is the MCP server key plus the short name: with the
install-azure-devops-mcpserver keyazure-devops-mcp, Claude Code exposesmcp__azure-devops-mcp__<tool>(the names inallowed-tools). Each tool takes anaction(repo_repository:get;repo_pull_request:list;repo_pull_request_write:create,update_reviewers). - The Azure DevOps MCP is configured with the org at install time (
npx @azure-devops/mcp <org>); the tools infer it. The org is parsed from the git remote only to build the human-facing PR URL in Step 7. - Branch refs to
repo_pull_request_write(action: "create") must be the fullrefs/heads/...form — short names are rejected. pullRequestIdis a number; the MCP coerces numeric strings, but prefer passing the integer.- Pass the whole resolved
reviewerIdsarray in a singlereviewerAction: "add"call; only fan out per-id as the retry path when the batch fails.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

