github-project — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited github-project (Plugin) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
GitHub platform configuration and repository management patterns. This skill focuses exclusively on GitHub-specific features.
New repo? Run this BEFORE the first PR:bash skills/github-project/scripts/init-branch-protection.sh OWNER/REPOAppliesrequired_conversation_resolution: true+ 1-approver baseline so the "abort merge if unresolved threads" rule is structurally enforced, not just documented. Seeskills/github-project/SKILL.md→ Required First Step After `gh repo create` for the full flow (baseline, required checks, merge queue).
This is an Agent Skill following the open standard originally developed by Anthropic and released for cross-platform use.
Supported Platforms:
Skills are portable packages of procedural knowledge that work across any AI agent supporting the Agent Skills specification.
This Skill Covers:
Delegate to Other Skills:
go-development, php-modernization, typo3-testingsecurity-auditenterprise-readinessgit-workflowSetup & Configuration:
Sub-Issues & Issue Hierarchies:
Troubleshooting (PR Merge Blocked):
gh pr merge returns errorTroubleshooting (Auto-Merge Failures):
Troubleshooting (Ruleset Conflicts):
Merge Queue Configuration:
Branch Migration:
Add the Netresearch marketplace once, then browse and install skills:
# Claude Code
/plugin marketplace add netresearch/claude-code-marketplace
/plugin install github-project@netresearch-claude-code-marketplaceSince Claude Code 2.1.157 a plugin directory under your personal skills directory loads on its own:
mkdir -p ~/.claude/skills
git clone https://github.com/netresearch/github-project-skill.git \
~/.claude/skills/github-projectIt loads as github-project@skills-dir on the next session. Update with git -C ~/.claude/skills/github-project pull and start a new session; remove it by deleting the directory. This route has no claude plugin update.
Install with any Agent Skills-compatible agent:
npx skills add https://github.com/netresearch/github-project-skill --skill github-projectDownload the latest release and extract to your agent's skills directory.
git clone https://github.com/netresearch/github-project-skill.gitcomposer require netresearch/github-project-skillRequires netresearch/composer-agent-skill-plugin.
npm install --save-dev \
@netresearch/agent-skill-coordinator \
github:netresearch/github-project-skillRequires @netresearch/agent-skill-coordinator, which discovers the skill in node_modules and registers it in AGENTS.md via a postinstall hook. For pnpm, also allowlist the coordinator's postinstall:
{
"pnpm": {
"onlyBuiltDependencies": ["@netresearch/agent-skill-coordinator"]
}
}To set up a new GitHub repository:
.github/ directory structure (see references/repository-structure.md)main branchRun verification: ./scripts/verify-github-project.sh /path/to/repo
Required branch settings:
| Setting | Value | Rationale |
|---|---|---|
| Default branch name | main | Industry standard |
| Default branch protected | ✅ | Prevent direct pushes |
| Allowed merge method | Merge commits only | Preserve complete history |
| Delete branch on merge | ✅ | Keep repo clean |
To configure via GitHub CLI:
# Ensure default branch is named "main"
gh api repos/{owner}/{repo} --jq '.default_branch'
# Configure merge settings (merge commits only, delete on merge)
gh repo edit --enable-merge-commit --disable-rebase-merge --disable-squash-merge --delete-branch-on-mergeNote: If you prefer rebase merging for linear history, use --enable-rebase-merge --disable-merge-commit instead. Ensure consistency with any GitHub Rulesets (see Rulesets Configuration).
Complete repository settings:
| Setting | Value | Rationale |
|---|---|---|
| Allow merge commits | ✅ | With PR title and description |
| Allow squash merging | ❌ | Preserve commit granularity |
| Allow rebase merging | ❌ | Use merge queue instead |
| Automatically delete head branches | ✅ | Keep repo clean |
| Allow auto-merge | ✅ | Enable merge queue integration |
| Always suggest updating PR branches | ✅ | Keep PRs up-to-date with base |
| Discussions | ✅ | Community Q&A |
| Wikis | ❌ | Use docs folder instead |
Configure via CLI:
# Configure all repository settings
gh repo edit \
--enable-merge-commit \
--disable-squash-merge \
--disable-rebase-merge \
--delete-branch-on-merge \
--enable-auto-merge \
--enable-discussions \
--disable-wiki
# Set merge commit message format (PR title and description)
gh api repos/{owner}/{repo} \
--method PATCH \
-f merge_commit_title=PR_TITLE \
-f merge_commit_message=PR_BODY
# Enable "always suggest updating PR branches"
gh api repos/{owner}/{repo} \
--method PATCH \
-F allow_update_branch=trueImportant: Do NOT enable required_linear_history when using merge commits. Linear history requires fast-forward merges only (no merge commits). If you see "not authorized to push" errors, check if required_linear_history is enabled and disable it.
For complete migration steps from master to main as default branch, see references/branch-migration.md.
Quick start:
git branch -m master main
git push -u origin main
gh repo edit --default-branch main
git push origin --delete masterTo configure branch protection via GitHub CLI:
# View current protection
gh api repos/{owner}/{repo}/branches/main/protection
# Set branch protection
gh api repos/{owner}/{repo}/branches/main/protection \
--method PUT \
-f required_status_checks='{"strict":true,"contexts":["test","lint"]}' \
-f enforce_admins=true \
-f required_pull_request_reviews='{"required_approving_review_count":1,"dismiss_stale_reviews":true,"require_code_owner_reviews":true}' \
-f restrictions=null \
-f required_conversation_resolution=trueRecommended Settings:
| Setting | Value | Purpose |
|---|---|---|
| Require pull request | ✅ | Enforce code review |
| Required approvals | 1+ | Based on team size |
| Dismiss stale reviews | ✅ | Re-review after changes |
| Require CODEOWNERS review | ✅ | Domain expert review |
| Require conversation resolution | ✅ | All comments addressed |
| Do not allow force pushes | ✅ | Protect history |
| Allow merge commits | ✅ | Preserve complete history |
| Disable rebase merge | ✅ | Avoid linear-only restriction |
| Disable squash merge | ✅ | Preserve commit granularity |
| Delete branch on merge | ✅ | Auto-cleanup merged branches |
GitHub Rulesets provide repository rules that can override or conflict with repository-level settings. Critical: The pull_request rule in rulesets has its own allowed_merge_methods setting that must match repository settings.
View existing rulesets:
# List all rulesets
gh api repos/{owner}/{repo}/rulesets
# Get specific ruleset details
gh api repos/{owner}/{repo}/rulesets/{ruleset_id}Common Issue: "Rebase is not an allowed merge method" Warning
This warning appears on PRs when there's a mismatch between:
allow_rebase_merge: false)allowed_merge_methods includes "rebase")Diagnosis:
# Check repository merge settings
gh api repos/{owner}/{repo} --jq '{allow_merge_commit, allow_rebase_merge, allow_squash_merge}'
# Check ruleset merge settings
gh api repos/{owner}/{repo}/rulesets/{ruleset_id} --jq '.rules[] | select(.type == "pull_request") | .parameters.allowed_merge_methods'
# Check merge queue settings
gh api repos/{owner}/{repo}/rulesets/{ruleset_id} --jq '.rules[] | select(.type == "merge_queue") | .parameters.merge_method'Fix: Update ruleset to match repository settings
A PUT replaces every field it sends as a whole: a rules array holding only the two changed rules deletes all other rules of the ruleset, and GitHub rejects a pull_request or merge_queue rule that does not carry all of its required parameters. Read the ruleset, change the two fields, and send the full rules array back. The REST reference marks no top-level field of the PUT as required; that a field left out (bypass_actors, conditions) is kept is not tested here, so read the ruleset back.
For merge commits only:
gh api repos/{owner}/{repo}/rulesets/{ruleset_id} --jq '{rules: [.rules[]
| if .type == "pull_request" then .parameters.allowed_merge_methods = ["merge"]
elif .type == "merge_queue" then .parameters.merge_method = "MERGE"
else . end
| if .parameters == null then del(.parameters) else . end]}' \
| gh api repos/{owner}/{repo}/rulesets/{ruleset_id} -X PUT --input -
# Read it back: the same rules and bypass actors as before, with the two fields changed
gh api repos/{owner}/{repo}/rulesets/{ruleset_id} --jq '{bypass_actors, conditions, rules}'Ruleset merge method alignment:
| Repository Setting | Ruleset allowed_merge_methods | Ruleset merge_queue.merge_method |
|---|---|---|
allow_merge_commit: true only | ["merge"] | "MERGE" |
allow_rebase_merge: true only | ["rebase"] | "REBASE" |
allow_squash_merge: true only | ["squash"] | "SQUASH" |
To enforce PR comment resolution:
IMPORTANT: "Addressing" review comments means BOTH:
The gh CLI does not support resolving review threads directly. Use GraphQL API.
Step 1: Get unresolved review threads
gh api graphql -f query='{
repository(owner: "OWNER", name: "REPO") {
pullRequest(number: NUMBER) {
reviewThreads(first: 50) {
nodes {
id
isResolved
path
line
comments(first: 1) {
nodes { body }
}
}
}
}
}
}'Step 2: Resolve each thread by ID
gh api graphql -f query='mutation {
resolveReviewThread(input: {threadId: "PRRT_xxxxxxxxxxxx"}) {
thread { isResolved }
}
}'Step 3: Verify all threads resolved
gh api graphql -f query='{
repository(owner: "OWNER", name: "REPO") {
pullRequest(number: NUMBER) {
reviewThreads(first: 50) {
nodes { id isResolved }
}
}
}
}' | jq '.data.repository.pullRequest.reviewThreads.nodes | map(select(.isResolved == false))'PR Completion Checklist:
To configure automatic reviewer assignment:
.github/CODEOWNERS# Default owners for everything
* @org/maintainers
# Directory ownership
/src/auth/ @org/security-team
/docs/ @org/docs-team
# File pattern ownership
*.sql @org/dba-team
/.github/ @org/maintainersTo configure automatic merging of dependency updates:
references/dependency-management.md)Auto-Merge Decision Matrix:
| Repository Configuration | Template | Merge Method |
|---|---|---|
| Merge queue enabled | auto-merge-queue.yml.template | GraphQL enqueuePullRequest |
| Branch protection (no queue) | auto-merge.yml.template | gh pr merge --auto |
| No branch protection | auto-merge-direct.yml.template | gh pr merge --rebase (direct) |
For repositories with merge queues enabled, the --auto flag and direct merge commands don't work. Use the GraphQL enqueuePullRequest mutation instead.
Key points:
mergeMethod parameter is NOT valid for enqueuePullRequest - merge method is set by queue configurationgithub.event.pull_request.node_id to get the PR's GraphQL node ID# .github/workflows/auto-merge-deps.yml
name: Auto-merge dependency PRs
on:
pull_request_target:
types: [opened, synchronize, reopened]
permissions:
contents: write
pull-requests: write
jobs:
auto-merge:
runs-on: ubuntu-latest
if: github.actor == 'dependabot[bot]' || github.actor == 'renovate[bot]'
steps:
- name: Approve PR
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh pr review --approve "$PR_URL"
- name: Add to merge queue
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NODE_ID: ${{ github.event.pull_request.node_id }}
run: |
gh api graphql -f query='
mutation($pullRequestId: ID!) {
enqueuePullRequest(input: {pullRequestId: $pullRequestId}) {
mergeQueueEntry { id }
}
}' -f pullRequestId="$PR_NODE_ID"For repositories without branch protection rules, the --auto flag fails with "Protected branch rules not configured". Use direct merge instead:
# .github/workflows/auto-merge-deps.yml
- name: Merge PR
env:
PR_URL: ${{ github.event.pull_request.html_url }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: gh pr merge --rebase "$PR_URL"When auto-merge is enabled but PRs aren't merging automatically:
Step 1: Check repo merge settings vs workflow merge method
# View current merge method settings
gh api repos/{owner}/{repo} --jq '{
allow_squash_merge,
allow_merge_commit,
allow_rebase_merge,
delete_branch_on_merge
}'Step 2: Identify merge method mismatch
| Workflow Uses | Repo Setting Required | Error Message |
|---|---|---|
gh pr merge --squash | allow_squash_merge: true | "Merge method squash merging is not allowed" |
gh pr merge --merge | allow_merge_commit: true | "Merge method merge commit is not allowed" |
gh pr merge --rebase | allow_rebase_merge: true | "Merge method rebase is not allowed" |
Step 3: Fix merge method alignment
Either update workflow to match repo settings (gh pr merge --rebase) or update repo:
gh api repos/{owner}/{repo} --method PATCH -f allow_rebase_merge=trueStep 4: Validate required status checks
Status check names must exactly match what workflows produce:
# Compare expected vs actual check names
gh api repos/{owner}/{repo}/branches/main/protection/required_status_checks --jq '.contexts[]'
gh pr checks <number> --json name --jq '.[].name'Common status check name mismatches:
| Expected | Actual | Issue |
|---|---|---|
Analyze (javascript-typescript) | Analyze (javascript) | Language detection |
build | Build / build | Workflow name prefix |
test | test (ubuntu-latest, 18) | Matrix parameters |
Step 5: Fix and re-trigger
# Update branch protection check names
gh api repos/{owner}/{repo}/branches/main/protection/required_status_checks \
--method PATCH -f strict=true --input - <<< '{"contexts":["actual-check-name"]}'
# Re-trigger stuck PRs
gh pr update-branch <number> --rebaseAuto-merge compatibility checklist:
| Check | Command | Expected |
|---|---|---|
| Merge method alignment | gh api repos/{owner}/{repo} --jq '.allow_rebase_merge' | Matches workflow flag |
| Status checks pass | gh pr checks <number> | All green |
| Status check names match | Compare gh pr checks vs branch protection | Exact match |
| Reviews complete | gh pr view <number> --json reviewDecision | APPROVED |
| No merge conflicts | gh pr view <number> --json mergeable | MERGEABLE |
| Auto-merge enabled | gh pr view <number> --json autoMergeRequest | Not null |
To enable Discussions:
Use Discussions for: Questions, ideas, announcements Use Issues for: Bug reports, confirmed features, actionable tasks
To configure automatic release notes:
.github/release.yml:changelog:
exclude:
authors: [dependabot, renovate]
categories:
- title: 🚀 Features
labels: [enhancement]
- title: 🐛 Bug Fixes
labels: [bug]
- title: 📚 Documentation
labels: [documentation]gh release create v1.0.0 --generate-notesGitHub's sub-issues feature enables parent-child relationships between issues (up to 8 levels, 100 sub-issues per parent). For complete GraphQL API reference, see references/sub-issues.md.
Important: The gh CLI does not support sub-issues directly. Use GraphQL API.
Quick reference:
# Get issue node ID
gh api graphql -f query='{repository(owner:"OWNER",name:"REPO"){issue(number:123){id}}}'
# Add sub-issue (requires node IDs)
gh api graphql -f query='mutation{addSubIssue(input:{issueId:"PARENT_ID",subIssueId:"CHILD_ID"}){issue{number}subIssue{number}}}'
# List sub-issues
gh api graphql -f query='{repository(owner:"OWNER",name:"REPO"){issue(number:123){subIssues(first:50){nodes{number title state}}}}}'When a PR cannot be merged, diagnose the cause:
Step 1: Check PR status
# View PR details including merge state
gh pr view <number> --json mergeable,mergeStateStatus,reviewDecision,statusCheckRollup
# Check for blocking issues
gh pr checks <number>Step 2: Identify the blocker
| Symptom | Cause | Resolution |
|---|---|---|
BLOCKED mergeStateStatus | Unresolved conversations | Resolve all review threads |
REVIEW_REQUIRED reviewDecision | Missing approvals | Request reviews from required reviewers |
CHANGES_REQUESTED reviewDecision | Changes requested | Address feedback, request re-review |
Failed checks in statusCheckRollup | CI/CD failures | Fix failing tests/lints |
CODEOWNERS review required | Missing code owner approval | Get approval from designated owners |
Step 3: Resolution actions
# For unresolved conversations: view and resolve threads
gh pr view <number> --comments
# For missing reviews: request specific reviewers
gh pr edit <number> --add-reviewer username
# For CODEOWNERS blocking: check which files need review
gh pr view <number> --json filesCommon `gh pr merge` errors:
| Error Message | Meaning | Fix |
|---|---|---|
Pull request is not mergeable | Branch protection blocking | Run diagnosis steps above |
Required status check "X" is expected | CI not run or pending | Wait for CI or trigger manually |
At least 1 approving review is required | No approvals yet | Request and obtain review |
Changes were made after the most recent approval | Stale approval | Request re-review |
Protected branch rules not configured | --auto requires branch protection | Use direct merge or enable branch protection |
InputObject 'EnqueuePullRequestInput' doesn't accept argument 'mergeMethod' | Invalid GraphQL parameter | Remove mergeMethod from mutation - it's set by queue config |
# Repository
gh repo view
gh repo edit --enable-discussions
# Issues
gh issue list
gh issue create
gh issue edit 123 --add-label "priority: high"
# Pull requests
gh pr list
gh pr create
gh pr review --approve
gh pr merge --squash
# Releases
gh release create v1.0.0 --generate-notes
# Labels
gh label create "name" --color "hex" --description "desc"
# Projects
gh project create --title "Project Name"| Resource | Purpose |
|---|---|
references/repository-structure.md | Standard files and directory layout |
references/dependency-management.md | Dependabot/Renovate and auto-merge patterns |
references/sub-issues.md | GitHub sub-issues GraphQL API |
references/branch-migration.md | Master to main migration guide |
assets/auto-merge.yml.template | Auto-merge with branch protection (--auto flag) |
assets/auto-merge-queue.yml.template | Auto-merge with merge queue (GraphQL mutation) |
assets/auto-merge-direct.yml.template | Auto-merge without branch protection |
scripts/verify-github-project.sh | Verification script for project setup |
This skill focuses on GitHub platform configuration. For complete project setup:
| Skill | Purpose |
|---|---|
go-development | Go code patterns, Makefile interface, testing, linting |
enterprise-readiness | OpenSSF Scorecard, SLSA provenance, signed releases, CI workflow templates |
git-workflow | Git branching strategies, conventional commits |
security-audit | Deep security audits (OWASP, CVE analysis) |
github-project (this skill)
├── Repository configuration
├── Branch protection / rulesets
├── Dependabot/Renovate auto-merge
└── Coordinates with:
├── go-development → CI workflow content (test, lint, build)
└── enterprise-readiness → Security workflows (Scorecard, CodeQL, SLSA)The behavioural tests live in tests/ and run offline; they need bash, git, jq, python3 and the usual coreutils, grep and sed:
bash tests/verify-github-project.sh # skills/github-project/scripts/verify-github-project.sh
bash tests/init-branch-protection.sh # skills/github-project/scripts/init-branch-protection.sh
bash tests/check-plugin-version.sh # Build/Scripts/check-plugin-version.sh and Build/hooks/pre-push
bash tests/checkpoint-gh-37.sh # checkpoint GH-37 in skills/github-project/checkpoints.yamltests/verify-github-project.sh runs the verifier against fixture directories; only the dotted-name fixture has a github.com origin, and the three remote fixtures run with a stub gh on PATH, so no fixture reaches the network. The stub checks that a non-GitHub remote and a host that merely contains github.com trigger no call and that a github.com remote whose name contains a dot is queried with its slug. It checks the exit codes, that all 12 sections run, and individual pass and failure lines, including the default-branch check in a git repository.tests/init-branch-protection.sh puts a stub gh first on PATH that answers from prepared responses and records every request and request body. It checks each exit code documented in the script header, the body sent with PUT (with and without --solo), that nothing is written over protection the script could not read, that drift is reported and not corrected, both --from-current-checks paths (with strict false under a merge queue), and --merge-queue: the ruleset body and its read-back, the strict PATCH, the allow_auto_merge PATCH and its read-back, the no-op on an existing standard queue, and each refusal, including a required Analyze (…) context under CodeQL default setup (allowed without default setup and as a workflow-prefixed codeql / Analyze (…)) and "app_id": -1 for unpinned contexts in the printed PATCH.tests/check-plugin-version.sh builds throwaway git repositories and checks that a semver tag at HEAD must match the version in .claude-plugin/plugin.json, and that the pre-push hook passes the result on.tests/checkpoint-gh-37.sh reads GH-37's script out of checkpoints.yaml and runs it in fixture directories: the org preset as github> or local> (with :preset or #ref, in JSON, JSONC or JSON5) and a repository without a Renovate config pass; config:recommended alone, the preset only in a comment, a description or ignorePresets, a look-alike repository name, a missing extends, and a preset in a later config file than the one Renovate reads fail.Each check prints ok or FAIL; a FAIL line names the expectation that was not met and is followed by the script's output. A test file exits 1 when any check failed. In CI, the Skill Tests workflow (.github/workflows/tests.yml) runs every tests/**/*.sh on each pull request and on pushes to main, and fails when the repository ships scripts under skills/*/scripts/ but no test ran.
The skill's Markdown and templates are not executed here; Skill Validation and Eval Validation check their structure and the eval definitions in skills/github-project/evals/evals.json. A pull request that adds or changes behaviour in a script adds or updates a check in tests/ that fails without the change.
This repository follows the Netresearch organisation policies:
The security assurance case for this skill (threat model, trust boundaries, countermeasures and limits) is in docs/SECURITY-ASSURANCE.md.
Checks that run on pull requests in this repository:
lint.yml: skill structure, markdownlint, yamllint, actionlint, JSON syntax, ShellCheck, ruff, checkpoint schema), Eval Validation (eval-validate.yml) and Skill Tests (tests.yml).main: security.yml with Betterleaks (secret scanning), zizmor (workflow static analysis), dependency review, Composer Audit and Opengrep SAST (both behind a preflight event-gate job), and CodeQL for actions; Harness Verification (harness-verify.yml) and Template Drift (check-template-drift.yml). The organisation's security policy sets when dependency review and Opengrep fail: see dependencies and static analysis (SAST).labeler.yml), the DCO sign-off check, SonarCloud Code Analysis and the CodeRabbit review status (GitHub Apps) and, for dependency-update pull requests, auto-merge (auto-merge-deps.yml).This project uses split licensing:
See the individual license files for full terms.
Made with ❤️ for Open Source by [Netresearch](https://www.netresearch.de/)
To check GitHub project configuration:
./skills/github-project/scripts/verify-github-project.sh /path/to/repositoryChecks: documentation files, CODEOWNERS, dependency management, issue/PR templates, auto-merge workflow, release configuration.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.