{"id":50671912,"url":"https://github.com/claymantwinkle/godot-cli-control","last_synced_at":"2026-06-12T02:00:35.617Z","repository":{"id":354419051,"uuid":"1223534926","full_name":"ClaymanTwinkle/godot-cli-control","owner":"ClaymanTwinkle","description":"WebSocket bridge for headless / scripted control of Godot 4 scenes (click / property / input / screenshot / record).","archived":false,"fork":false,"pushed_at":"2026-06-07T15:39:03.000Z","size":1342,"stargazers_count":2,"open_issues_count":8,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-08T12:29:09.382Z","etag":null,"topics":["ai-agent","automation","game-testing","gdscript","godot","godot-plugin","godot4","llm","pytest","websocket"],"latest_commit_sha":null,"homepage":"https://pypi.org/project/godot-cli-control/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ClaymanTwinkle.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":".github/SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-04-28T12:18:04.000Z","updated_at":"2026-06-07T15:39:07.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ClaymanTwinkle/godot-cli-control","commit_stats":null,"previous_names":["claymantwinkle/godot-cli-control"],"tags_count":28,"template":false,"template_full_name":null,"purl":"pkg:github/ClaymanTwinkle/godot-cli-control","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ClaymanTwinkle%2Fgodot-cli-control","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ClaymanTwinkle%2Fgodot-cli-control/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ClaymanTwinkle%2Fgodot-cli-control/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ClaymanTwinkle%2Fgodot-cli-control/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ClaymanTwinkle","download_url":"https://codeload.github.com/ClaymanTwinkle/godot-cli-control/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ClaymanTwinkle%2Fgodot-cli-control/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34225351,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-12T02:00:06.859Z","response_time":109,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ai-agent","automation","game-testing","gdscript","godot","godot-plugin","godot4","llm","pytest","websocket"],"created_at":"2026-06-08T12:00:47.102Z","updated_at":"2026-06-12T02:00:35.606Z","avatar_url":"https://github.com/ClaymanTwinkle.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# Godot CLI Control\n\n**Drive a running Godot 4 scene from Python, pytest, a shell, or an AI agent — over a localhost WebSocket.**\n\nClick nodes. Read \u0026 write properties. Simulate input. Take screenshots. Record movies. Black-box test a game without recompiling it.\n\n[![PyPI](https://img.shields.io/pypi/v/godot-cli-control.svg)](https://pypi.org/project/godot-cli-control/)\n[![Python](https://img.shields.io/pypi/pyversions/godot-cli-control.svg)](https://pypi.org/project/godot-cli-control/)\n[![Godot](https://img.shields.io/badge/Godot-4.x-478cbf.svg)](https://godotengine.org/)\n[![CI](https://github.com/ClaymanTwinkle/godot-cli-control/actions/workflows/ci.yml/badge.svg)](https://github.com/ClaymanTwinkle/godot-cli-control/actions/workflows/ci.yml)\n[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)\n\n[**▶ Runnable demo — `examples/platformer-demo` →**](examples/platformer-demo)\n\n\u003c/div\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"docs/hero.gif\" width=\"640\" alt=\"A shell / AI agent driving a running Godot scene via godot-cli-control — clicking Start, then making the character jump and run\"\u003e\n\u003c/p\u003e\n\n---\n\n## Why\n\nGodot ships great tools for *playing* a scene, but very little for *programmatically poking it from outside*. If you want CI to assert that \"the boss spawns after the third hit,\" or you want an AI agent to navigate a menu, or you want a pytest run to drive an animated character through 200 frames and diff the screenshot — you typically end up writing a one-off in-engine harness per project.\n\n`godot-cli-control` is that harness, generalized:\n\n- It's a **plugin** that drops into `addons/` and exposes a JSON-RPC server bound to `127.0.0.1`.\n- It's a **Python client + CLI** (`pipx install godot-cli-control`) that talks to that server.\n- It's a **pytest plugin** so test files just declare `bridge` and go.\n- It's an **agent integration** — `init` writes Claude Code / Codex skills so AI tools immediately know your scene's automation surface.\n\nOne install, one daemon, one consistent API across your editor, your tests, your CI, and your agents.\n\n## Highlights\n\n| | |\n|---|---|\n| **One-shot onboarding** | `godot-cli-control init` copies the addon, patches `project.godot`, autodetects the Godot binary, and writes AI-agent skill files. Idempotent. |\n| **Shell is canonical**  | 30+ subcommands cover the full RPC surface; output is single-line JSON envelope by default (`--text` for legacy strings). `GameClient` (async) / `GameBridge` (sync) are still there when you need to keep one connection across many steps. |\n| **pytest fixtures**     | Auto-loaded `godot_daemon` (session) + `bridge` (function) fixtures, plus opt-in `fresh_scene` (per-test scene reload) and `no_push_errors` (fail on silent `push_error`). Held inputs / pause / time-scale restored between cases; failures auto-screenshot. |\n| **Deterministic waits** | `wait-prop` / `wait-signal` / `wait-frames` / `pause` + `step-frames` / `time-scale` — assert on real engine state instead of sleeping and hoping. |\n| **AI-agent ready**      | `init` ships `.claude/skills/.../SKILL.md` and `.codex/skills/.../SKILL.md` pinned to your installed CLI version. Includes exit-code semantics, error code reference, and JSON envelope contract. |\n| **Headless or GUI**     | Runs under `--headless` for CI, or with a real window for visual debugging. |\n| **Cross-platform**      | Linux, macOS, and native Windows (no WSL). |\n| **Safe by default**     | Localhost-only bind, OFF unless explicitly activated, release builds always disabled, property/method blacklist. |\n\n## Quickstart\n\n```bash\n# 1. Install the CLI (it bundles the Godot plugin source)\npipx install godot-cli-control\n\n# 2. From your Godot project root: copy plugin, patch project.godot, detect Godot binary\ncd path/to/your_godot_project\ngodot-cli-control init\n\n# 3. Start the daemon and try it\ngodot-cli-control daemon start\ngodot-cli-control tree 3\ngodot-cli-control screenshot /tmp/x.png\ngodot-cli-control daemon stop\n```\n\nWant unreleased main? `pipx install \"git+https://github.com/ClaymanTwinkle/godot-cli-control.git\"`.\n\n## Try the demo\n\nA self-contained example lives in [`examples/platformer-demo/`](examples/platformer-demo) — a tiny scene with a **Start** button and a jumping character, plus a `drive.sh` that walks it end to end (init → daemon → click → simulate input → read state back → screenshot). Clone the repo, then:\n\n```bash\ncd examples/platformer-demo\n./drive.sh\n```\n\nIt's driven under a real (headless) Godot in CI, so it can't silently rot.\n\n## A taste\n\n**From Python, async:**\n\n```python\nimport asyncio\nfrom godot_cli_control import GameClient\n\nasync def main():\n    # Omitting port lets GameClient auto-discover from .cli_control/port (written by daemon start)\n    async with GameClient() as client:\n        await client.click(\"/root/Game/StartButton\")\n        await client.action_press(\"jump\")\n        await client.wait_game_time(0.5)\n        await client.action_release(\"jump\")\n        png = await client.screenshot()\n        open(\"frame.png\", \"wb\").write(png)\n\nasyncio.run(main())\n```\n\n**From pytest (fixtures auto-loaded — no `conftest.py` boilerplate):**\n\n```python\ndef test_jump(godot_daemon, bridge):\n    bridge.click(\"/root/Game/Start\")\n    bridge.tap(\"jump\")\n    assert bridge.get_property(\"/root/Player\", \"on_floor\") is False\n```\n\n**From a shell or wrapper script — same surface, JSON by default for AI agents:**\n\n```bash\ngodot-cli-control daemon start --headless\ngodot-cli-control click /root/Game/StartButton\ngodot-cli-control tap jump\nif godot-cli-control exists /root/Game/Boss; then\n  godot-cli-control get /root/Game/Boss hp | jq .result   # → 100\nfi\ngodot-cli-control screenshot frame.png\ngodot-cli-control daemon stop\n```\n\nPass `--text` (or `--no-json`) to switch back to legacy human-readable output.\n\n## How it fits together\n\n```\n┌────────────────────────────────────┐                ┌────────────────────────────────────┐\n│  Your tooling                      │                │  Your Godot 4 project              │\n│                                    │   WebSocket    │                                    │\n│   ┌──────────────────────────┐     │   JSON-RPC     │   ┌────────────────────────────┐   │\n│   │ godot-cli-control CLI    │     │   127.0.0.1    │   │ addons/godot_cli_control/  │   │\n│   │ GameClient   (async)     │ ◄──────────────────► │   │   GameBridgeNode autoload  │   │\n│   │ GameBridge   (sync)      │     │   :\u003cport\u003e      │   │   LowLevelApi              │   │\n│   │ pytest fixtures          │     │   (auto)       │   │   InputSimulationApi       │   │\n│   └──────────────────────────┘     │                │   └────────────────────────────┘   │\n└────────────────────────────────────┘                └────────────────────────────────────┘\n```\n\n`\u003cport\u003e` is OS-assigned by default; the actual value is written to `.cli_control/port` when the daemon starts. CLI subcommands and `GameClient()` (no port arg) auto-discover it from there.\n\nThe plugin is **off by default** even when enabled — see [Activation modes](addons/godot_cli_control/README.md#activation-modes). The server binds `127.0.0.1` only; PID/port files are mode `0600`; release builds are unconditionally disabled. See [Security model](addons/godot_cli_control/README.md#security-model).\n\n## Use cases\n\n- **Black-box automated testing.** Drive your scene from pytest, assert on properties.\n- **CI visual regression.** Screenshot a known state, diff against a golden PNG.\n- **Demo recording.** Run a script that walks the scene, record a movie via Godot's Movie Maker (GUI mode).\n- **AI agents.** Claude Code / Codex pick up the SKILL.md and can immediately operate your scene.\n- **Bug repros.** Capture a sequence of inputs in a script and replay deterministically.\n\n## API at a glance\n\nEvery RPC has both a CLI subcommand and a `GameClient` method — pick whichever fits your harness. Default output is a JSON envelope (`--text` for legacy strings).\n\n| Category | CLI | GameClient |\n|---|---|---|\n| Scene tree | `tree`, `children`, `exists` | `get_scene_tree`, `get_children`, `node_exists` |\n| Inspection | `get` (multi-prop = atomic same-frame read), `text`, `visible`, `sprite-info` | `get_property`, `get_properties`, `get_text`, `is_visible`, `sprite_info` |\n| Mutation   | `set`, `call`, `click` | `set_property`, `call_method`, `click` |\n| Input      | `press`, `release`, `tap`, `hold`, `combo`, `combo-cancel`, `release-all`, `pressed`, `actions` | `action_press`, `action_release`, `action_tap`, `hold`, `combo`, `combo_cancel`, `release_all`, `get_pressed`, `list_input_actions` |\n| Waiting    | `wait-node`, `wait-prop`, `wait-signal`, `wait-frames`, `wait-time` | `wait_for_node`, `wait_property`, `wait_signal`, `wait_frames`, `wait_game_time` |\n| Scene isolation | `scene-reload`, `scene-change` | `scene_reload`, `scene_change` |\n| Time control | `time-scale`, `pause`, `unpause`, `step-frames` | `time_scale`, `pause`, `unpause`, `step_frames` |\n| Render     | `screenshot` (path required, `--node` crops to a node's screen rect) | `screenshot` |\n| Diagnostics | `errors` (structured `push_error` log, Godot 4.5+), `daemon logs` | `errors` |\n\nFull RPC reference (signatures, error codes, blacklist): [plugin README](addons/godot_cli_control/README.md#rpc-reference). Output contract \u0026 exit codes: see the AI Quickstart in any project's `.claude/skills/godot-cli-control/SKILL.md`.\n\n## Repository layout\n\n```\ngodot-cli-control/\n├── addons/godot_cli_control/   # Godot 4 plugin (drop into your project's addons/)\n├── python/                     # Python client + CLI (pip-installable)\n├── examples/platformer-demo/   # runnable demo (clone-and-run + hero-GIF source)\n└── .github/workflows/          # CI + release packaging\n```\n\nEach subdirectory has its own README:\n\n- [`addons/godot_cli_control/README.md`](addons/godot_cli_control/README.md) — plugin install, RPC reference, activation modes, security model, known limitations\n- [`python/README.md`](python/README.md) — Python `GameClient` / `GameBridge` API + CLI usage + pytest fixtures\n\n## Agent integration\n\nWhen `godot-cli-control init` runs, two `SKILL.md` files are dropped under your Godot project root:\n\n- `.claude/skills/godot-cli-control/SKILL.md` (Claude Code)\n- `.codex/skills/godot-cli-control/SKILL.md` (Codex)\n\nBoth are rendered from the same template and pin the current CLI version + `--help` output, so an agent loaded into your project can immediately see the full command surface, the `GameClient` API, and the `def run(bridge)` script convention. After upgrading the CLI (`pipx upgrade godot-cli-control`), refresh both with:\n\n```bash\ngodot-cli-control init --skills-only\n```\n\nRe-running plain `godot-cli-control init` also works and additionally refreshes the bundled `addons/godot_cli_control/` plugin to the new version (pass `--keep-addon` if you've deliberately modified your copy).\n\nIf you've hand-edited a `SKILL.md` and want to keep your version:\n\n- `godot-cli-control init --no-skills` — skip skill writes entirely going forward\n- `godot-cli-control init --skills-no-clobber` — keep existing files, only fill in missing ones\n\nThe two `--no-*` flags are mutually exclusive with each other; `--skills-no-clobber` is orthogonal and may be combined with `--skills-only`.\n\n\u003e Optional: add `.claude/` and `.codex/` to your project's `.gitignore` if you don't want the SKILL.md files committed. They are reproducible at any time via `godot-cli-control init --skills-only`.\n\n## Manual install (advanced)\n\nIf you don't want `init`, copy the plugin manually and enable it from the editor — see the [plugin README](addons/godot_cli_control/README.md#manual-setup-if-you-prefer) for the long-form walkthrough. The legacy wrappers (`bin/run_cli_control.sh` / `.ps1`) are kept as compatibility shims but **deprecated since 0.1.6 and scheduled for removal in 0.3.0** — new code should call `godot-cli-control \u003csubcommand\u003e` directly.\n\n## Recent changes\n\n- **Wait primitives:** `wait-prop` (poll a property until it matches, with `--op` / `--tolerance`), `wait-signal` (arm before triggering), `wait-frames` (deterministic frame advance) — replace sleep-and-hope polling.\n- **Scene isolation:** `scene-reload` / `scene-change` block until the new scene is ready; the pytest `fresh_scene` fixture gives each test a pristine scene.\n- **Time control:** `time-scale` (read/write `Engine.time_scale`), `pause` / `unpause`, and `step-frames` for deterministic stepping while paused.\n- **Visual-state assertions:** `sprite-info` aggregates a sprite's render state (effective atlas region, frame, flips, modulate) in one call; `screenshot --node` crops to a node's screen rect for pixel-level asserts.\n- **Diagnostics:** `errors` queries structured `push_error` / `push_warning` logs with a cursor (Godot 4.5+); `daemon logs --tail N` reads the daemon log even post-mortem; pytest gains `no_push_errors` (fail on silent errors) and automatic failure screenshots.\n- **BREAKING (0.2.x):** `get` returns compound Variants (Vector2, Color, …) as `{\"value\": [x, y], \"type\": \"Vector2\"}` instead of the old `\"(x, y)\"` string — the `value` layout round-trips into `set`. `get \u003cpath\u003e \u003cprop1\u003e \u003cprop2\u003e` reads multiple properties atomically in one frame.\n\nFull history: [`CHANGELOG`](addons/godot_cli_control/CHANGELOG.md).\n\n## Status\n\nAlpha. Published on PyPI (`pipx install godot-cli-control`); Godot AssetLib submission pending ([#18](https://github.com/ClaymanTwinkle/godot-cli-control/issues/18)). Current version: see [`CHANGELOG`](addons/godot_cli_control/CHANGELOG.md).\n\n## Roadmap\n\nTracked in [issues](https://github.com/ClaymanTwinkle/godot-cli-control/issues). Remaining open headline items:\n\n- AssetLib first submission ([#18](https://github.com/ClaymanTwinkle/godot-cli-control/issues/18))\n\n## Contributing\n\nIssues and PRs welcome. The plugin's GUT unit tests run via `./addons/godot_cli_control/tests/run_gut.sh` (bash) or the cross-platform `python addons/godot_cli_control/tests/run_gut.py` (what CI uses) — both require `GODOT_BIN`. The Python package's tests run via `pytest` from `python/`. CI matrix covers Linux, macOS, and Windows.\n\n## License\n\nMIT — see [`LICENSE`](LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclaymantwinkle%2Fgodot-cli-control","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fclaymantwinkle%2Fgodot-cli-control","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fclaymantwinkle%2Fgodot-cli-control/lists"}