Utilities for reading structure/repos.json and operating on
OpenPhysics repositories. These scripts are intended for local use and for AI agents working in
the monorepo checkout.
jqghfor GitHub sync commands- Node.js +
npm install(in the repo root) for the screenshot and thumbnail scripts — installs Playwright and sharp
| Script | Purpose |
|---|---|
parse-repos.sh |
Core parser/CLI for repos.json |
list-repos.sh |
Human-friendly listing wrapper |
check-repos-catalog.sh |
Validate repos.json against structure/repos.schema.json + fleet invariants |
check-uncataloged.sh |
Detect org repos on GitHub that are missing from repos.json (catches un-onboarded repos) |
clone-fleet.sh |
Clone/update every catalog repo into the workspace as a sibling |
fleet |
Run a git command across every local checkout (fleet push, fleet status -s, …) |
fleet-exec.sh |
Run a command across many repos and open one PR each |
../doc/fleet-git.md |
Cheat sheet: everyday git across local checkouts (pull/push/status all) |
sync-github-metadata.sh |
Push description + website to GitHub |
sync-github-settings.sh |
Check/apply GitHub repo settings baseline (security + feature flags) |
../doc/github-repo-settings.md |
Documented GitHub settings baseline for sims |
create-sim.sh |
Bootstrap a new sim from SceneryStackTemplate (rename + N screens; --onboard applies the full baseline incl. GitHub settings; --existing adopts a repo already on GitHub; --pr / --shared-model) |
sync-claude-settings.sh |
Roll the scenerystack Claude Code plugin out to sim repos' .claude/settings.json |
lib/repos.sh |
Bash helper functions for other scripts |
check-repo-compliance.sh |
README/CI/structure compliance (bootstrap, i18n, memory-leak suite, KeyboardHelp, githooks, …) |
check-skills.sh |
Validate the skills/ collection and its README index (Baton self-check) |
check-node-version.sh |
Assert fleet Node major agrees across workflows; with sibling checkouts, also engines.node / @types/node |
sync-dependabot.sh |
Copy Dependabot configs from config/ to catalog npm/pip repos (see structure/repos.json) |
generate-pages-index.sh |
Build docs/index.html simulation landing page |
make-thumbnails.mjs |
Downscale screenshots/*.png to docs/assets/*.webp with sharp |
generate-screenshots.sh |
Capture each sim's screen to <sim>/assets/screenshot.png |
screenshot.mjs |
Playwright driver behind generate-screenshots.sh |
../.github/workflows/refresh-screenshots.yml |
Weekly/manual Pages thumbnail refresh → PR |
Primary entry point for agents. Reads structure/repos.json and adds computed fields:
githubHomepage— normalized Pages URL (https://openphysics.github.io/{name})localPath— sibling directory in the workspace checkoutlocalExists— whether that directory is present locally
# All repository names
scripts/parse-repos.sh names
# Simulation repos only
scripts/parse-repos.sh names --simulation
# Full JSON with computed fields
scripts/parse-repos.sh list --format json --simulation
# One repo
scripts/parse-repos.sh get DopplerEffect
# Local checkout paths for sims that exist on disk
scripts/parse-repos.sh paths --simulation --require-local
# Run a command per repo (env: REPO_NAME, REPO_DISPLAY_NAME, REPO_LINEAGE, REPO_HOMEPAGE, REPO_PATH, ...)
scripts/parse-repos.sh for-each --simulation -- \
echo "$REPO_NAME ($REPO_LINEAGE) -> $REPO_HOMEPAGE"
# Catalog summary
scripts/parse-repos.sh summaryFilters:
--type simulation|template|config|hardware-interface|tool--status active|template|draft|wip|archived--lineage original|phet|naap--simulation/--no-simulation
Populate the workspace from the catalog: clone every selected repo as a sibling directory
beside Baton. repos.json is the single source of truth — there are no submodules, so a
repo appears here the moment it is added to the catalog. Re-runnable and safe: repos already
on disk are skipped unless --update is given (which git pull --ff-onlys them).
The thin OpenPhysics superproject's
bootstrap.sh clones Baton and then calls this; run it directly once you already have
Baton.
# Clone whatever is missing into the workspace
scripts/clone-fleet.sh
# Only the simulations, and fast-forward any already present
scripts/clone-fleet.sh --simulation --update
# Preview the plan, change nothing (HTTPS instead of SSH)
scripts/clone-fleet.sh --dry-run --httpsReuses the same catalog filters as parse-repos.sh (--simulation, --type, --status,
--lineage, --only NAME, --skip NAME). Clones over SSH by default; --https for
token/anonymous use.
Run any git command across every catalog repo already checked out locally:
# put on PATH once (if ~/.local/bin is already there)
ln -sfn ~/OpenPhysics/Baton/scripts/fleet ~/.local/bin/fleet
fleet push
fleet pull --ff-only
fleet status -s
fleet --simulation log -1 --onelineSame catalog filters as parse-repos.sh. Full cheat sheet: doc/fleet-git.md.
Fan a change out across the org: clone each selected repo, run a command in it, and
— with --apply — push a branch and open one PR per repo. Dry-run by default (prints
a diffstat per repo and opens nothing). Reuses the same catalog filters as parse-repos.sh.
# Preview bumping a dependency across every simulation (no PRs):
scripts/fleet-exec.sh --simulation -- npm pkg set dependencies.scenerystack=^3.1.0
# Apply a Biome autofix across all sims and open one PR each:
scripts/fleet-exec.sh --simulation --apply --install \
--branch chore/biome-fix --title "chore: biome autofix" -- npm run fixKey options: --apply (push + open PRs), --install (npm install before the command,
needed for lint/build codemods), --branch, --title, --label, --skip NAME, --keep.
Pushing and opening PRs needs a token with write access to the target repos — your local
gh auth, or an org PAT / GitHub App token as GH_TOKEN. The default GITHUB_TOKEN only
reaches the repo running a workflow, so the fleet-exec.yml
dispatch wrapper reads a FLEET_PAT secret for apply=true.
Updates GitHub Description and Website from repos.json:
scripts/sync-github-metadata.sh --dry-run
scripts/sync-github-metadata.sh
scripts/sync-github-metadata.sh --repo SceneryStackTemplateNote: GitHub does not expose API toggles for Deployments / Packages in the About sidebar. Descriptions longer than 350 characters are truncated with a warning (GitHub’s API limit).
Check or apply the canonical GitHub repository settings baseline
(config/github-repo-baseline.json): wiki/Projects off,
Dependabot alerts + security updates, secret scanning + push protection, private vulnerability
reporting, and Pages build_type=workflow. Full write-up:
../doc/github-repo-settings.md.
scripts/sync-github-settings.sh --check # sims + template; exit 1 on drift
scripts/sync-github-settings.sh --apply --repo MyNewSim # fix one repo
scripts/sync-github-settings.sh --apply --all # every catalog repo
scripts/sync-github-settings.sh --apply --dry-run # show planned fixesUse this after creating a new sim (GitHub defaults diverge from the fleet) instead of inspecting a mature sim by hand.
Roll the scenerystack Claude Code plugin out to the
SceneryStack repos by merging the canonical keys from
config/claude-settings.json into each repo's
.claude/settings.json. It only adds/updates extraKnownMarketplaces.openphysics and
enabledPlugins["scenerystack@openphysics"] — existing keys in a repo's settings are preserved.
Targets every catalog repo whose framework is SceneryStack. Writes files only; commit/push (or
fan out as PRs via fleet-exec.sh) is left to you.
scripts/sync-claude-settings.sh --dry-run # show what would change, write nothing
scripts/sync-claude-settings.sh # merge into each sibling repo
scripts/sync-claude-settings.sh DopplerEffect # limit to named repo(s)Run by baton-selfcheck.yml on every PR or main push that
touches skills/, .claude-plugin/, scripts/, or Baton's own .github/workflows/.github/actions,
and runnable locally:
scripts/check-skills.sh # every skills/<name>/SKILL.md is well-formed and indexed in skills/README.md
scripts/check-node-version.sh # all setup-node workflows declare the same Node versionEnd-to-end checklist (create repo → repos.json → screenshot → WebP → regenerate
docs/index.html): ../doc/add-simulation.md.
create-sim.sh --onboard does the whole thing in one shot — catalog row, screenshot +
WebP + Pages index, and the GitHub settings/metadata/Dependabot/Claude baseline.
If the repo was created some other way and just needs fleet onboarding, pass --existing
(skip the template/rename/scaffold dance):
scripts/create-sim.sh --existing --repo HeatTransfer --name "Heat Transfer" --onboardA repo can exist on GitHub but never make it into repos.json, which makes it invisible to
every fleet tool. This check diffs the live org against the catalog (run by baton-selfcheck
on PRs touching scripts//structure/):
scripts/check-uncataloged.shLists repos under the GitHub org that are missing from structure/repos.json, so a
forgotten onboarding can't stay hidden. Intentional non-members (the superproject itself,
the textbook bundle) are kept in an in-script allowlist, extendable via UNCATALOGED_ALLOWLIST
or structure/uncataloged-allowlist.txt. Requires gh (authed) and jq; exits 1 if any
uncataloged repo is found.
Captures a screenshot of every SceneryStack simulation into <sim>/assets/screenshot.png.
It serves each sim's built dist/ and renders the requested screen with the sim's own
ScreenshotGenerator (the same code path as the in-app camera button), so the result is a
clean PNG at the sim's nominal layout — not a raw viewport grab. Multi-screen sims are forced
onto a single screen with ?screens=N, so the capture is that screen's play area rather than
the home-screen selector.
# One-time setup: install Playwright (declared in this repo's package.json)
npm install
# If Chromium is not already cached: npx playwright install chromium
# Capture every sim's first screen (reuses dist/ if already built)
npm run screenshots
# or directly:
scripts/generate-screenshots.sh
# Force a rebuild first, capture a specific screen, or limit to some sims
scripts/generate-screenshots.sh --build
scripts/generate-screenshots.sh --screen 2 Resonance OscillationsAndChaosOptions: --build (force npm run build), --screen N (default 1),
--width/--height (default 1154×753, matching existing assets). Trailing positional
arguments limit the run to the named sims.
screenshot.mjs is the underlying Playwright driver and can be run on a single dist directory:
node scripts/screenshot.mjs --dist ../DopplerEffect/dist --out /tmp/shot.png --screen 1It discovers a usable Chromium automatically (Playwright's bundled build, the newest cached
build, or a system Chromium); override with PLAYWRIGHT_CHROMIUM_EXECUTABLE.
Source from other scripts:
source "$(dirname "$0")/lib/repos.sh"
repos_simulation_names | while read -r sim; do
echo "$sim"
doneOr call the CLI directly:
scripts/parse-repos.sh names --simulationScripts assume the orchestration Baton repo lives beside member repos:
OpenPhysics/
Baton/ ← this repo
DopplerEffect/
SceneryStackTemplate/
...
If your checkout differs, set OPENPHYSICS_WORKSPACE or pass --catalog /path/to/repos.json.