[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6CEAF6DC7X4V00VCHPFMRHW":3,"skill-same:01M6CEAF6DC7X4V00VCHPFMRHW":120},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":32,"linkOnly":33,"markdownContent":34,"markdownBody":35,"frontMatter":36,"refName":37,"commitSha":38,"skillDirectory":39,"forksCount":40,"repositoryHtmlUrl":41,"repositoryHomepageUrl":6,"ownerHtmlUrl":42,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":43,"install":44,"resources":57},"01M6CEAF6DC7X4V00VCHPFMRHW","merge-pr",null,"Merge a GitHub PR via squash after \u002Fprepare-pr. Use when asked to merge a ready PR. Do not push to main or modify code. Ensure the PR ends in MERGED state and clean up worktrees after success.","LongIV999\u002FOpenclawd","LongIV999","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F92411512?v=4","01M6CEACAHRWWFRVKBQPSQ2WBM","01M6CEACEJDYKGDKD1P6A7J7VV",0,"MIT","https:\u002F\u002Fgithub.com\u002FLongIV999\u002FOpenclawd\u002Fblob\u002Fmain\u002F.agents\u002Farchive\u002Fmerge-pr-v1\u002FSKILL.md",[],[18,19,20,21,22,23,24,25,26,27,28,29,30,31],"merge","pr","merge pr","github","prepare","code","state","push","squash","use","ready","up","worktrees","clean","2026-11-10 10:12:30",false,"---\nname: merge-pr\ndescription: Merge a GitHub PR via squash after \u002Fprepare-pr. Use when asked to merge a ready PR. Do not push to main or modify code. Ensure the PR ends in MERGED state and clean up worktrees after success.\n---\n\n# Merge PR\n\n## Overview\n\nMerge a prepared PR via deterministic squash merge (`--match-head-commit` + explicit co-author trailer), then clean up the worktree after success.\n\n## Inputs\n\n- Ask for PR number or URL.\n- If missing, use `.local\u002Fprep.env` from the worktree if present.\n- If ambiguous, ask.\n\n## Safety\n\n- Use `gh pr merge --squash` as the only path to `main`.\n- Do not run `git push` at all during merge.\n- Do not use `gh pr merge --auto` for maintainer landings.\n- Do not run gateway stop commands. Do not kill processes. Do not touch port 18792.\n\n## Execution Rule\n\n- Execute the workflow. Do not stop after printing the TODO checklist.\n- If delegating, require the delegate to run commands and capture outputs.\n\n## Known Footguns\n\n- If you see \"fatal: not a git repository\", you are in the wrong directory. Move to the repo root and retry.\n- Read `.local\u002Freview.md`, `.local\u002Fprep.md`, and `.local\u002Fprep.env` in the worktree. Do not skip.\n- Always merge with `--match-head-commit \"$PREP_HEAD_SHA\"` to prevent racing stale or changed heads.\n- Clean up `.worktrees\u002Fpr-\u003CPR>` only after confirmed `MERGED`.\n\n## Completion Criteria\n\n- Ensure `gh pr merge` succeeds.\n- Ensure PR state is `MERGED`, never `CLOSED`.\n- Record the merge SHA.\n- Leave a PR comment with merge SHA and prepared head SHA, and capture the comment URL.\n- Run cleanup only after merge success.\n\n## First: Create a TODO Checklist\n\nCreate a checklist of all merge steps, print it, then continue and execute the commands.\n\n## Setup: Use a Worktree\n\nUse an isolated worktree for all merge work.\n\n```sh\nrepo_root=$(git rev-parse --show-toplevel)\ncd \"$repo_root\"\ngh auth status\n\nWORKTREE_DIR=\".worktrees\u002Fpr-\u003CPR>\"\ncd \"$WORKTREE_DIR\"\n```\n\nRun all commands inside the worktree directory.\n\n## Load Local Artifacts (Mandatory)\n\nExpect these files from earlier steps:\n\n- `.local\u002Freview.md` from `\u002Freview-pr`\n- `.local\u002Fprep.md` from `\u002Fprepare-pr`\n- `.local\u002Fprep.env` from `\u002Fprepare-pr`\n\n```sh\nls -la .local || true\n\nfor required in .local\u002Freview.md .local\u002Fprep.md .local\u002Fprep.env; do\n  if [ ! -f \"$required\" ]; then\n    echo \"Missing $required. Stop and run \u002Freview-pr then \u002Fprepare-pr.\"\n    exit 1\n  fi\ndone\n\nsed -n '1,120p' .local\u002Freview.md\nsed -n '1,120p' .local\u002Fprep.md\nsource .local\u002Fprep.env\n```\n\n## Steps\n\n1. Identify PR meta and verify prepared SHA still matches\n\n```sh\npr_meta_json=$(gh pr view \u003CPR> --json number,title,state,isDraft,author,headRefName,headRefOid,baseRefName,headRepository,body)\nprintf '%s\\n' \"$pr_meta_json\" | jq '{number,title,state,isDraft,author:.author.login,head:.headRefName,headSha:.headRefOid,base:.baseRefName,headRepo:.headRepository.nameWithOwner,body}'\npr_title=$(printf '%s\\n' \"$pr_meta_json\" | jq -r .title)\npr_number=$(printf '%s\\n' \"$pr_meta_json\" | jq -r .number)\npr_head_sha=$(printf '%s\\n' \"$pr_meta_json\" | jq -r .headRefOid)\ncontrib=$(printf '%s\\n' \"$pr_meta_json\" | jq -r .author.login)\nis_draft=$(printf '%s\\n' \"$pr_meta_json\" | jq -r .isDraft)\n\nif [ \"$is_draft\" = \"true\" ]; then\n  echo \"ERROR: PR is draft. Stop and run \u002Fprepare-pr after draft is cleared.\"\n  exit 1\nfi\n\nif [ \"$pr_head_sha\" != \"$PREP_HEAD_SHA\" ]; then\n  echo \"ERROR: PR head changed after \u002Fprepare-pr (expected $PREP_HEAD_SHA, got $pr_head_sha). Re-run \u002Fprepare-pr.\"\n  exit 1\nfi\n```\n\n2. Run sanity checks\n\nStop if any are true:\n\n- PR is a draft.\n- Required checks are failing.\n- Branch is behind main.\n\nIf checks are pending, wait for completion before merging. Do not use `--auto`.\nIf no required checks are configured, continue.\n\n```sh\ngh pr checks \u003CPR> --required --watch --fail-fast || true\nchecks_json=$(gh pr checks \u003CPR> --required --json name,bucket,state 2>\u002Ftmp\u002Fgh-checks.err || true)\nif [ -z \"$checks_json\" ]; then\n  checks_json='[]'\nfi\nrequired_count=$(printf '%s\\n' \"$checks_json\" | jq 'length')\nif [ \"$required_count\" -eq 0 ]; then\n  echo \"No required checks configured for this PR.\"\nfi\nprintf '%s\\n' \"$checks_json\" | jq -r '.[] | \"\\(.bucket)\\t\\(.name)\\t\\(.state)\"'\n\nfailed_required=$(printf '%s\\n' \"$checks_json\" | jq '[.[] | select(.bucket==\"fail\")] | length')\npending_required=$(printf '%s\\n' \"$checks_json\" | jq '[.[] | select(.bucket==\"pending\")] | length')\nif [ \"$failed_required\" -gt 0 ]; then\n  echo \"Required checks are failing, run \u002Fprepare-pr.\"\n  exit 1\nfi\nif [ \"$pending_required\" -gt 0 ]; then\n  echo \"Required checks are still pending, retry \u002Fmerge-pr when green.\"\n  exit 1\nfi\n\ngit fetch origin main\ngit fetch origin pull\u002F\u003CPR>\u002Fhead:pr-\u003CPR> --force\ngit merge-base --is-ancestor origin\u002Fmain pr-\u003CPR> || (echo \"PR branch is behind main, run \u002Fprepare-pr\" && exit 1)\n```\n\nIf anything is failing or behind, stop and say to run `\u002Fprepare-pr`.\n\n3. Merge PR with explicit attribution metadata\n\n```sh\nreviewer=$(gh api user --jq .login)\nreviewer_id=$(gh api user --jq .id)\ncoauthor_email=${COAUTHOR_EMAIL:-\"$contrib@users.noreply.github.com\"}\nif [ -z \"$coauthor_email\" ] || [ \"$coauthor_email\" = \"null\" ]; then\n  contrib_id=$(gh api users\u002F$contrib --jq .id)\n  coauthor_email=\"${contrib_id}+${contrib}@users.noreply.github.com\"\nfi\n\ngh_email=$(gh api user --jq '.email \u002F\u002F \"\"' || true)\ngit_email=$(git config user.email || true)\nmapfile -t reviewer_email_candidates \u003C \u003C(\n  printf '%s\\n' \\\n    \"$gh_email\" \\\n    \"$git_email\" \\\n    \"${reviewer_id}+${reviewer}@users.noreply.github.com\" \\\n    \"${reviewer}@users.noreply.github.com\" | awk 'NF && !seen[$0]++'\n)\n[ \"${#reviewer_email_candidates[@]}\" -gt 0 ] || { echo \"ERROR: could not resolve reviewer author email\"; exit 1; }\nreviewer_email=\"${reviewer_email_candidates[0]}\"\n\ncat > .local\u002Fmerge-body.txt \u003C\u003CEOF\nMerged via \u002Freview-pr -> \u002Fprepare-pr -> \u002Fmerge-pr.\n\nPrepared head SHA: $PREP_HEAD_SHA\nCo-authored-by: $contrib \u003C$coauthor_email>\nCo-authored-by: $reviewer \u003C$reviewer_email>\nReviewed-by: @$reviewer\nEOF\n\nrun_merge() {\n  local email=\"$1\"\n  local stderr_file\n  stderr_file=$(mktemp)\n  if gh pr merge \u003CPR> \\\n    --squash \\\n    --delete-branch \\\n    --match-head-commit \"$PREP_HEAD_SHA\" \\\n    --author-email \"$email\" \\\n    --subject \"$pr_title (#$pr_number)\" \\\n    --body-file .local\u002Fmerge-body.txt \\\n    2> >(tee \"$stderr_file\" >&2)\n  then\n    rm -f \"$stderr_file\"\n    return 0\n  fi\n  merge_err=$(cat \"$stderr_file\")\n  rm -f \"$stderr_file\"\n  return 1\n}\n\nmerge_err=\"\"\nselected_merge_author_email=\"$reviewer_email\"\nif ! run_merge \"$selected_merge_author_email\"; then\n  if printf '%s\\n' \"$merge_err\" | rg -qi 'author.?email|email.*associated|associated.*email|invalid.*email' && [ \"${#reviewer_email_candidates[@]}\" -ge 2 ]; then\n    selected_merge_author_email=\"${reviewer_email_candidates[1]}\"\n    echo \"Retrying once with fallback author email: $selected_merge_author_email\"\n    run_merge \"$selected_merge_author_email\" || { echo \"ERROR: merge failed after fallback retry\"; exit 1; }\n  else\n    echo \"ERROR: merge failed\"\n    exit 1\n  fi\nfi\n```\n\nRetry is allowed exactly once when the error is clearly author-email validation.\n\n4. Verify PR state and capture merge SHA\n\n```sh\nstate=$(gh pr view \u003CPR> --json state --jq .state)\nif [ \"$state\" != \"MERGED\" ]; then\n  echo \"Merge not finalized yet (state=$state), waiting up to 15 minutes...\"\n  for _ in $(seq 1 90); do\n    sleep 10\n    state=$(gh pr view \u003CPR> --json state --jq .state)\n    if [ \"$state\" = \"MERGED\" ]; then\n      break\n    fi\n  done\nfi\n\nif [ \"$state\" != \"MERGED\" ]; then\n  echo \"ERROR: PR state is $state after waiting. Leave worktree and retry \u002Fmerge-pr later.\"\n  exit 1\nfi\n\nmerge_sha=$(gh pr view \u003CPR> --json mergeCommit --jq '.mergeCommit.oid')\nif [ -z \"$merge_sha\" ] || [ \"$merge_sha\" = \"null\" ]; then\n  echo \"ERROR: merge commit SHA missing.\"\n  exit 1\nfi\n\ncommit_body=$(gh api repos\u002F:owner\u002F:repo\u002Fcommits\u002F$merge_sha --jq .commit.message)\ncontrib=${contrib:-$(gh pr view \u003CPR> --json author --jq .author.login)}\nreviewer=${reviewer:-$(gh api user --jq .login)}\nprintf '%s\\n' \"$commit_body\" | rg -q \"^Co-authored-by: $contrib \u003C\" || { echo \"ERROR: missing PR author co-author trailer\"; exit 1; }\nprintf '%s\\n' \"$commit_body\" | rg -q \"^Co-authored-by: $reviewer \u003C\" || { echo \"ERROR: missing reviewer co-author trailer\"; exit 1; }\n\necho \"merge_sha=$merge_sha\"\n```\n\n5. PR comment\n\nUse a multiline heredoc with interpolation enabled.\n\n```sh\nok=0\ncomment_output=\"\"\nfor _ in 1 2 3; do\n  if comment_output=$(gh pr comment \u003CPR> -F - \u003C\u003CEOF\nMerged via squash.\n\n- Prepared head SHA: $PREP_HEAD_SHA\n- Merge commit: $merge_sha\n\nThanks @$contrib!\nEOF\n); then\n    ok=1\n    break\n  fi\n  sleep 2\ndone\n\n[ \"$ok\" -eq 1 ] || { echo \"ERROR: failed to post PR comment after retries\"; exit 1; }\ncomment_url=$(printf '%s\\n' \"$comment_output\" | rg -o 'https:\u002F\u002Fgithub.com\u002F[^ ]+\u002Fpull\u002F[0-9]+#issuecomment-[0-9]+' -m1 || true)\n[ -n \"$comment_url\" ] || comment_url=\"unresolved\"\necho \"comment_url=$comment_url\"\n```\n\n6. Clean up worktree only on success\n\nRun cleanup only if step 4 returned `MERGED`.\n\n```sh\ncd \"$repo_root\"\ngit worktree remove \".worktrees\u002Fpr-\u003CPR>\" --force\ngit branch -D temp\u002Fpr-\u003CPR> 2>\u002Fdev\u002Fnull || true\ngit branch -D pr-\u003CPR> 2>\u002Fdev\u002Fnull || true\ngit branch -D pr-\u003CPR>-prep 2>\u002Fdev\u002Fnull || true\n```\n\n## Guardrails\n\n- Worktree only.\n- Do not close PRs.\n- End in MERGED state.\n- Clean up only after merge success.\n- Never push to main. Use `gh pr merge --squash` only.\n- Do not run `git push` at all in this command.\n","# Merge PR\n\n## Overview\n\nMerge a prepared PR via deterministic squash merge (`--match-head-commit` + explicit co-author trailer), then clean up the worktree after success.\n\n## Inputs\n\n- Ask for PR number or URL.\n- If missing, use `.local\u002Fprep.env` from the worktree if present.\n- If ambiguous, ask.\n\n## Safety\n\n- Use `gh pr merge --squash` as the only path to `main`.\n- Do not run `git push` at all during merge.\n- Do not use `gh pr merge --auto` for maintainer landings.\n- Do not run gateway stop commands. Do not kill processes. Do not touch port 18792.\n\n## Execution Rule\n\n- Execute the workflow. Do not stop after printing the TODO checklist.\n- If delegating, require the delegate to run commands and capture outputs.\n\n## Known Footguns\n\n- If you see \"fatal: not a git repository\", you are in the wrong directory. Move to the repo root and retry.\n- Read `.local\u002Freview.md`, `.local\u002Fprep.md`, and `.local\u002Fprep.env` in the worktree. Do not skip.\n- Always merge with `--match-head-commit \"$PREP_HEAD_SHA\"` to prevent racing stale or changed heads.\n- Clean up `.worktrees\u002Fpr-\u003CPR>` only after confirmed `MERGED`.\n\n## Completion Criteria\n\n- Ensure `gh pr merge` succeeds.\n- Ensure PR state is `MERGED`, never `CLOSED`.\n- Record the merge SHA.\n- Leave a PR comment with merge SHA and prepared head SHA, and capture the comment URL.\n- Run cleanup only after merge success.\n\n## First: Create a TODO Checklist\n\nCreate a checklist of all merge steps, print it, then continue and execute the commands.\n\n## Setup: Use a Worktree\n\nUse an isolated worktree for all merge work.\n\n```sh\nrepo_root=$(git rev-parse --show-toplevel)\ncd \"$repo_root\"\ngh auth status\n\nWORKTREE_DIR=\".worktrees\u002Fpr-\u003CPR>\"\ncd \"$WORKTREE_DIR\"\n```\n\nRun all commands inside the worktree directory.\n\n## Load Local Artifacts (Mandatory)\n\nExpect these files from earlier steps:\n\n- `.local\u002Freview.md` from `\u002Freview-pr`\n- `.local\u002Fprep.md` from `\u002Fprepare-pr`\n- `.local\u002Fprep.env` from `\u002Fprepare-pr`\n\n```sh\nls -la .local || true\n\nfor required in .local\u002Freview.md .local\u002Fprep.md .local\u002Fprep.env; do\n  if [ ! -f \"$required\" ]; then\n    echo \"Missing $required. Stop and run \u002Freview-pr then \u002Fprepare-pr.\"\n    exit 1\n  fi\ndone\n\nsed -n '1,120p' .local\u002Freview.md\nsed -n '1,120p' .local\u002Fprep.md\nsource .local\u002Fprep.env\n```\n\n## Steps\n\n1. Identify PR meta and verify prepared SHA still matches\n\n```sh\npr_meta_json=$(gh pr view \u003CPR> --json number,title,state,isDraft,author,headRefName,headRefOid,baseRefName,headRepository,body)\nprintf '%s\\n' \"$pr_meta_json\" | jq '{number,title,state,isDraft,author:.author.login,head:.headRefName,headSha:.headRefOid,base:.baseRefName,headRepo:.headRepository.nameWithOwner,body}'\npr_title=$(printf '%s\\n' \"$pr_meta_json\" | jq -r .title)\npr_number=$(printf '%s\\n' \"$pr_meta_json\" | jq -r .number)\npr_head_sha=$(printf '%s\\n' \"$pr_meta_json\" | jq -r .headRefOid)\ncontrib=$(printf '%s\\n' \"$pr_meta_json\" | jq -r .author.login)\nis_draft=$(printf '%s\\n' \"$pr_meta_json\" | jq -r .isDraft)\n\nif [ \"$is_draft\" = \"true\" ]; then\n  echo \"ERROR: PR is draft. Stop and run \u002Fprepare-pr after draft is cleared.\"\n  exit 1\nfi\n\nif [ \"$pr_head_sha\" != \"$PREP_HEAD_SHA\" ]; then\n  echo \"ERROR: PR head changed after \u002Fprepare-pr (expected $PREP_HEAD_SHA, got $pr_head_sha). Re-run \u002Fprepare-pr.\"\n  exit 1\nfi\n```\n\n2. Run sanity checks\n\nStop if any are true:\n\n- PR is a draft.\n- Required checks are failing.\n- Branch is behind main.\n\nIf checks are pending, wait for completion before merging. Do not use `--auto`.\nIf no required checks are configured, continue.\n\n```sh\ngh pr checks \u003CPR> --required --watch --fail-fast || true\nchecks_json=$(gh pr checks \u003CPR> --required --json name,bucket,state 2>\u002Ftmp\u002Fgh-checks.err || true)\nif [ -z \"$checks_json\" ]; then\n  checks_json='[]'\nfi\nrequired_count=$(printf '%s\\n' \"$checks_json\" | jq 'length')\nif [ \"$required_count\" -eq 0 ]; then\n  echo \"No required checks configured for this PR.\"\nfi\nprintf '%s\\n' \"$checks_json\" | jq -r '.[] | \"\\(.bucket)\\t\\(.name)\\t\\(.state)\"'\n\nfailed_required=$(printf '%s\\n' \"$checks_json\" | jq '[.[] | select(.bucket==\"fail\")] | length')\npending_required=$(printf '%s\\n' \"$checks_json\" | jq '[.[] | select(.bucket==\"pending\")] | length')\nif [ \"$failed_required\" -gt 0 ]; then\n  echo \"Required checks are failing, run \u002Fprepare-pr.\"\n  exit 1\nfi\nif [ \"$pending_required\" -gt 0 ]; then\n  echo \"Required checks are still pending, retry \u002Fmerge-pr when green.\"\n  exit 1\nfi\n\ngit fetch origin main\ngit fetch origin pull\u002F\u003CPR>\u002Fhead:pr-\u003CPR> --force\ngit merge-base --is-ancestor origin\u002Fmain pr-\u003CPR> || (echo \"PR branch is behind main, run \u002Fprepare-pr\" && exit 1)\n```\n\nIf anything is failing or behind, stop and say to run `\u002Fprepare-pr`.\n\n3. Merge PR with explicit attribution metadata\n\n```sh\nreviewer=$(gh api user --jq .login)\nreviewer_id=$(gh api user --jq .id)\ncoauthor_email=${COAUTHOR_EMAIL:-\"$contrib@users.noreply.github.com\"}\nif [ -z \"$coauthor_email\" ] || [ \"$coauthor_email\" = \"null\" ]; then\n  contrib_id=$(gh api users\u002F$contrib --jq .id)\n  coauthor_email=\"${contrib_id}+${contrib}@users.noreply.github.com\"\nfi\n\ngh_email=$(gh api user --jq '.email \u002F\u002F \"\"' || true)\ngit_email=$(git config user.email || true)\nmapfile -t reviewer_email_candidates \u003C \u003C(\n  printf '%s\\n' \\\n    \"$gh_email\" \\\n    \"$git_email\" \\\n    \"${reviewer_id}+${reviewer}@users.noreply.github.com\" \\\n    \"${reviewer}@users.noreply.github.com\" | awk 'NF && !seen[$0]++'\n)\n[ \"${#reviewer_email_candidates[@]}\" -gt 0 ] || { echo \"ERROR: could not resolve reviewer author email\"; exit 1; }\nreviewer_email=\"${reviewer_email_candidates[0]}\"\n\ncat > .local\u002Fmerge-body.txt \u003C\u003CEOF\nMerged via \u002Freview-pr -> \u002Fprepare-pr -> \u002Fmerge-pr.\n\nPrepared head SHA: $PREP_HEAD_SHA\nCo-authored-by: $contrib \u003C$coauthor_email>\nCo-authored-by: $reviewer \u003C$reviewer_email>\nReviewed-by: @$reviewer\nEOF\n\nrun_merge() {\n  local email=\"$1\"\n  local stderr_file\n  stderr_file=$(mktemp)\n  if gh pr merge \u003CPR> \\\n    --squash \\\n    --delete-branch \\\n    --match-head-commit \"$PREP_HEAD_SHA\" \\\n    --author-email \"$email\" \\\n    --subject \"$pr_title (#$pr_number)\" \\\n    --body-file .local\u002Fmerge-body.txt \\\n    2> >(tee \"$stderr_file\" >&2)\n  then\n    rm -f \"$stderr_file\"\n    return 0\n  fi\n  merge_err=$(cat \"$stderr_file\")\n  rm -f \"$stderr_file\"\n  return 1\n}\n\nmerge_err=\"\"\nselected_merge_author_email=\"$reviewer_email\"\nif ! run_merge \"$selected_merge_author_email\"; then\n  if printf '%s\\n' \"$merge_err\" | rg -qi 'author.?email|email.*associated|associated.*email|invalid.*email' && [ \"${#reviewer_email_candidates[@]}\" -ge 2 ]; then\n    selected_merge_author_email=\"${reviewer_email_candidates[1]}\"\n    echo \"Retrying once with fallback author email: $selected_merge_author_email\"\n    run_merge \"$selected_merge_author_email\" || { echo \"ERROR: merge failed after fallback retry\"; exit 1; }\n  else\n    echo \"ERROR: merge failed\"\n    exit 1\n  fi\nfi\n```\n\nRetry is allowed exactly once when the error is clearly author-email validation.\n\n4. Verify PR state and capture merge SHA\n\n```sh\nstate=$(gh pr view \u003CPR> --json state --jq .state)\nif [ \"$state\" != \"MERGED\" ]; then\n  echo \"Merge not finalized yet (state=$state), waiting up to 15 minutes...\"\n  for _ in $(seq 1 90); do\n    sleep 10\n    state=$(gh pr view \u003CPR> --json state --jq .state)\n    if [ \"$state\" = \"MERGED\" ]; then\n      break\n    fi\n  done\nfi\n\nif [ \"$state\" != \"MERGED\" ]; then\n  echo \"ERROR: PR state is $state after waiting. Leave worktree and retry \u002Fmerge-pr later.\"\n  exit 1\nfi\n\nmerge_sha=$(gh pr view \u003CPR> --json mergeCommit --jq '.mergeCommit.oid')\nif [ -z \"$merge_sha\" ] || [ \"$merge_sha\" = \"null\" ]; then\n  echo \"ERROR: merge commit SHA missing.\"\n  exit 1\nfi\n\ncommit_body=$(gh api repos\u002F:owner\u002F:repo\u002Fcommits\u002F$merge_sha --jq .commit.message)\ncontrib=${contrib:-$(gh pr view \u003CPR> --json author --jq .author.login)}\nreviewer=${reviewer:-$(gh api user --jq .login)}\nprintf '%s\\n' \"$commit_body\" | rg -q \"^Co-authored-by: $contrib \u003C\" || { echo \"ERROR: missing PR author co-author trailer\"; exit 1; }\nprintf '%s\\n' \"$commit_body\" | rg -q \"^Co-authored-by: $reviewer \u003C\" || { echo \"ERROR: missing reviewer co-author trailer\"; exit 1; }\n\necho \"merge_sha=$merge_sha\"\n```\n\n5. PR comment\n\nUse a multiline heredoc with interpolation enabled.\n\n```sh\nok=0\ncomment_output=\"\"\nfor _ in 1 2 3; do\n  if comment_output=$(gh pr comment \u003CPR> -F - \u003C\u003CEOF\nMerged via squash.\n\n- Prepared head SHA: $PREP_HEAD_SHA\n- Merge commit: $merge_sha\n\nThanks @$contrib!\nEOF\n); then\n    ok=1\n    break\n  fi\n  sleep 2\ndone\n\n[ \"$ok\" -eq 1 ] || { echo \"ERROR: failed to post PR comment after retries\"; exit 1; }\ncomment_url=$(printf '%s\\n' \"$comment_output\" | rg -o 'https:\u002F\u002Fgithub.com\u002F[^ ]+\u002Fpull\u002F[0-9]+#issuecomment-[0-9]+' -m1 || true)\n[ -n \"$comment_url\" ] || comment_url=\"unresolved\"\necho \"comment_url=$comment_url\"\n```\n\n6. Clean up worktree only on success\n\nRun cleanup only if step 4 returned `MERGED`.\n\n```sh\ncd \"$repo_root\"\ngit worktree remove \".worktrees\u002Fpr-\u003CPR>\" --force\ngit branch -D temp\u002Fpr-\u003CPR> 2>\u002Fdev\u002Fnull || true\ngit branch -D pr-\u003CPR> 2>\u002Fdev\u002Fnull || true\ngit branch -D pr-\u003CPR>-prep 2>\u002Fdev\u002Fnull || true\n```\n\n## Guardrails\n\n- Worktree only.\n- Do not close PRs.\n- End in MERGED state.\n- Clean up only after merge success.\n- Never push to main. Use `gh pr merge --squash` only.\n- Do not run `git push` at all in this command.\n",{"name":5,"description":7},"main","56e72bb5549c5fa0a0470501619427b4f104aa92",".agents\u002Farchive\u002Fmerge-pr-v1",1,"https:\u002F\u002Fgithub.com\u002FLongIV999\u002FOpenclawd","https:\u002F\u002Fgithub.com\u002FLongIV999","2026-02-15 08:26:56",{"source":8,"skillName":5,"commands":45,"prompt":55,"zipAvailable":56},[46,49,52],{"tool":47,"command":48},"npx","npx skills add LongIV999\u002FOpenclawd --skill merge-pr",{"tool":50,"command":51},"bunx","bunx skills add LongIV999\u002FOpenclawd --skill merge-pr",{"tool":53,"command":54},"pnpm","pnpm dlx skills add LongIV999\u002FOpenclawd --skill merge-pr","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002FLongIV999\u002FOpenclawd\u002Fblob\u002Fmain\u002F.agents\u002Farchive\u002Fmerge-pr-v1\u002FSKILL.md",true,[58,64,70,74,78,82,86,90,94,98,104,108,112,116],{"path":59,"resourceType":60,"resourceTypeDesc":61,"sizeBytes":62,"sourceHtmlUrl":63},"agents\u002Fopenai.yaml","REFERENCE","参考文档",174,"https:\u002F\u002Fgithub.com\u002FLongIV999\u002FOpenclawd\u002Fblob\u002Fmain\u002F.agents\u002Farchive\u002Fmerge-pr-v1\u002Fagents\u002Fopenai.yaml",{"path":65,"resourceType":66,"resourceTypeDesc":67,"sizeBytes":68,"sourceHtmlUrl":69},"assets\u002Fsizing-report-template.md","ASSET","资产文件",2352,"https:\u002F\u002Fgithub.com\u002Fngocsangyem\u002FMeowKit\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fstory-sizer\u002Fassets\u002Fsizing-report-template.md",{"path":71,"resourceType":60,"resourceTypeDesc":61,"sizeBytes":72,"sourceHtmlUrl":73},"references\u002Fauto-create-execution.md",5155,"https:\u002F\u002Fgithub.com\u002Fngocsangyem\u002FMeowKit\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fstory-sizer\u002Freferences\u002Fauto-create-execution.md",{"path":75,"resourceType":60,"resourceTypeDesc":61,"sizeBytes":76,"sourceHtmlUrl":77},"references\u002Fauto-create-gating.md",5151,"https:\u002F\u002Fgithub.com\u002Fngocsangyem\u002FMeowKit\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fstory-sizer\u002Freferences\u002Fauto-create-gating.md",{"path":79,"resourceType":60,"resourceTypeDesc":61,"sizeBytes":80,"sourceHtmlUrl":81},"references\u002Finjection-patterns.md",2159,"https:\u002F\u002Fgithub.com\u002Fngocsangyem\u002FMeowKit\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fstory-sizer\u002Freferences\u002Finjection-patterns.md",{"path":83,"resourceType":60,"resourceTypeDesc":61,"sizeBytes":84,"sourceHtmlUrl":85},"references\u002Finput-adapter.md",4020,"https:\u002F\u002Fgithub.com\u002Fngocsangyem\u002FMeowKit\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fstory-sizer\u002Freferences\u002Finput-adapter.md",{"path":87,"resourceType":60,"resourceTypeDesc":61,"sizeBytes":88,"sourceHtmlUrl":89},"references\u002Forchestration.md",3393,"https:\u002F\u002Fgithub.com\u002Fngocsangyem\u002FMeowKit\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fstory-sizer\u002Freferences\u002Forchestration.md",{"path":91,"resourceType":60,"resourceTypeDesc":61,"sizeBytes":92,"sourceHtmlUrl":93},"references\u002Freport-writer.md",3074,"https:\u002F\u002Fgithub.com\u002Fngocsangyem\u002FMeowKit\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fstory-sizer\u002Freferences\u002Freport-writer.md",{"path":95,"resourceType":60,"resourceTypeDesc":61,"sizeBytes":96,"sourceHtmlUrl":97},"references\u002Fsizing-heuristics.md",6535,"https:\u002F\u002Fgithub.com\u002Fngocsangyem\u002FMeowKit\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fstory-sizer\u002Freferences\u002Fsizing-heuristics.md",{"path":99,"resourceType":100,"resourceTypeDesc":101,"sizeBytes":102,"sourceHtmlUrl":103},"scripts\u002Fcheck-auto-create-gating.py","SCRIPT","脚本",8586,"https:\u002F\u002Fgithub.com\u002Fngocsangyem\u002FMeowKit\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fstory-sizer\u002Fscripts\u002Fcheck-auto-create-gating.py",{"path":105,"resourceType":100,"resourceTypeDesc":101,"sizeBytes":106,"sourceHtmlUrl":107},"scripts\u002Fexecute-auto-create.py",10791,"https:\u002F\u002Fgithub.com\u002Fngocsangyem\u002FMeowKit\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fstory-sizer\u002Fscripts\u002Fexecute-auto-create.py",{"path":109,"resourceType":100,"resourceTypeDesc":101,"sizeBytes":110,"sourceHtmlUrl":111},"scripts\u002Fparse-paste-stories.py",6488,"https:\u002F\u002Fgithub.com\u002Fngocsangyem\u002FMeowKit\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fstory-sizer\u002Fscripts\u002Fparse-paste-stories.py",{"path":113,"resourceType":100,"resourceTypeDesc":101,"sizeBytes":114,"sourceHtmlUrl":115},"scripts\u002Fscore-story.py",10934,"https:\u002F\u002Fgithub.com\u002Fngocsangyem\u002FMeowKit\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fstory-sizer\u002Fscripts\u002Fscore-story.py",{"path":117,"resourceType":100,"resourceTypeDesc":101,"sizeBytes":118,"sourceHtmlUrl":119},"scripts\u002Fwrite-sizing-report.py",9473,"https:\u002F\u002Fgithub.com\u002Fngocsangyem\u002FMeowKit\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fstory-sizer\u002Fscripts\u002Fwrite-sizing-report.py",[121,130,141,160,173,189],{"skillNo":122,"name":123,"nameZh":6,"description":6,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":124,"categories":125,"keywords":126,"publishedAt":32},"01M6CEFMR22ANX8BMWBY9J8VW3","youtube-to-obsidian","https:\u002F\u002Fgithub.com\u002FLongIV999\u002FOpenclawd\u002Fblob\u002Fmain\u002Fskills\u002Fyoutube-to-obsidian\u002FSKILL.md",[],[127,128,129],"youtube to obsidian","youtube","obsidian",{"skillNo":131,"name":132,"nameZh":6,"description":133,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":134,"categories":135,"keywords":136,"publishedAt":32},"01M6CEFJW8HMZ7EY2C7FXDN8HG","weather","Get current weather and forecasts (no API key required).","https:\u002F\u002Fgithub.com\u002FLongIV999\u002FOpenclawd\u002Fblob\u002Fmain\u002Fskills\u002Fweather\u002FSKILL.md",[],[132,137,138,139,140],"help","https","api","curl",{"skillNo":142,"name":143,"nameZh":6,"description":144,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":145,"categories":146,"keywords":147,"publishedAt":32},"01M6CEFGENXXNDQTCWVC82VBFT","wacli","Send WhatsApp messages to other people or search\u002Fsync WhatsApp history via the wacli CLI (not for normal user chats).","https:\u002F\u002Fgithub.com\u002FLongIV999\u002FOpenclawd\u002Fblob\u002Fmain\u002Fskills\u002Fwacli\u002FSKILL.md",[],[143,21,148,149,150,138,151,152,153,154,155,156,157,158,159],"cli","sync","search","messages","history","cmd","brew","user","install","tap","whatsapp","go",{"skillNo":161,"name":162,"nameZh":6,"description":163,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":164,"categories":165,"keywords":166,"publishedAt":32},"01M6CEFERBY58R1VW8Z8ZCYEMY","voice-call","Start voice calls via the OpenClaw voice-call plugin.","https:\u002F\u002Fgithub.com\u002FLongIV999\u002FOpenclawd\u002Fblob\u002Fmain\u002Fskills\u002Fvoice-call\u002FSKILL.md",[],[167,168,169,170,171,172],"voice","voice call","openclaw","plugin","plugins","start",{"skillNo":174,"name":175,"nameZh":6,"description":176,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":177,"categories":178,"keywords":184,"publishedAt":32},"01M6CEFD0C501CWXD9E4BMV2B2","video-frames","Extract frames or short clips from videos using ffmpeg.","https:\u002F\u002Fgithub.com\u002FLongIV999\u002FOpenclawd\u002Fblob\u002Fmain\u002Fskills\u002Fvideo-frames\u002FSKILL.md",[179],{"categoryNo":180,"categoryName":181,"taxonomyType":182,"taxonomyTypeDesc":183},"01JBSEEDCAT000000000000016","图像与多媒体","SCENARIO","使用场景",[185,186,154,138,187,188,156],"Video","video frames","short","extract",{"skillNo":190,"name":191,"nameZh":6,"description":192,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":193,"categories":194,"keywords":203,"publishedAt":32},"01M6CEFAJ48939CYW40VTBYGVP","trello","Manage Trello boards, lists, and cards via the Trello REST API.","https:\u002F\u002Fgithub.com\u002FLongIV999\u002FOpenclawd\u002Fblob\u002Fmain\u002Fskills\u002Ftrello\u002FSKILL.md",[195,198],{"categoryNo":196,"categoryName":197,"taxonomyType":182,"taxonomyTypeDesc":183},"01JBSEEDCAT000000000000010","后端与接口",{"categoryNo":199,"categoryName":200,"taxonomyType":201,"taxonomyTypeDesc":202},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[191,138,204,205,206,139,207,208,209],"cloud","developer","REST","jq","atlassian","manage"]