Sets up the boring but important files every new GitHub repo needs. Issue forms, PR template, CI, Dependabot, CodeQL, release notes, code owners, and git hooks — in one command.
npx reposet initMost repos don't rot because of bad code. They rot because nobody wrote down how to report a bug, what a PR is supposed to contain, which reviewers own which folder, or where CI lives. So every new project starts with the same hour of copying files from the last one.
This kit is that hour, done once, for any stack.
Nothing here is language-specific. Every file has TODO comments where a
decision is yours to make.
| Concern | Files |
|---|---|
| Issue intake | Bug + feature forms, blank issues disabled, contact links |
| PRs | Template with change type, test evidence, checklist |
| Community health | SECURITY.md, CONTRIBUTING.md, CODE_OF_CONDUCT.md, SUPPORT.md, FUNDING.yml |
| Ownership | CODEOWNERS with per-path reviewers |
| Dependencies | dependabot.yml with grouping, Actions enabled by default |
| CI | Workflow skeleton + language-agnostic hygiene job (conflict markers, CRLF) |
| Security | CodeQL scanning on push, PR, and weekly |
| Triage | Stale bot + path-based auto-labeling |
| Releases | Grouped auto-generated notes + tag→release workflow |
| Git hooks | Secret scan (gitleaks, with a built-in fallback), large-file block, Conventional Commits |
| Repo config | .editorconfig, .gitattributes, .gitignore |
| Scale-out | Reusable workflow_call CI for many repos |
npx reposet initThat writes all 11 default recipes into the current repository, never overwriting a file that already exists.
npx reposet init ../my-app # another folder
npx reposet init ../my-app --only pr-template,dependabot # cherry-pick
npx reposet init . --owner acme --repo widget --email dev@acme.dev
npx reposet check . # list leftover placeholders
npx reposet list # see everything
npx reposet init . --dry-run # plan only| Option | What it does |
|---|---|
--only <list> |
Recipe ids or names, comma separated (02,05 or pr-template,dependabot) |
--all |
Every recipe except 12 (this is the default) |
--force, -f |
Overwrite files that already exist |
--dry-run, -n |
Print the plan, write nothing |
--yes, -y |
Never prompt (for scripts and CI) |
--json |
Machine-readable result |
Placeholders are filled in every file init writes. Values come from the
target's git remote (owner/repo), GITHUB_REPOSITORY, git config user.email,
and GITHUB_ACTOR; override them when detection isn't right:
| Placeholder option | Fills |
|---|---|
--owner <name> |
OWNER / OWNER/REPO |
--repo <name> |
REPO / OWNER/REPO |
--user <handle> |
@your-username (defaults to the owner) |
--org <name> |
The commented your-org examples |
--email <addr> |
security@example.com and conduct@example.com |
--security-email <addr> |
Just the security contact |
--conduct-email <addr> |
Just the conduct contact |
--no-fill |
Leave every placeholder untouched |
npx reposet check . reports anything still left and exits 1 when a required
placeholder remains — useful as a CI gate.
The CLI needs Node 18+ and has zero dependencies — no lockfile, no install tree, ~70 KB unpacked.
- Fill any leftover placeholders —
OWNER/REPO,@your-username, the example emails.initfills them automatically when it can;npx reposet check .lists whatever remains (reference below). - Create the labels the templates reference — list below.
- If you took the hooks:
winget install evilmartians.lefthook && lefthook install. - Push to your default branch, then check Issues → New issue to confirm the forms render.
The CLI prints these steps for exactly the recipes you chose.
Every recipe folder mirrors your target repo's layout, so you copy its
contents, not the folder itself. Copying recipes/02-pr-template/ produces
my-repo/.github/pull_request_template.md.
# everything
Copy-Item "templates\generic\*" -Destination "C:\path\to\my-repo" -Recurse -Force
# or one concern
Copy-Item "recipes\02-pr-template\*" -Destination "C:\path\to\my-repo" -Recurse -Force# macOS / Linux
cp -r templates/generic/. /path/to/my-repo/No package to install, no config to generate, no lock-in — it's files you own outright.
| The obvious option | Where it stops | What this adds |
|---|---|---|
The green "Configure" button (actions/starter-workflows) |
Language CI only — nothing for issues, PRs, ownership, releases | One coherent set across 12 concerns, named consistently |
.gitignore generators |
Only .gitignore |
.editorconfig + .gitattributes, which kill CRLF and lockfile diff noise |
| Husky | Requires Node in every repo that uses it | Lefthook: one binary, any stack, no Node |
Hand-rolled .git/hooks |
Never reaches teammates — .git/ isn't committed |
lefthook install and committed config |
| gitleaks on its own | Excellent scanner, but you still have to wire and tune it | Wired into pre-commit: gitleaks when present, zero-dep pattern fallback when not |
| Contributor Covenant + docs | Authoritative text, but manual assembly | Assembled, cross-linked, with a response-time table |
| Dependabot settings UI | One ecosystem at a time, no grouping | Grouped configs for npm/pip/go/cargo/docker; Actions on by default |
- Tested on Windows 11 + Git for Windows 2.45 + PowerShell 5.1.
- Every YAML file parses; CI re-checks it on every push.
- Hook scripts are exercised in CI against valid and invalid commit messages, a leaked AWS key, and a 2 MB staged file.
- Confirmed that
shresolves inside git hooks on Windows (Git for Windows provides/usr/bin/sh), so Lefthook'srun: sh .githooks/...works even whenshis not on your PowerShellPATH. templates/generic/is generated fromrecipes/and CI fails on drift, so the two never disagree.- The CLI has a test suite (
node --test) covering selection, idempotent installs,--force,--dry-run,--json, placeholder filling andcheck, and usage errors — all run in CI. - CI packs the real npm tarball, extracts it, and installs from it, which caught
npm silently dropping
.gitignorefrom published packages (the CLI ships a packed twin and renames it back).
.
├── bin/
│ └── reposet.js ← CLI entry point (npx reposet)
├── src/ ← CLI: args, recipes, placeholders, installer
├── recipes/ ← source of truth: cherry-pick ONE concern at a time
│ ├── 01-issue-forms/
│ │ └── .github/ISSUE_TEMPLATE/...
│ ├── 02-pr-template/
│ │ └── .github/pull_request_template.md
│ └── ...
├── templates/
│ └── generic/ ← recipes 01–11 merged, ready to drop in whole
├── scripts/
│ ├── build-templates.ps1 ← regenerates templates/generic (maintainers)
│ └── pack-*.js ← work around npm dropping .gitignore
├── test/ ← CLI tests (node --test)
└── .github/workflows/
└── verify.yml ← this repo's CI (not copied to consumers)
Pass either the id or the name to --only: --only 2,05,root-files.
| # | Recipe | Take it when |
|---|---|---|
| 01 | issue-forms |
Any public repo with issues |
| 02 | pr-template |
Always — highest ROI file here |
| 03 | community-health |
Any repo other people touch |
| 04 | codeowners |
Teams, or protecting critical files |
| 05 | dependabot |
Always — Actions updates enabled by default |
| 06 | ci-basic |
Any repo that isn't CI'd yet |
| 07 | security-scan |
Public repos (free), or private with GHAS |
| 08 | stale-and-labeler |
Busy issue trackers |
| 09 | release-notes |
Anything you version |
| 10 | githooks-lefthook |
Local safety net before CI |
| 11 | root-files |
Always — prevents CRLF and encoding diffs |
| 12 | reusable-workflows |
3+ repos sharing one pipeline (select explicitly) |
02-pr-template— makes every future PR reviewable.11-root-files— kills line-ending and encoding diffs forever.05-dependabot— free dependency security updates.06-ci-basic— nothing merges untested.10-githooks-lefthook— stops secrets before they reach history.
01 — Issue forms · .github/ISSUE_TEMPLATE/
- Edit:
OWNER/REPOinconfig.yml(Discussions and private-advisory URLs). - Gotcha: labels in
labels: [...]must already exist, or they're silently skipped. Create them first. - Gotcha: each
bodyentry needs a uniqueid. blank_issues_enabled: falseforces people through a form; settrueto keep free-form issues.- GitHub validates this YAML on push — a typo makes the template vanish from the chooser. Verify at Issues → New issue.
02 — PR template · .github/pull_request_template.md
- Applies to every new PR automatically. No configuration.
- Keep it under ~40 lines or people delete it on sight.
- Multiple templates: move to
.github/PULL_REQUEST_TEMPLATE/bugfix.md, then append?template=bugfix.mdto the PR URL.
03 — Community health · .github/SECURITY.md etc.
- Edit:
OWNER/REPO,security@example.com,conduct@example.com. - GitHub surfaces these in Community Standards and links them from the new-issue chooser automatically.
FUNDING.ymlonly does something once a sponsorship platform is set up. Delete it if you don't want a Sponsor button.- The CoC is a summary that links the authoritative Contributor Covenant 2.1.
04 — CODEOWNERS · .github/CODEOWNERS
- Edit: every
@your-username. - Rules match bottom-up; the last match wins. Keep the catch-all
*first. - Works with users and org teams (
@org/team); teams require org-owned repos. - Invalid patterns show a warning under Settings → Branches → Code owners.
05 — Dependabot · .github/dependabot.yml
- Ships with only
github-actionsenabled — useful in every repo. Uncomment your stack (npm,pip,gomod,cargo,docker). groups:collapses updates into one PR per group instead of twenty noisy ones. This is what separates "useful" from "muted".- Requires Settings → Code security → Dependabot to be enabled.
directoryis the folder holding the manifest; monorepos need one entry each.
06 — CI skeleton · .github/workflows/ci.yml
- Two jobs:
build(withTODOsteps) andhygiene(fails on committed conflict markers or CRLF files). - Edit: uncomment one runtime setup, replace three
run:commands, delete the rest. concurrencycancels superseded runs — free speed on busy branches.permissions: contents: readis deliberate; add scopes per job.- The
hygienejob is safe to delete if it annoys you.
07 — CodeQL · .github/workflows/codeql.yml
- Enabled for the
actionslanguage by default; uncomment your languages. - Free for public repos. Private repos need GitHub Advanced Security.
- Weekly runs matter: they find new query results in old code.
- Compiled languages use the autobuild step; interpreted ones skip it.
08 — Stale + labeler · .github/workflows/ + .github/labeler.yml
- Edit: create the
stale,documentation,ci,dependencies,testslabels, or rename them in the config. - Tune
days-before-*-stalebefore enabling. 60/14 is gentle. pull_request_targetis used on purpose: fork PRs get a read-only token underpull_request, so labeling silently fails. This workflow never checks out PR code, so it avoids the dangerouspull_request_targetpattern.
09 — Release notes · .github/release.yml + workflow
release.yml(config) groups auto-generated notes by label. Edit the category labels to match what you actually use.- The workflow fires on
v*.*.*tags, builds, and creates a Release viagh. --generate-notesreads PR titles and labels between tags, so it's only as good as your PR hygiene.- Registry publishing is commented out and kept in a separate job, so a failed publish never blocks the release.
10 — Git hooks · lefthook.yml + .githooks/
Blocks, before the commit exists:
- Secrets — via gitleaks when it is
installed; the hook otherwise falls back to a dependency-free pattern list, so
you're never left unprotected. Both paths live in
.githooks/check-secrets.sh. - Large files — anything over 1 MiB (override with
LIMIT_BYTES=...). - Commit messages — Conventional Commits; merge/revert/fixup exempt.
winget install evilmartians.lefthook # or scoop / brew / npm i -D lefthook
lefthook install
lefthook run pre-commit # test itEscape hatches:
git commit --no-verify # skip hooks for this commit
LEFTHOOK=0 git commit -m "wip" # skip lefthook specificallyWhy Lefthook instead of a .git/hooks folder: .git/hooks is not committed,
so it never reaches teammates, and raw shell hooks break on Windows. Here you
commit a YAML file, everyone runs lefthook install, and the same hooks work
everywhere.
Keep pre-commit fast (formatters, secret scan). Put tests in pre-push.
11 — Root files · .editorconfig, .gitattributes, .gitignore
.gitattributesforcesLFin the repo, keeps.bat/.cmd/.ps1as CRLF, and marks lockfiles as generated so they stop wrecking diffs. This is the single best fix for "why does every file look changed on Windows?"..gitignorehas a safe base plus commented per-language sections..editorconfigis respected natively by VS Code, JetBrains, and most editors.
12 — Reusable workflows · workflow_call
- This is an alternative to recipe 06, not an addition. Copying both runs two
identical
CIworkflows. workflow_calldefines the pipeline once; callers pass inputs.- Across repos, the workflow's repo must be public (or same org with access).
Pin
@mainto a tag or SHA. - A called workflow cannot exceed the caller's
permissions.
Create these under Issues → Labels:
| Label | Used by |
|---|---|
bug, enhancement, needs-triage |
issue forms |
stale |
stale bot |
documentation, ci, dependencies, tests |
labeler |
breaking, breaking-change, feature, fix, performance |
release notes |
chore, refactor, build, test |
release notes |
skip-changelog |
release notes (excluded) |
pinned, security |
stale bot (exempt) |
gh label create "needs-triage" --color "fbca04"init fills these in every file it writes:
| Placeholder | Replaced with | Appears in |
|---|---|---|
OWNER/REPO |
owner/repo |
ISSUE_TEMPLATE/config.yml, SECURITY.md, SUPPORT.md, CONTRIBUTING.md |
OWNER, REPO |
owner / repo | the same URLs, plus cd REPO |
@your-username, your-username |
@handle / handle |
CODEOWNERS, FUNDING.yml, issue-form assignees |
security@example.com |
--email |
SECURITY.md |
conduct@example.com |
--email |
CODE_OF_CONDUCT.md |
your-org, your-org/your-repo |
optional (--org) |
commented examples in CODEOWNERS and reusable-ci.yml |
TODO: |
never touched | every workflow — deliberate |
Values are detected from the target's git remote (owner/repo),
GITHUB_REPOSITORY, git config user.email and GITHUB_ACTOR. Override with
--owner, --repo, --user, --org, --email, --security-email,
--conduct-email, or skip the whole step with --no-fill.
reposet check . scans only files the recipes install, prints each placeholder
with its file and line, and exits 1 when a required one remains. The commented
your-org examples are reported but do not fail the check.
- Never commit
.git/hooks/. It's local-only by design. --no-verifyskips hooks. Hooks are convenience, not a security boundary. Enforcement belongs in CI and branch protection.- Hooks run client-side. A determined contributor bypasses them. Server-side checks are the real gate.
pull_request_target+ checking out PR code = RCE. Only use it for metadata-only actions, as the labeler recipe does.- New repos default to read-only workflow tokens. A 403 means add the
specific scope to
permissions:, notwrite-all. paths/branchesfilters can silently skip a required check. If a job is required in branch protection, make sure it always runs.- Cron is UTC and often delayed under load. Don't rely on minute precision.
- Stuck? docs/troubleshooting.md covers hooks that
don't fire,
shon Windows, CodeQL on private repos, exit codes, and more.
Branch protection rules, required reviews, environment secrets, signed commits, and org-level policy. Those are settings and process, not files. This kit gets a repo to "well-kept"; it can't get it to "governed".
To give others a one-click "Use this template" button:
gh api -X PATCH repos/OWNER/REPO -f is_template=true
gh repo create my-new-project --template OWNER/REPOFor org-wide defaults, name a repo .github and push these files there —
GitHub falls back to it for every repo in the org. Add profile/README.md for
the org landing page.
recipes/ is the source of truth. templates/generic/ is generated:
# Windows: pass -ExecutionPolicy Bypass, or scripts may be disabled on your box.
powershell -NoProfile -ExecutionPolicy Bypass -File ./scripts/build-templates.ps1
powershell -NoProfile -ExecutionPolicy Bypass -File ./scripts/build-templates.ps1 -Check# macOS / Linux (PowerShell 6+)
pwsh -NoProfile -File ./scripts/build-templates.ps1 -CheckRules:
- One concern per recipe folder; the folder mirrors the target repo layout.
- No two recipes may provide the same path — the build script fails loudly.
12-reusable-workflowsis excluded from the merge because it replaces06.templates/generic/is never edited by hand.
Further reading:
- CONTRIBUTING.md — the ground rules, local setup, and how to add a recipe.
- docs/releasing.md — versioning and the publish checklist.
- docs/troubleshooting.md — fixes for common problems.
- SECURITY.md — how to report a vulnerability.
- CHANGELOG.md — what changed in each release.
npm login, thennpm publish.- Smoke-test:
npx reposet@latest init /tmp/scratch --dry-run.
repository, bugs, homepage and author in package.json already point at
n4od; update them if you move the repo.
npm pack runs scripts/pack-prepare.js / pack-clean.js, because npm drops
files named .gitignore from tarballs. Don't remove that workaround — the
packaging test in CI exists to catch it if it breaks.
MIT. The config files are meant to be copied; keep the notice in place and you're done.
Built by N4OD · github.com/n4od
If this saved you an hour, a ⭐ helps other people find it.