On this page

Usage scenarios

Eleven ways to use the kit. The README has the short version.

1. Add the kit to an existing game

Keep the kit outside your game and point it at the game with -Project; then none of the kit's own code ends up in your repository or your exported builds (init adds only the files listed below):

git clone https://github.com/nick-vas/hyperdot C:\Tools\hyperdot
pwsh C:\Tools\hyperdot\ci\godot\hyperdot.ps1 init -Project 'C:\path\to\your\game'

With the kit outside, init -Agents writes its full path into AGENTS.md, so agents call it from there (the skills come from the plugin; the path only names the script AGENTS.md's commands run, so keep that clone on the version you want).

To share one pinned version with everyone on the game's repository instead, add it as a submodule: git submodule add https://github.com/nick-vas/hyperdot tools/hyperdot. Its folders carry a .gdignore file, so Godot never imports or exports them, and the kit's own detection ignores them. If you copy only the ci/godot folder, keep its .gdignore: without it, the kit's .gd tool scripts (ten in ci/godot/tools, and the templates' placement_check.gd) would be exported with your game. init only adds things and never overwrites a file, so it's safe to run again. It adds:

With -Agents it also writes AGENTS.md, CLAUDE.md and docs/kit-notes.md (where the agent notes each time the kit got in the way), and enables the kit as a Claude Code plugin in .claude/settings.json. The plugin supplies the skills, the subagents and the hooks, so nothing is copied into the game and nothing there goes stale; the skills are named /hyperdot:game and so on. It comes from GitHub (-PluginSource github, the default: any machine, cloud sessions; every commit on main is a new plugin version, fetched with claude plugin update hyperdot@hyperdot or by the marketplace's auto-update) or from the kit's folder (-PluginSource local: a git pull there updates every game). -CopySkills copies the skills and subagents instead, for a host that loads no plugins; it is refused in a game that already enables the plugin, because each skill would load twice. docs/kit-help.md is no longer written: help and /kit write .godot/kit-help.md, which no repository tracks. Start every Claude Code session in the game with /game: it reads where the game stands and ends with the next tasks to pick from (plan.md).

One script for the whole setup, and a second working copy for a parallel chat. ci/godot/tools/Initialize-HyperdotGame.ps1 checks the game's Git repository, git and the .NET 8 SDK, finds the kit, runs doctor, adds the kit on a branch (init -Plugin, the same as init -Agents), runs the tests, commits and merges the branch, and makes a Git worktree on a new branch so a second Claude Code chat can work on the game while you keep working in the editor:

pwsh C:\Tools\hyperdot\ci\godot\tools\Initialize-HyperdotGame.ps1 -Game 'C:\path\to\your\game' -DryRun   # prints what it would do
pwsh C:\Tools\hyperdot\ci\godot\tools\Initialize-HyperdotGame.ps1 -Game 'C:\path\to\your\game'

It looks for local copies before it downloads anything, and downloads nothing unless you pass -AllowDownload:

A Godot found outside the kit's folder is written to .claude/settings.local.json (env.GODOT_BIN) in the game and in the worktree, and that file is excluded in .git/info/exclude; GODOT_BIN is not set for the machine, because the kit uses it without checking its version. A second run skips what is done. In the Claude desktop app, open a local session on the worktree folder the script prints and send /game.

2. Run the tests locally, and check your setup

pwsh ci/godot/hyperdot.ps1            # build + every detected test
pwsh ci/godot/hyperdot.ps1 doctor     # tools and project checks, each with a fix
pwsh ci/godot/hyperdot.ps1 detect     # show what it found, change nothing
Get-Help ci/godot/hyperdot.ps1 -Detailed

doctor catches the mistakes that break CI or ship broken builds. Examples: no solution_directory (exports would silently drop your C# code), test packages or test folders going into release builds, GUT files that won't run, a GDExtension with no library for a build a preset exports (picked as Godot picks it, docs/addons.md) or for the machine the kit runs on (the editor and test need one too), C# files with no project to compile them, a Godot.NET.Sdk pin or a workflow's godot-version that names another Godot, and a missing .NET SDK. Godot is downloaded once to ~/.hyperdot. Among its warnings: copies of one asset file, OccluderInstance3D nodes with occlusion culling off, and bodies moved per physics tick with physics interpolation off (docs/profiling-and-builds.md).

It also reads the instructions every agent session loads: AGENTS.md, CLAUDE.md and CLAUDE.local.md at the repository root (and in the project folder), with the files they import (@path, up to four hops, as Claude Code does). It warns about a kit call with a command or flag the script does not have (read from the script's own parameters, so -Quite and an ambiguous -Re are caught and -Runn is not), a file or res:// path that does not exist, an import that loads nothing, and a file over 200 lines. Lines a game kept unchanged from the kit's template are the kit's to keep right and are skipped. These are warnings: a stale line does not break the build, but every session follows it.

3. The same tests on a GitHub Linux runner, with a PR comment

on: [push, pull_request]
jobs:
  godot:
    permissions:
      contents: read
      pull-requests: write     # lets it comment; without this it just warns
    uses: nick-vas/hyperdot/.github/workflows/godot.yml@v1

The workflow runs the same script, with the same detection, on ubuntu-latest, the cheapest runner. On pull requests it keeps a single comment up to date with the results table, failures, and coverage compared with the base branch. You don't need the submodule for CI; the workflow brings its own copy of the script, pinned to the @ref you use.

@v1 is what init writes. It is a moving tag on this repository that points at the release games should run, so a change on the kit's main reaches no game until a release moves the tag, and a game moves up (to another tag, or a commit) on purpose, in a commit of its own. A workflow init wrote earlier keeps the ref it has.

4. A GDScript-only game with GUT

Write test_*.gd files that extends GutTest, and the kit installs the GUT version for your Godot release; you don't commit addons/gut. See examples/gdscript.

5. A C# game with unit tests, scene tests and in-engine tests

Keep engine-free logic in a class library tested with xUnit, add gdUnit4Net scene tests, and optionally GoDotTest suites. All of them are detected and run. See examples/csharp and the framework setup notes.

6. Fast pull-request checks, full runs on main

on:
  pull_request:
  push:
    branches: [main]
jobs:
  godot:
    permissions: { contents: read, pull-requests: write }
    uses: nick-vas/hyperdot/.github/workflows/godot.yml@v1
    with:
      # Unit tests only on PRs; every framework once merged.
      runners: ${{ github.event_name == 'pull_request' && 'dotnet' || '' }}

7. Windows builds, checked and released on every tag

on:
  push:
    tags: ['v*']
jobs:
  release:
    permissions:
      contents: write          # to create the release
    uses: nick-vas/hyperdot/.github/workflows/godot.yml@v1
    with:
      exports: Windows         # a preset name from export_presets.cfg, or "all"
      run-exports: true        # launch the .exe on a Windows runner and fail on errors
      release: true            # zip it and publish a GitHub Release with generated notes

Tests run first. Then each preset is exported, launched headless on a runner of its own OS, and zipped as <game>-<tag>-<preset>.zip into a GitHub Release. Launching the export catches what the editor hides: missing assets, code trimmed from the build, and release-only bugs. On a non-tag run, release: true builds the zips but doesn't publish them (a dry run).

Locally: pwsh ci/godot/hyperdot.ps1 export -Preset Windows -Run exports to build/ and launches the .exe.

8. Coverage report and a minimum

pwsh ci/godot/hyperdot.ps1 -Coverage            # report: test-results/coverage/index.html
pwsh ci/godot/hyperdot.ps1 -MinCoverage 60      # fail below 60% line coverage

In the workflow: coverage: true or min-coverage: 60. GoDotTest measures code that only runs inside the engine. dotnet coverage needs coverlet.collector in the test project, and covers code tested outside the engine: gdUnit4Net's [RequireGodotRuntime] tests run inside Godot, where the collector can't see them.

9. Run a subset of tests

pwsh ci/godot/hyperdot.ps1 -Runners dotnet -Filter 'FullyQualifiedName~Combo'   # dotnet test filter
pwsh ci/godot/hyperdot.ps1 -Runners gut -Filter test_combo                       # GUT test name
pwsh ci/godot/hyperdot.ps1 -Runners godottest -Filter PlayerTest                 # GoDotTest suite
pwsh ci/godot/hyperdot.ps1 -Runners validate                                     # just the load check
pwsh ci/godot/hyperdot.ps1 -Runners none                                         # build only

The workflow takes the same values as runners and filter inputs, e.g. for a manual workflow_dispatch.

A test that fails now and then: -Retry 1 (workflow input retry: 1) runs a failed runner once more and passes if the retry does, but lists every test that passed only on the retry in flaky[] and summary.md and records it, so status can name the repeat offenders. A test that keeps failing can go into the project's quarantine.json for a few weeks, with an owner: it still runs and is reported, but does not fail the run; after its date doctor fails until it is fixed or deleted. The policy is Rare's for Sea of Thieves (gdc/sea-of-thieves-automated-testing.md).

10. Several games in one repository

pwsh ci/godot/hyperdot.ps1 -Project games/arena
jobs:
  godot:
    strategy:
      matrix:
        project: [games/arena, games/puzzle]
    permissions: { contents: read, pull-requests: write }
    uses: nick-vas/hyperdot/.github/workflows/godot.yml@v1
    with:
      project: ${{ matrix.project }}

Artifacts and PR comments are per game (named after config/name), so they don't collide. A test results artifact also names the runners input when it is set, so two jobs that test one game with different runners keep apart.

11. GitLab CI

Untested on GitLab: ci/gitlab/hyperdot.gitlab-ci.yml is written against GitLab's documented keywords and checked by tests/Test-GitLabTemplate.ps1 (it parses the file, checks its keys, and matches its report paths against what the kit writes), but it has never run on a GitLab runner. Reports of a first run are welcome.

include:
  - remote: https://raw.githubusercontent.com/nick-vas/hyperdot/v1/ci/gitlab/hyperdot.gitlab-ci.yml
variables:
  HYPERDOT_PROJECT: game        # the folder with project.godot, if not found by itself
  HYPERDOT_COVERAGE: "true"

It adds two jobs, the same steps as the GitHub action: hyperdot-test (stage test, every pipeline) sets up Godot and runs test; hyperdot-export (stage deploy, the default branch and tags) runs export and keeps build/ as the job's artifact. Both run in mcr.microsoft.com/dotnet/sdk:8.0, which holds .NET 8 and git. Its PowerShell is the global tool that follows .NET 8 (7.4), older than the kit's 7.6, and PowerShell 7.6 is built on .NET 10, so before_script unpacks PowerShell's self-contained Linux build (HYPERDOT_PWSH_VERSION, 7.6.9; cached in .hyperdot-cache/pwsh/), puts it first on PATH and stops the job if pwsh is still older than 7.6; games still build with the image's .NET 8 SDK. The kit clones itself into /tmp/hyperdot at HYPERDOT_REF (set HYPERDOT_KIT to a folder that holds it, such as a submodule, to skip the clone) and downloads Godot like anywhere else. The GitHub action's inputs are variables, upper case with underscores: HYPERDOT_PROJECT, HYPERDOT_GODOT_VERSION, HYPERDOT_RUNNERS, HYPERDOT_FILTER, HYPERDOT_RETRY, HYPERDOT_COVERAGE, HYPERDOT_MIN_COVERAGE, HYPERDOT_PRESET, HYPERDOT_RUN_EXPORTS, HYPERDOT_GAME_VERSION (ci/gitlab/Invoke-Hyperdot.ps1 turns them into hyperdot.ps1's parameters).