{
  "id": "unity-test-runner",
  "type": "loadout",
  "title": "Unity tests from the command line, read properly",
  "summary": "Run Unity Test Framework Edit Mode and Play Mode tests headless and read the NUnit results, so a run of zero tests (Unity exits 0) never passes as green.",
  "description": "The Unity agent plugin lets coding agents edit scripts, scenes and assets. The step agents skip is proving the C# still works, and Unity's exit code makes that easy to get wrong. With -runTests, Unity exits 0 when every test passed and also when no tests ran at all, for example when a -testFilter matches nothing or the test assembly is missing. 3 means a run error, usually a compile error, and 4 a missing platform.\n\nThis loadout adds the testing half for Unity 6 projects. The rules keep logic in plain C# classes so Edit Mode tests cover it in milliseconds, with a test .asmdef and an example NUnit test to copy. A standard-library Python wrapper runs the Editor in batch mode with the documented flags, deletes stale results first, and reads the NUnit 3 XML. It lists each failure with its message and first stack line, pulls C# compile errors and the \"project already open\" case out of the log, and treats zero tests as a failure. The verifier drives it with a stub Editor that writes each outcome Unity documents.\n",
  "highlights": [
    "Zero tests ran = failure, even though Unity exits 0",
    "Exit codes 0/2/3/4 explained; compile errors pulled from the log",
    "Stale result files deleted before every run",
    "Edit Mode .asmdef and an NUnit example test to copy",
    "Plain C# logic, thin MonoBehaviours, so tests run in milliseconds"
  ],
  "version": "1.0.0",
  "license": "MIT",
  "author": {
    "name": "SetupBay",
    "url": "https://setupbay.com"
  },
  "credits": [
    {
      "title": "Unity Manual: Test Framework command-line reference",
      "url": "https://docs.unity.com/en-us/engine/7000.0/manual/programming-environment/test-framework-introduction/reference-command-line"
    },
    {
      "title": "Unity Test Framework source: CommandLineTest/Executer.cs (return codes, 'No tests were executed' exits 0)",
      "url": "https://github.com/needle-mirror/com.unity.test-framework/blob/master/UnityEditor.TestRunner/CommandLineTest/Executer.cs"
    },
    {
      "title": "Unity-Technologies/unity-agent-plugin",
      "url": "https://github.com/Unity-Technologies/unity-agent-plugin"
    },
    {
      "title": "@Spanky4thewin: version pins and a never-write list stop deprecated Unity APIs",
      "url": "https://x.com/Spanky4thewin/status/2111340997728723331"
    }
  ],
  "categories": [
    "gamedev"
  ],
  "stacks": [
    "unity",
    "csharp",
    "python"
  ],
  "files": [
    {
      "path": "AGENTS.md",
      "role": "instructions",
      "description": "Testable C# layout and the rules for running Unity tests.",
      "skill": null,
      "agent": null,
      "install_to": {
        "claude-code": "AGENTS.md",
        "codex": "AGENTS.md",
        "cursor": "AGENTS.md",
        "gemini-cli": "AGENTS.md",
        "antigravity-cli": "AGENTS.md",
        "copilot": "AGENTS.md"
      },
      "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/AGENTS.md",
      "raw_latest_url": "https://setupbay.com/raw/unity-test-runner/latest/AGENTS.md",
      "bytes": 1287,
      "lang": "markdown",
      "content": "# AGENTS.md: Unity project with command-line tests\n\nUnity 6 project. C# changes are checked by the Unity Test Framework, run headless. The `unity-tests` skill runs\nthe tests and reads the results; use it after every C# change.\n\n## Code layout for testable C#\n- Game logic in plain C# classes (no `MonoBehaviour`) in the runtime assembly, so Edit Mode tests can run it in\n  milliseconds. MonoBehaviours read input, call the logic and update the scene.\n- Tests in `Assets/Tests/EditMode/` and `Assets/Tests/PlayMode/`, each with its own `.asmdef`. One test class per\n  class under test, named `<Class>Tests`.\n- Never edit `Library/`, `Temp/`, `Logs/`, `obj/` or `UserSettings/`. Never delete or regenerate `.meta` files.\n\n## Running tests\n- The Editor binary is in `UNITY_PATH`, matching `ProjectSettings/ProjectVersion.txt`. Never run a different version.\n- The project must be closed in the Editor for a command-line run. If it is open, ask the human.\n- Unity exits 0 when no tests ran. A run is only green when at least one test ran and none failed.\n\n## Done means\nEdit Mode tests ran (and Play Mode tests, if you changed behaviour that needs frames), the summary shows 0 failed\nand more than 0 total, no test was ignored or weakened, and you reported the command and the summary line.\n"
    },
    {
      "path": "CLAUDE.md",
      "role": "adapter",
      "description": "Imports AGENTS.md for Claude Code.",
      "skill": null,
      "agent": "claude-code",
      "install_to": {
        "claude-code": "CLAUDE.md",
        "codex": null,
        "cursor": null,
        "gemini-cli": null,
        "antigravity-cli": null,
        "copilot": null
      },
      "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/CLAUDE.md",
      "raw_latest_url": "https://setupbay.com/raw/unity-test-runner/latest/CLAUDE.md",
      "bytes": 383,
      "lang": "markdown",
      "content": "# CLAUDE.md\n\n@AGENTS.md\n\n## Claude Code notes\n\n- Skills from this loadout live in `.claude/skills/`: `unity-tests`. Use them when their description matches the task.\n- Follow AGENTS.md above; where this file and AGENTS.md disagree, AGENTS.md wins.\n\n<!-- From SetupBay \"Unity tests from the command line, read properly\" v1.0.0 — https://setupbay.com/loadouts/unity-test-runner/ -->\n"
    },
    {
      "path": "GEMINI.md",
      "role": "adapter",
      "description": "Imports AGENTS.md for Gemini CLI.",
      "skill": null,
      "agent": "gemini-cli",
      "install_to": {
        "claude-code": null,
        "codex": null,
        "cursor": null,
        "gemini-cli": "GEMINI.md",
        "antigravity-cli": null,
        "copilot": null
      },
      "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/GEMINI.md",
      "raw_latest_url": "https://setupbay.com/raw/unity-test-runner/latest/GEMINI.md",
      "bytes": 384,
      "lang": "markdown",
      "content": "# GEMINI.md\n\n@./AGENTS.md\n\n## Gemini CLI notes\n\n- Skills from this loadout live in `.agents/skills/`: `unity-tests`. Use them when their description matches the task.\n- Follow AGENTS.md above; where this file and AGENTS.md disagree, AGENTS.md wins.\n\n<!-- From SetupBay \"Unity tests from the command line, read properly\" v1.0.0 — https://setupbay.com/loadouts/unity-test-runner/ -->\n"
    },
    {
      "path": "skills/unity-tests/SKILL.md",
      "role": "skill",
      "description": "Run tests headless and read the results.",
      "skill": "unity-tests",
      "agent": null,
      "install_to": {
        "claude-code": ".claude/skills/unity-tests/SKILL.md",
        "codex": ".agents/skills/unity-tests/SKILL.md",
        "cursor": ".agents/skills/unity-tests/SKILL.md",
        "gemini-cli": ".agents/skills/unity-tests/SKILL.md",
        "antigravity-cli": ".agents/skills/unity-tests/SKILL.md",
        "copilot": ".agents/skills/unity-tests/SKILL.md"
      },
      "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/skills/unity-tests/SKILL.md",
      "raw_latest_url": "https://setupbay.com/raw/unity-test-runner/latest/skills/unity-tests/SKILL.md",
      "bytes": 3315,
      "lang": "markdown",
      "content": "---\nname: unity-tests\ndescription: Run Unity Test Framework Edit Mode and Play Mode tests from the command line and read the NUnit results, including the zero-tests case Unity reports as success. Use after changing C# in a Unity project, when adding tests, or when the user asks whether the Unity tests pass.\n---\n\n# Unity tests from the command line\n\nThe Unity agent plugin can edit scripts and scenes; this skill is how you prove the C# still works. It runs the\nUnity Test Framework headless and reads the result file, because the exit code alone is misleading:\n\n| Exit | Meaning |\n|---|---|\n| 0 | every test passed **or no tests ran at all** (a filter that matches nothing, a missing test assembly) |\n| 2 | one or more tests failed |\n| 3 | run error: usually a compile error, so no tests ran |\n| 4 | the test platform isn't installed |\n\n## 1. Preconditions\n- `ProjectSettings/ProjectVersion.txt` gives the Editor version. `UNITY_PATH` must point at that Editor's binary.\n  If it isn't set, ask the human for the path; do not guess another version (it would re-import the project).\n- **The project must not be open in the Editor.** Batch mode refuses a project that is already open. If the human\n  has it open, ask them to close it, or to run the tests in Window → General → Test Runner and paste the result.\n- `git status` clean, or the human knows you are about to run a re-import.\n\n## 2. Edit Mode first\nEdit Mode tests run plain C# without entering Play mode, in seconds. Put logic in plain classes so it can be tested\nhere, and keep MonoBehaviours thin.\n- Test assembly: `Assets/Tests/EditMode/` with an `.asmdef` like `references/EditModeTests.asmdef.json`\n  (Editor-only, references `UnityEngine.TestRunner` and `UnityEditor.TestRunner`, `nunit.framework.dll`, and the\n  runtime assembly under test, constrained to `UNITY_INCLUDE_TESTS`). Save it with the `.asmdef` extension.\n- Example test: `references/HealthTests.cs`. NUnit `[Test]` and `[TestCase]`; `Assert.That(x, Is.EqualTo(y))`.\n- Play Mode tests (`[UnityTest]` coroutines, scenes, physics) go in `Assets/Tests/PlayMode/` with a separate\n  `.asmdef` that is not Editor-only. Use them for behaviour that needs frames.\n\n## 3. Run\n```bash\npython3 .agents/skills/unity-tests/scripts/unity_tests.py run --platform EditMode\npython3 .agents/skills/unity-tests/scripts/unity_tests.py run --platform EditMode --filter \"Game.Tests.HealthTests\"\npython3 .agents/skills/unity-tests/scripts/unity_tests.py run --platform PlayMode\n```\n(Claude Code installs the skill under `.claude/skills/`.) It writes `TestResults/<platform>.xml` and `.log`\n(add `TestResults/` to `.gitignore`), deletes old ones first so a stale file is never read, prints the Unity\ncommand, each failing test with its message and first stack line, compile errors from the log, and skipped tests\nwith their reasons. Already have a results file? `unity_tests.py read TestResults/editmode.xml`.\n\n## 4. Read it like a reviewer\n- `NO TESTS RAN` is a failure, whatever Unity's exit code says. Fix the filter, the `.asmdef` or the platform.\n- `COMPILE` lines: fix those first; nothing else ran.\n- Never mark a test `[Ignore]`, delete it, or loosen an assertion to get green. If a test is wrong, say why and ask.\n- Report the summary line (total, passed, failed, skipped) and the command you ran.\n"
    },
    {
      "path": "skills/unity-tests/scripts/unity_tests.py",
      "role": "reference",
      "description": "Batch-mode runner and NUnit 3 results reader.",
      "skill": "unity-tests",
      "agent": null,
      "install_to": {
        "claude-code": ".claude/skills/unity-tests/scripts/unity_tests.py",
        "codex": ".agents/skills/unity-tests/scripts/unity_tests.py",
        "cursor": ".agents/skills/unity-tests/scripts/unity_tests.py",
        "gemini-cli": ".agents/skills/unity-tests/scripts/unity_tests.py",
        "antigravity-cli": ".agents/skills/unity-tests/scripts/unity_tests.py",
        "copilot": ".agents/skills/unity-tests/scripts/unity_tests.py"
      },
      "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/skills/unity-tests/scripts/unity_tests.py",
      "raw_latest_url": "https://setupbay.com/raw/unity-test-runner/latest/skills/unity-tests/scripts/unity_tests.py",
      "bytes": 5035,
      "lang": "markdown",
      "content": "#!/usr/bin/env python3\n\"\"\"Run Unity Test Framework tests from the command line and read the NUnit results properly.\n\nUsage:\n  python3 unity_tests.py run [--platform EditMode|PlayMode] [--filter REGEX] [--category NAME] [--project .]\n  python3 unity_tests.py read RESULTS.xml\n\n`run` needs the Unity Editor binary in UNITY_PATH (for example\n/Applications/Unity/Hub/Editor/6000.2.6f1/Unity.app/Contents/MacOS/Unity). The Editor must not have the project open.\nIt runs: Unity -batchmode -projectPath P -runTests -testPlatform X -testResults F -logFile L [-testFilter ...]\nand leaves out -quit (the test run exits the Editor when it ends).\n\nWhy not trust the exit code alone: Unity exits 0 when every test passed and ALSO when no tests ran at all\n(for example a -testFilter that matches nothing). 2 means tests failed, 3 a run error (often a compile error),\n4 a platform that is not installed. This script fails on zero tests, lists each failure with its message,\nand pulls compile errors out of the log.\nExit 0 only when at least one test ran and none failed. Standard library only.\n\"\"\"\nimport os\nimport re\nimport subprocess\nimport sys\nimport xml.etree.ElementTree as ET\n\nCODES = {0: \"run completed\", 2: \"one or more tests failed\", 3: \"run error (compile error, crash or no test callbacks)\", 4: \"test platform not found\"}\n\n\ndef read(path):\n    if not os.path.exists(path):\n        print(f\"unity-tests: no results file at {path}; the run did not get as far as tests\")\n        return 1\n    root = ET.parse(path).getroot()\n    run = root if root.tag == \"test-run\" else root.find(\".//test-run\")\n    if run is None:\n        print(f\"unity-tests: {path} is not an NUnit 3 test-run file\")\n        return 1\n    a = {k: run.get(k, \"0\") for k in (\"total\", \"passed\", \"failed\", \"skipped\", \"inconclusive\", \"result\", \"duration\")}\n    total, failed = int(a[\"total\"]), int(a[\"failed\"])\n    print(f\"unity-tests: {a['total']} total, {a['passed']} passed, {a['failed']} failed, {a['skipped']} skipped, {a['inconclusive']} inconclusive ({a['result']})\")\n    for case in run.iter(\"test-case\"):\n        if case.get(\"result\") == \"Failed\":\n            msg = (case.findtext(\"failure/message\") or \"\").strip().splitlines()\n            where = (case.findtext(\"failure/stack-trace\") or \"\").strip().splitlines()\n            print(f\"FAILED {case.get('fullname')}: {msg[0] if msg else '(no message)'}\")\n            if where:\n                print(f\"   at {where[0].strip()}\")\n    for case in run.iter(\"test-case\"):\n        if case.get(\"result\") == \"Skipped\":\n            reason = (case.findtext(\"reason/message\") or \"\").strip()\n            print(f\"skipped {case.get('fullname')}{': ' + reason if reason else ''}\")\n    if total == 0:\n        print(\"unity-tests: NO TESTS RAN. Unity still exits 0 here. Check -testFilter/-testCategory, the test assembly's .asmdef, and -testPlatform.\")\n        return 1\n    return 1 if failed else 0\n\n\ndef compile_errors(log):\n    if not os.path.exists(log):\n        return []\n    text = open(log, encoding=\"utf-8\", errors=\"replace\").read()\n    errs = re.findall(r\"^(Assets/[^\\n(]+\\(\\d+,\\d+\\): error CS\\d+: [^\\n]+)\", text, re.M)\n    if \"another Unity instance is running\" in text or \"It looks like another Unity instance\" in text:\n        errs.insert(0, \"The project is open in another Unity Editor. Ask the human to close it, or run the tests in the Test Runner window.\")\n    return list(dict.fromkeys(errs))\n\n\ndef run(argv):\n    unity = os.environ.get(\"UNITY_PATH\")\n    if not unity:\n        print(\"unity-tests: set UNITY_PATH to the Unity Editor binary for this project's version (ProjectSettings/ProjectVersion.txt)\")\n        return 2\n    opt = lambda n, d=None: argv[argv.index(n) + 1] if n in argv else d\n    project = os.path.abspath(opt(\"--project\", \".\"))\n    platform = opt(\"--platform\", \"EditMode\")\n    out = os.path.join(project, \"TestResults\")\n    os.makedirs(out, exist_ok=True)\n    results, log = os.path.join(out, f\"{platform.lower()}.xml\"), os.path.join(out, f\"{platform.lower()}.log\")\n    for p in (results, log):\n        if os.path.exists(p):\n            os.remove(p)  # never read a stale file from an earlier run\n    cmd = [unity, \"-batchmode\", \"-projectPath\", project, \"-runTests\", \"-testPlatform\", platform, \"-testResults\", results, \"-logFile\", log]\n    if platform == \"EditMode\":\n        cmd.insert(2, \"-nographics\")\n    if opt(\"--filter\"):\n        cmd += [\"-testFilter\", opt(\"--filter\")]\n    if opt(\"--category\"):\n        cmd += [\"-testCategory\", opt(\"--category\")]\n    print(\"$ \" + \" \".join(cmd))\n    code = subprocess.run(cmd).returncode\n    print(f\"unity exit {code}: {CODES.get(code, 'unknown')}\")\n    for e in compile_errors(log):\n        print(f\"COMPILE {e}\")\n    status = read(results)\n    return status if code in (0, 2) else (code or 1)\n\n\ndef main(argv):\n    if argv[:1] == [\"read\"] and len(argv) == 2:\n        return read(argv[1])\n    if argv[:1] == [\"run\"]:\n        return run(argv[1:])\n    print(__doc__.strip(), file=sys.stderr)\n    return 2\n\n\nif __name__ == \"__main__\":\n    sys.exit(main(sys.argv[1:]))\n"
    },
    {
      "path": "skills/unity-tests/references/EditModeTests.asmdef.json",
      "role": "reference",
      "description": "Edit Mode test assembly definition to copy.",
      "skill": "unity-tests",
      "agent": null,
      "install_to": {
        "claude-code": ".claude/skills/unity-tests/references/EditModeTests.asmdef.json",
        "codex": ".agents/skills/unity-tests/references/EditModeTests.asmdef.json",
        "cursor": ".agents/skills/unity-tests/references/EditModeTests.asmdef.json",
        "gemini-cli": ".agents/skills/unity-tests/references/EditModeTests.asmdef.json",
        "antigravity-cli": ".agents/skills/unity-tests/references/EditModeTests.asmdef.json",
        "copilot": ".agents/skills/unity-tests/references/EditModeTests.asmdef.json"
      },
      "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/skills/unity-tests/references/EditModeTests.asmdef.json",
      "raw_latest_url": "https://setupbay.com/raw/unity-test-runner/latest/skills/unity-tests/references/EditModeTests.asmdef.json",
      "bytes": 471,
      "lang": "json",
      "content": "{\n  \"name\": \"Game.Tests.EditMode\",\n  \"rootNamespace\": \"Game.Tests\",\n  \"references\": [\n    \"Game.Runtime\",\n    \"UnityEngine.TestRunner\",\n    \"UnityEditor.TestRunner\"\n  ],\n  \"includePlatforms\": [\"Editor\"],\n  \"excludePlatforms\": [],\n  \"allowUnsafeCode\": false,\n  \"overrideReferences\": true,\n  \"precompiledReferences\": [\"nunit.framework.dll\"],\n  \"autoReferenced\": false,\n  \"defineConstraints\": [\"UNITY_INCLUDE_TESTS\"],\n  \"versionDefines\": [],\n  \"noEngineReferences\": false\n}\n"
    },
    {
      "path": "skills/unity-tests/references/HealthTests.cs",
      "role": "reference",
      "description": "Example NUnit Edit Mode test.",
      "skill": "unity-tests",
      "agent": null,
      "install_to": {
        "claude-code": ".claude/skills/unity-tests/references/HealthTests.cs",
        "codex": ".agents/skills/unity-tests/references/HealthTests.cs",
        "cursor": ".agents/skills/unity-tests/references/HealthTests.cs",
        "gemini-cli": ".agents/skills/unity-tests/references/HealthTests.cs",
        "antigravity-cli": ".agents/skills/unity-tests/references/HealthTests.cs",
        "copilot": ".agents/skills/unity-tests/references/HealthTests.cs"
      },
      "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/skills/unity-tests/references/HealthTests.cs",
      "raw_latest_url": "https://setupbay.com/raw/unity-test-runner/latest/skills/unity-tests/references/HealthTests.cs",
      "bytes": 930,
      "lang": "markdown",
      "content": "// Example Edit Mode test. Copy to Assets/Tests/EditMode/HealthTests.cs next to the test .asmdef.\n// Tests plain C# logic (no scene, no frames), so it runs in milliseconds from the command line.\nusing NUnit.Framework;\n\nnamespace Game.Tests\n{\n    public class HealthTests\n    {\n        [Test]\n        public void Damage_ReducesHealth_AndNeverGoesBelowZero()\n        {\n            var health = new Health(max: 10);\n            health.TakeDamage(4);\n            Assert.That(health.Current, Is.EqualTo(6));\n            health.TakeDamage(50);\n            Assert.That(health.Current, Is.EqualTo(0));\n            Assert.That(health.IsDead, Is.True);\n        }\n\n        [TestCase(0)]\n        [TestCase(-3)]\n        public void Damage_IgnoresZeroOrNegative(int amount)\n        {\n            var health = new Health(max: 10);\n            health.TakeDamage(amount);\n            Assert.That(health.Current, Is.EqualTo(10));\n        }\n    }\n}\n"
    }
  ],
  "requires": {
    "tools": [
      "unity-6",
      "python3"
    ],
    "env": [
      "UNITY_PATH"
    ],
    "mcp": []
  },
  "tested": {
    "status": "untested",
    "last_run": null,
    "fixture": null,
    "per_agent": {
      "claude-code": "untested",
      "codex": "untested",
      "cursor": "untested",
      "gemini-cli": "untested",
      "antigravity-cli": "untested",
      "copilot": "untested"
    }
  },
  "security": {
    "scanned": true,
    "scanner": "intake-precheck/2",
    "verdict": "clean",
    "network": false,
    "scripts": [
      "unity_tests.py"
    ],
    "notes": "The runner starts the Unity Editor binary named in UNITY_PATH in batch mode and writes results under TestResults/. It makes no network calls of its own."
  },
  "featured": false,
  "deprecated": false,
  "last_checked": "2026-10-24",
  "published": "2026-10-24",
  "updated": "2026-10-24",
  "compatibility": {
    "claude-code": "native",
    "codex": "native",
    "cursor": "native",
    "gemini-cli": "native",
    "antigravity-cli": "native",
    "copilot": "native"
  },
  "counts": {
    "rules": 1,
    "skills": 1,
    "mcp": 0
  },
  "url": "https://setupbay.com/loadouts/unity-test-runner/",
  "api_url": "https://setupbay.com/api/loadouts/unity-test-runner.json",
  "verification": {
    "level": "verified-install",
    "status": "verified",
    "label": "Verified install",
    "verified": true,
    "checked_at": "2026-10-24T10:30:13Z",
    "commit": "42646ff9a52315018c9b0947c291c7ad980e1dc7",
    "content_current": true,
    "agent_tests": "not-run",
    "counts": {
      "pass": 9,
      "fail": 0,
      "skip": 0,
      "not_applicable": 1
    },
    "meaning": "Level 1: automated checks on the files (parse, install paths, MCP config, safety scan, references, size, licence, starter build). No AI agent was run.",
    "checks": [
      {
        "id": "listing",
        "name": "Listing",
        "status": "pass",
        "summary": "Schema valid; 7 files declared, none missing or extra",
        "details": [
          "tested.status: untested (agent tests are separate from Level 1)"
        ]
      },
      {
        "id": "parse",
        "name": "Files parse",
        "status": "pass",
        "summary": "All 7 files parse; front matter valid",
        "details": []
      },
      {
        "id": "adapters",
        "name": "Adapters",
        "status": "pass",
        "summary": "CLAUDE.md and GEMINI.md import AGENTS.md and are up to date",
        "details": [
          "CLAUDE.md: matches the generator",
          "GEMINI.md: matches the generator"
        ]
      },
      {
        "id": "install-paths",
        "name": "Install paths",
        "status": "pass",
        "summary": "Correct paths for all 6 agents; layouts re-discovered",
        "details": [
          "Claude Code: 6 files, 1 skill in .claude/skills/",
          "Codex CLI: 5 files, 1 skill in .agents/skills/",
          "Cursor: 5 files, 1 skill in .agents/skills/",
          "Gemini CLI: 6 files, 1 skill in .agents/skills/",
          "Antigravity CLI: 5 files, 1 skill in .agents/skills/",
          "GitHub Copilot: 5 files, 1 skill in .agents/skills/"
        ]
      },
      {
        "id": "mcp",
        "name": "MCP config",
        "status": "n/a",
        "summary": "No MCP servers in this loadout",
        "details": []
      },
      {
        "id": "safety",
        "name": "Safety scan",
        "status": "pass",
        "summary": "Clean: no credentials, risky shell, hidden text or covert instructions",
        "details": [
          "Scanner intake-precheck/2: verdict clean; network no (listing says no)"
        ]
      },
      {
        "id": "references",
        "name": "References",
        "status": "pass",
        "summary": "2 references between files resolve for every agent",
        "details": []
      },
      {
        "id": "size",
        "name": "Size limits",
        "status": "pass",
        "summary": "7 files, 11.5 KB total; largest skills/unity-tests/scripts/unity_tests.py 4.9 KB",
        "details": [
          "Limits: 30 files, 32 KB per file, 128 KB total, 500 lines per skill or rule"
        ]
      },
      {
        "id": "licence",
        "name": "Licence",
        "status": "pass",
        "summary": "MIT, by SetupBay; text in the repository LICENSE",
        "details": []
      },
      {
        "id": "starter-build",
        "name": "Starter build",
        "status": "pass",
        "summary": "The unity-tests wrapper runs a stub Unity Editor that writes the NUnit 3 results and log for each documented outcome. It passes a green run, lists a failing test with its message and line, reports a skipped test's reason, fails the zero-tests run that Unity exits 0 on, pulls a C# compile error out o",
        "details": [
          "Applied 11 loadout files at every agent's paths",
          "✓ green run: bash -c export UNITY_PATH=$PWD/bin/Unity STUB_OUTCOME=pass && python3 .agents/skills/unity-tests/scripts/unity_tests.py run --platform EditMode; echo \"exit=$?\"; tr '\\n' ' ' < args.txt",
          "✓ failing test is listed with its message and line; skipped reason shown: bash -c export UNITY_PATH=$PWD/bin/Unity STUB_OUTCOME=fail && python3 .agents/skills/unity-tests/scripts/unity_tests.py run --platform EditMode --filter Game.Tests.HealthTests; echo \"exit=$?\"; grep -A1 -- -testFilter args.txt",
          "✓ zero tests is a failure even though Unity exits 0: bash -c export UNITY_PATH=$PWD/bin/Unity STUB_OUTCOME=none && python3 .agents/skills/unity-tests/scripts/unity_tests.py run --filter NoSuchTest; echo \"exit=$?\"",
          "✓ compile error on exit 3; stale results are not read: bash -c export UNITY_PATH=$PWD/bin/Unity STUB_OUTCOME=compile && python3 .agents/skills/unity-tests/scripts/unity_tests.py run; echo \"exit=$?\"",
          "✓ project already open in the Editor: bash -c export UNITY_PATH=$PWD/bin/Unity STUB_OUTCOME=open && python3 .agents/skills/unity-tests/scripts/unity_tests.py run --platform PlayMode; echo \"exit=$?\"; tr '\\n' ' ' < args.txt",
          "✓ no UNITY_PATH: asks instead of guessing: bash -c env -u UNITY_PATH python3 .agents/skills/unity-tests/scripts/unity_tests.py run; echo \"exit=$?\"",
          "✓ test .asmdef reference is an Editor-only test assembly: python3 -c import json; a=json.load(open('.agents/skills/unity-tests/references/EditModeTests.asmdef.json')); assert a['includePlatforms']==['Editor'] and 'UnityEngine.TestRunner' in a['references'] and 'UnityEditor.TestRunner' in a['references'] and a['defineConstraints']==['UNITY_INCLUDE_TESTS'] and 'nunit.framework.dll' in a['precompiledReferences']; print('asmdef ok')",
          "Covers: Unity Test Framework's documented return codes (0, 2, 3) and the 'No tests were executed' case that exits 0",
          "Covers: NUnit 3 result parsing: totals, failure message and stack line, skipped reason",
          "Covers: Batch-mode command line: -batchmode, -nographics for Edit Mode only, -runTests, -testPlatform, -testResults, -logFile, -testFilter, no -quit",
          "Covers: Not covered: a real Unity Editor run (needs a licence and a multi-gigabyte install; the stub writes the documented files)"
        ]
      }
    ]
  },
  "install": {
    "claude-code": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "CLAUDE.md",
          "target": "CLAUDE.md",
          "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/CLAUDE.md",
          "role": "adapter"
        },
        {
          "path": "skills/unity-tests/SKILL.md",
          "target": ".claude/skills/unity-tests/SKILL.md",
          "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/skills/unity-tests/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/unity-tests/scripts/unity_tests.py",
          "target": ".claude/skills/unity-tests/scripts/unity_tests.py",
          "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/skills/unity-tests/scripts/unity_tests.py",
          "role": "reference"
        },
        {
          "path": "skills/unity-tests/references/EditModeTests.asmdef.json",
          "target": ".claude/skills/unity-tests/references/EditModeTests.asmdef.json",
          "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/skills/unity-tests/references/EditModeTests.asmdef.json",
          "role": "reference"
        },
        {
          "path": "skills/unity-tests/references/HealthTests.cs",
          "target": ".claude/skills/unity-tests/references/HealthTests.cs",
          "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/skills/unity-tests/references/HealthTests.cs",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://setupbay.com/download/unity-test-runner/claude-code.zip"
    },
    "codex": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "skills/unity-tests/SKILL.md",
          "target": ".agents/skills/unity-tests/SKILL.md",
          "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/skills/unity-tests/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/unity-tests/scripts/unity_tests.py",
          "target": ".agents/skills/unity-tests/scripts/unity_tests.py",
          "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/skills/unity-tests/scripts/unity_tests.py",
          "role": "reference"
        },
        {
          "path": "skills/unity-tests/references/EditModeTests.asmdef.json",
          "target": ".agents/skills/unity-tests/references/EditModeTests.asmdef.json",
          "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/skills/unity-tests/references/EditModeTests.asmdef.json",
          "role": "reference"
        },
        {
          "path": "skills/unity-tests/references/HealthTests.cs",
          "target": ".agents/skills/unity-tests/references/HealthTests.cs",
          "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/skills/unity-tests/references/HealthTests.cs",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://setupbay.com/download/unity-test-runner/codex.zip"
    },
    "cursor": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "skills/unity-tests/SKILL.md",
          "target": ".agents/skills/unity-tests/SKILL.md",
          "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/skills/unity-tests/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/unity-tests/scripts/unity_tests.py",
          "target": ".agents/skills/unity-tests/scripts/unity_tests.py",
          "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/skills/unity-tests/scripts/unity_tests.py",
          "role": "reference"
        },
        {
          "path": "skills/unity-tests/references/EditModeTests.asmdef.json",
          "target": ".agents/skills/unity-tests/references/EditModeTests.asmdef.json",
          "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/skills/unity-tests/references/EditModeTests.asmdef.json",
          "role": "reference"
        },
        {
          "path": "skills/unity-tests/references/HealthTests.cs",
          "target": ".agents/skills/unity-tests/references/HealthTests.cs",
          "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/skills/unity-tests/references/HealthTests.cs",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://setupbay.com/download/unity-test-runner/cursor.zip"
    },
    "gemini-cli": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "GEMINI.md",
          "target": "GEMINI.md",
          "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/GEMINI.md",
          "role": "adapter"
        },
        {
          "path": "skills/unity-tests/SKILL.md",
          "target": ".agents/skills/unity-tests/SKILL.md",
          "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/skills/unity-tests/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/unity-tests/scripts/unity_tests.py",
          "target": ".agents/skills/unity-tests/scripts/unity_tests.py",
          "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/skills/unity-tests/scripts/unity_tests.py",
          "role": "reference"
        },
        {
          "path": "skills/unity-tests/references/EditModeTests.asmdef.json",
          "target": ".agents/skills/unity-tests/references/EditModeTests.asmdef.json",
          "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/skills/unity-tests/references/EditModeTests.asmdef.json",
          "role": "reference"
        },
        {
          "path": "skills/unity-tests/references/HealthTests.cs",
          "target": ".agents/skills/unity-tests/references/HealthTests.cs",
          "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/skills/unity-tests/references/HealthTests.cs",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://setupbay.com/download/unity-test-runner/gemini-cli.zip"
    },
    "antigravity-cli": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "skills/unity-tests/SKILL.md",
          "target": ".agents/skills/unity-tests/SKILL.md",
          "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/skills/unity-tests/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/unity-tests/scripts/unity_tests.py",
          "target": ".agents/skills/unity-tests/scripts/unity_tests.py",
          "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/skills/unity-tests/scripts/unity_tests.py",
          "role": "reference"
        },
        {
          "path": "skills/unity-tests/references/EditModeTests.asmdef.json",
          "target": ".agents/skills/unity-tests/references/EditModeTests.asmdef.json",
          "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/skills/unity-tests/references/EditModeTests.asmdef.json",
          "role": "reference"
        },
        {
          "path": "skills/unity-tests/references/HealthTests.cs",
          "target": ".agents/skills/unity-tests/references/HealthTests.cs",
          "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/skills/unity-tests/references/HealthTests.cs",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://setupbay.com/download/unity-test-runner/antigravity-cli.zip"
    },
    "copilot": {
      "files": [
        {
          "path": "AGENTS.md",
          "target": "AGENTS.md",
          "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/AGENTS.md",
          "role": "instructions"
        },
        {
          "path": "skills/unity-tests/SKILL.md",
          "target": ".agents/skills/unity-tests/SKILL.md",
          "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/skills/unity-tests/SKILL.md",
          "role": "skill"
        },
        {
          "path": "skills/unity-tests/scripts/unity_tests.py",
          "target": ".agents/skills/unity-tests/scripts/unity_tests.py",
          "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/skills/unity-tests/scripts/unity_tests.py",
          "role": "reference"
        },
        {
          "path": "skills/unity-tests/references/EditModeTests.asmdef.json",
          "target": ".agents/skills/unity-tests/references/EditModeTests.asmdef.json",
          "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/skills/unity-tests/references/EditModeTests.asmdef.json",
          "role": "reference"
        },
        {
          "path": "skills/unity-tests/references/HealthTests.cs",
          "target": ".agents/skills/unity-tests/references/HealthTests.cs",
          "raw_url": "https://setupbay.com/raw/unity-test-runner/1.0.0/skills/unity-tests/references/HealthTests.cs",
          "role": "reference"
        }
      ],
      "manual_steps": [],
      "zip_url": "https://setupbay.com/download/unity-test-runner/copilot.zip"
    }
  },
  "source_zip_url": "https://setupbay.com/download/unity-test-runner/source.zip"
}