GMLoop CLI Package
July 18, 2026 · View on GitHub
Command-line interface for the GMLoop toolchain. Provides utilities for formatting GameMaker Language files, watching for changes, generating metadata, and coordinating the hot-reload development pipeline.
If you are just getting started, begin with the repository quick start, then use this guide as the command reference.
Formatter/Linter/Refactor contract
- Run
refactorto execute global transactions (Codemods), atomic cross-file edits (e.g. rename transactions), and metadata updates via a native Collection API through@gmloop/refactor. - Run
parseto inspect.gmlparser AST output through@gmloop/parser. - Run
lintfor syntax diagnostics and single-file repairs/rewrites (applies local ESLint diagnostics/fixes through@gmloop/lint). - Run
formatfor layout-only formatting (formatter-only AST normalization + printing through@gmloop/format). - Run
fixto execute all three in one pass: project codemods, lint autofixes, and formatting.
Commands
agent-pack init - Initialize or Update Auto-Game Agent Resources
Materializes the independently published @gmloop/agent-pack into a GameMaker
project. The applicable resources include standard skills under
.agents/skills/ and portable project guidance such as AGENTS.md where
needed. Repeat and upgrade runs preserve project-authored or project-modified
content and report conflicts rather than silently overwriting it.
Initialization also creates or extends the project-root .gitignore by default
with .gmloop/, .gmcache/, node_modules/, .playwright-mcp/, and
.agents/skills/**/gmloop-*. Existing rules remain byte-for-byte intact and
equivalent patterns are not duplicated. Pass --no-gitignore to leave
.gitignore untouched.
Agent MCP integration setup is CLI-owned by each provider. GMLoop detects
Codex, Gemini/Antigravity, and Qwen project config state for reporting, but it
does not edit third-party agent config files directly. Automatic setup is
limited to providers with verified project-scoped CLI support; v1 configures
Qwen through qwen mcp add --scope project. Use --agents detected (default),
--agents qwen, --agents all, or --agents none to control which provider
CLI setup attempts run. Codex and Gemini/Antigravity are reported as manual
until their CLIs expose verified project-scoped MCP setup.
The CLI consumes the standalone package and does not own a duplicate skill
collection. For GMLoop maintainers, the agent-pack's skills/ directory is the
sole packaged-collection source. Drop a standard gmloop-<name>/SKILL.md directory
there and it is published and included by initialization automatically; no
registry, manifest, name list, or skill-specific loading code is required.
gmloop agent-pack init --path path/to/Game.yyp
gmloop agent-pack init --path path/to/Game.yyp --no-gitignore
gmloop agent-pack init --path path/to/Game.yyp --agents qwen
The target must resolve to a GameMaker project root containing a .yyp file.
GMLoop's own repository-development .agents/skills are not a source for this
command or for Auto-Game discovery.
Game projects that only need the raw resources can install them directly with
npm install -D @gmloop/agent-pack without installing the GMLoop CLI or the rest of
the monorepo tooling, then inspect, copy, or point compatible tooling at the
package's standard collection. Package installation does not mutate project
files, and the package has no separate executable. gmloop agent-pack init is
the sole command-line initializer. The Auto-Game UI uses the CLI host to expose explicit initialize or update
actions when the opened project's recorded pack version is missing or older than
the version available to GMLoop.
The shipped content is a standard Agent Skills collection, not a custom bundle:
each skills/gmloop-<name>/SKILL.md uses the same gmloop--prefixed frontmatter name, YAML frontmatter, and Markdown and may include
the specification's standard supporting directories. GMLoop uses gray-matter
to extract UI metadata and delegates conformance checks to the official
skills-ref tool or another established standards-compatible validator. It
does not maintain a custom skill parser or validator.
Packaged skills should remain self-contained and AI-client neutral. Describe capabilities and expected outcomes rather than depending on another skill, a specific LLM vendor, MCP server, provider adapter, or tool command name.
format - Format GML Files
Wraps the Prettier plugin to format GameMaker Language files with enhanced diagnostics and error handling (targets .gml files only).
pnpm run cli -- format path/to/project
# Implicit format mode with path-only input
pnpm run cli -- path/to/project
# Show format command options while using path-only invocation style
pnpm run cli -- path/to/project --help
Normal format runs now keep output focused on results and diagnostics. Use --list when you want a full dump of resolved command settings before execution.
Options:
--path <path>- Target file or directory path (defaults to current working directory)--config <path>- Path togmloop.jsonused for formatter-owned options--write- Apply formatting changes (without this flag, format runs in dry-run mode)--log-level <level>- Set Prettier log level (debug, info, warn, error, silent)--verbose- Emit per-file timing and total run duration diagnostics--on-parse-error <action>- How to handle parse errors (skip, revert, abort)--list- Print the effective format command settings and exit without scanning files--ignored-file-sample-limit <n>- Limit ignored file samples in output--unsupported-extension-sample-limit <n>- Limit unsupported extension samples
Environment Variables:
PRETTIER_PLUGIN_GML_LOG_LEVEL- Default log levelPRETTIER_PLUGIN_GML_ON_PARSE_ERROR- Default parse error strategy
parse - Parse GML Files to AST JSON
Runs @gmloop/parser over a .gml file or directory target. Dry-run mode writes AST JSON to stdout. With --write, the command writes sibling *.ast.json files next to each parsed .gml file.
pnpm run cli -- parse --path path/to/script.gml
pnpm run cli -- parse --path path/to/project > ast.json
pnpm run cli -- parse --write --path path/to/project
Options:
--path <path>- Target.gmlfile or directory path (defaults to current working directory)--write- Write AST JSON files (without this flag, parse prints AST JSON to stdout)--list- Print effective command settings and exit--verbose- Emit per-file parse diagnostics to stderr
lint - Lint and Auto-Fix GML Files
Runs @gmloop/lint over one or more paths, with optional ESLint autofix support.
pnpm run cli -- lint path/to/project
pnpm run cli -- lint --write path/to/project
Options:
--write- Apply automatic fixes--warn-ignored- Include ignored-file warnings from ESLint (disabled by default to reduce noisy output)--formatter <name>- Formatter output (stylish|json|checkstyle)--max-warnings <count>- Fail when warning count exceeds limit--config <path>- Path to a custom/overridegmloop.json--no-default-config- Disable bundled fallback config--path <path>- Force project root directory or.yyppath--project-strict- Fail when linted files are outside forced project root--quiet- Suppress fallback/config discovery warnings--verbose- Emit per-file lint/format timing and total run duration diagnostics
lint processes targets file-by-file in sequence. With --write, each processed file path is emitted immediately to stderr as progress output while fixes are written incrementally.
Post-lint config inspections (overlay wiring and processor policy enforcement) also run sequentially per file to bound peak memory usage on very large project scans.
Config resolution prefers a discovered ESLint flat config when one exists. If no
flat config is found, lint discovers gmloop.json from the lint root and
applies the bundled GML config with project lintRuleset and lintRules
overrides. Projects with gmloop.json do not need an eslint.config.* file for
the CLI or UI lint-fix workflow.
If you use a custom ESLint flat config and enable Feather or performance
overlay rules, make sure the matching config entry also wires the canonical GML
plugin and language (plugins: { gml: Lint.plugin } and
language: "gml/gml"). Otherwise the CLI now emits an actionable
GML_OVERLAY_WITHOUT_LANGUAGE_WIRING warning that points back to the affected
files.
lint does not build project-wide semantic indexes or coordinate cross-file fixes. --path only scopes out-of-root warnings and --project-strict enforcement for the current invocation. Project-wide identifier indexing, rename safety, codemods, and hoist-name generation belong in @gmloop/refactor.
If a target does not contain any .gml files, lint now prints an explicit
guidance message explaining that only .gml sources are processed and includes
an example invocation.
Passing an explicit path that resolves to an existing file with a non-.gml
extension (for example, gmloop lint src/scripts/player.ts) is now rejected
with exit code 2 and a clear error listing the offending paths, because the
command is scoped to GameMaker Language sources and would otherwise silently
run ESLint with whatever config happens to live alongside the input. Point at a
.gml file, a directory containing .gml sources, or use --path to target a
GameMaker project (.yyp) or directory. For non-.gml files, run ESLint
directly.
fix - Project-Wide Fix Workflow
Runs the project-wide write workflow in one command:
refactor codemod --writelint --writeformat
pnpm run cli -- fix --path path/to/project
pnpm run cli -- fix --only namingConvention
Options:
--path <path>- Explicit GameMaker project root directory or.yyppath--config <path>- Path to a custom/overridegmloop.json--write- Apply writes for the full workflow (without this flag, workflow runs dry-run)--only <ids>- Comma-separated list of configured refactor codemod ids to run--verbose- Enable verbose diagnostics for all three stages
fix is intentionally project-scoped and write-only. It runs the configured codemod set first so cross-file/project-aware edits happen before single-file lint fixes and final formatting normalization.
Shared project operation state
Project-scoped format, lint, fix, and refactor commands acquire one
exclusive lease in <project>/.gmloop/operation-state.lock and publish their
active phase, output lines, and completion record in
<project>/.gmloop/operation-state.json. The lock is authoritative across
processes and concurrent MCP calls, so a second operation reports a conflict
instead of repeating semantic analysis or mutating the same project in
parallel.
The CLI, MCP tools that delegate to the CLI runner, and the graph UI host all
use that project-local state. The UI's /api/fix/progress response reads the
same active operation and output history, while semantic facts remain owned by
the canonical <project>/.gmloop/graph-index.sqlite store. Live Reload keeps
its long-lived worker/session identity in the related
<project>/.gmloop/live-reload-session.json registry and status endpoint, which
are likewise shared by the UI, CLI, and MCP session controller.
mcp - Start MCP Stdio Server
Starts the GMLoop MCP server (@gmloop/mcp) over stdio so MCP clients can
invoke CLI-derived tools through a single gmloop entrypoint.
pnpm run cli -- mcp
This command is intended to be launched by an MCP host process, not from the in-process CLI test/capture runner.
project info / project setup - Project Metadata and Setup
project info reports read-only project metadata: the project root, .yyp manifest, gmloop.json status, installed Auto-Game agent-pack status, project skills, resource inventory, semantic graph summary, and configured companion-tool MCP availability.
project setup initializes and verifies GMLoop-owned readiness checks: config, graph, resource inventory, agent pack, parser status, and companion-tool availability.
gmloop project info --path path/to/Game.yyp --json
gmloop project setup --path path/to/Game.yyp --json
Official gm-cli
GMLoop does not wrap or mirror the official GameMaker CLI. Use gm-cli
directly for GameMaker-native workflows such as manual lookup, ResourceTool
project edits, compile/package/run, and ResourceTool MCP hosting.
npx @gamemaker/gm-cli@latest manual read "data structures"
npx @gamemaker/gm-cli@latest resourcetool eval "resource list"
npx @gamemaker/gm-cli@latest resourcetool mcp path/to/project.yyp
npx @gamemaker/gm-cli@latest compile --target=html5
npx @gamemaker/gm-cli@latest init --name="TestGame" --ai --actions --no-interactive --template "Blank Pixel Game"
GMLoop keeps its own graph-backed read/query commands such as resource list,
resource inspect, room list, and room inspect, but it does not own
GameMaker-native mutation/manual/MCP command surfaces that already live in
gm-cli.
Use gmloop gm-cli capability-audit --json to compare the current GMLoop
CLI/MCP catalog with the discovered official gm-cli and ResourceTool MCP
catalog. The audit classifies planned autonomous-game capabilities as direct
official-tool usage, GMLoop companion commands, GMLoop-native gaps, or deferred
work so new commands do not duplicate ResourceTool mirrors by accident.
When the graph visualization Config page is open, GMLoop inspects the
configured external gm-cli ResourceTool MCP server definition from the local
MCP config files and renders the live tools/list catalog directly from that
server instead of hardcoding a mirrored tool list.
watch - Monitor Files for Hot-Reload Pipeline
NEW: Now integrated with the transpiler to generate JavaScript patches when GML files change.
Watches GML source files and coordinates the hot-reload development pipeline. When files change, the command:
- Detects file system changes (native or polling)
- Reads modified GML source code
- Transpiles GML to JavaScript using the transpiler module
- Generates hot-reload patches with script IDs
- Streams patches to runtime wrapper via WebSocket
Scripts containing multiple top-level functions are split into one patch per
function and sent as a single websocket batch. This keeps each function bound
to its generated GameMaker runtime symbol; executable statements outside those
functions are emitted as a separate file-level patch. Compile-time directives
such as macros and regions are not treated as runtime function bodies. The
startup scan builds one deterministic project-wide macro table, so a function
patch can use a #macro declared in another GML resource. Macro definitions
and uses are tracked as gml/macro/<name> dependencies; changing a macro
resource, including a replacement-value or chained-definition change,
retranspiles its dependent patches. A file that combines executable top-level
statements with a same-named function is rejected because GameMaker has no
runtime binding for a second file-level initialization patch.
# Basic usage - watch current directory
pnpm run cli -- watch
# Watch specific directory with verbose output
pnpm run cli -- watch /path/to/project --verbose
# Auto-inject hot-reload runtime and start watching
pnpm run cli -- watch /path/to/project --auto-inject
Options:
[targetPath]- Directory to watch (default: current directory)--polling- Use polling instead of native file watching--polling-interval <ms>- Polling interval in milliseconds (default: 1000)--verbose- Enable verbose logging with detailed transpilation output--quiet- Suppress non-essential output (only show errors and server URLs); useful for CI/CD or background processes--debounce-delay <ms>- Delay in milliseconds before transpiling after file changes (default: 100, set to 0 to disable debouncing)--max-concurrent-dirs <count>- Maximum number of directories to scan concurrently during initial file discovery (default: 4); increase for faster scans on systems with more resources, or decrease to avoid file handle exhaustion on resource-constrained systems--max-patch-history <count>- Maximum number of patches to retain in memory (default: 100)--websocket-port <port>- WebSocket server port for streaming patches (default: 17890)--websocket-host <host>- WebSocket server host for streaming patches (default: 127.0.0.1)--no-websocket-server- Disable WebSocket server for patch streaming--status-port <port>- HTTP status server port for querying watch command status (default: 17891)--status-host <host>- HTTP status server host for querying watch command status (default: 127.0.0.1)--no-status-server- Disable HTTP status server--runtime-root <path>- Path to the HTML5 runtime assets--runtime-package <name>- Package name for the HTML5 runtime (default: gamemaker-html5)--no-runtime-server- Disable starting the HTML5 runtime static server--auto-inject- NEW: Automatically inject the hot-reload runtime wrapper into the HTML5 output directory before starting the watcher (default: false)--html5-output <path>- Path to the HTML5 output directory for auto-injection (overrides auto-detection; used with--auto-inject)--gm-temp-root <path>- Root directory for GameMaker HTML5 temporary outputs (default:/private/tmp/GameMakerStudio2/GMS2TEMP; used with--auto-inject)
Note: The --verbose and --quiet flags cannot be used together.
Example Output:
Watching: /path/to/project
Extensions: .gml
Mode: native
WebSocket patch server ready at ws://127.0.0.1:17890
Waiting for file changes... (Press Ctrl+C to stop)
[2025-11-05T18:28:54.771Z] change: example.gml
↳ Read 7 lines
↳ Transpiled to JavaScript (234 chars in 2.45ms)
↳ Generated patch: gml/script/example
↳ Streamed to 1 client(s)
^C
--- Transpilation Statistics ---
Total patches generated: 15
Total transpilation time: 42.13ms
Average transpilation time: 2.81ms
Total source processed: 12.45 KB
Total output generated: 8.23 KB
Output/source ratio: 66.1%
Fastest transpilation: 1.23ms (simple_script.gml)
Slowest transpilation: 5.67ms (complex_script.gml)
-------------------------------
Live-Reload Workflow:
Use the dedicated live-reload command group instead of mixing manual injection with watch:
# Build the configured GameMaker project into the live-reload HTML5 output
pnpm run cli -- live-reload build /path/to/project
# Prepare an existing HTML5 output explicitly
pnpm run cli -- live-reload prepare --html5-output /path/to/output
# Attach to or start the managed live-reload session (builds first when configured)
pnpm run cli -- live-reload session --path /path/to/project
# Replace or stop the managed project session
pnpm run cli -- live-reload session --path /path/to/project --force-start
pnpm run cli -- live-reload session --path /path/to/project --stop
The live-reload session command will:
- Resolve the canonical GameMaker project root and project-local
.gmloop/live-reload-session.jsonregistry. - Attach to a healthy registered session for that project unless
--force-startis set. - Resolve
runtime.liveReloadfromgmloop.json. - Build the GameMaker project into
runtime.liveReload.html5Outputwhenruntime.liveReload.buildis configured. - Otherwise, locate an existing HTML5 output via
--html5-output,runtime.liveReload.html5Output, or the latest GameMaker temp output. - Copy the self-contained
@gmloop/runtime-wrapper/dist/src/browserasset tree into the output directory. - Inject a single module bootstrap tag into
index.html. - Start the file watcher plus the runtime, patch, and status servers against that prepared HTML5 output, then write the session registry.
The injected bootstrap uses the configured --websocket-host, --websocket-port, --status-host, and --status-port values so the running game can connect deterministically to the live-reload services.
Human and JSON status output report the runtime URL, status URL, and WebSocket URL separately so automation does not confuse the playable runtime with the status endpoint.
The watcher ignores generated/cache directories such as .gmcache, .gml-hot-reload, dist, and node_modules so the patch stream stays focused on project-owned GML sources.
Project config for one-click graph UI startup:
When using graph visualize --serve, the Start Live Reload button uses the same managed session controller as live-reload session. It resolves the project-local .gmloop/live-reload-session.json registry first, adopts a healthy session when one exists, and returns that session's exact runtime, status, and WebSocket URLs to the UI. A new session receives dynamically allocated status and WebSocket ports, then reads the project's runtime.liveReload build/output configuration before the watcher starts. This keeps the graph UI from probing a fixed status port or creating parallel workers for a project that is already running.
The graph host owns a session only when the UI starts that session itself. Closing the graph host does not stop a session started by the CLI, MCP, or another graph host; an explicit Stop Live Reload action is the user-directed operation that stops the currently registered project session.
Graph serve verification:
- Inspect or start
live-reload session --path /path/to/project. - Launch
graph visualize --serveand open the Live Reload tab. - Confirm Start returns the registry's unchanged runtime, status, and WebSocket URLs and does not create a second worker.
- Confirm closing the graph host leaves an externally owned session running.
- Use
live-reload session --path /path/to/project --stopfor explicit cleanup.
{
"runtime": {
"liveReload": {
"build": {
"backend": "auto",
"configuration": "Default"
},
"html5Output": "build/html5",
"gmTempRoot": ".gm-temp/html5"
}
}
}
runtime.liveReload.html5Output- Stable HTML5 output directory to inject and serve for live reload. Relative paths resolve from the project root.runtime.liveReload.gmTempRoot- Optional fallback root for GameMaker temporary HTML5 outputs whenhtml5Outputis not set. Relative paths resolve from the project root.runtime.liveReload.build.backend- Build backend selection:auto(default),gm-cli, origor.runtime.liveReload.build.project- Optional explicit.yyppath. Defaults to the single.yypdiscovered under the selected project root.runtime.liveReload.build.configuration- Optional GameMaker build configuration name. Defaults toDefault.runtime.liveReload.build.toolPath- Optional explicit path togm-clior Igor.runtime.liveReload.build.runtimeRoot- Optional explicit GameMaker runtime root for Igor.runtime.liveReload.build.userFolder- Optional explicit GameMaker user folder for Igor.runtime.liveReload.build.licenseFile- Optional explicit GameMaker license plist path.runtime.liveReload.build.cacheDir- Optional explicit build cache directory.runtime.liveReload.build.tempDir- Optional explicit build temp directory for Igor.runtime.liveReload.build.extraArgs- Optional extra command-line arguments appended to the selected backend invocation.
When runtime.liveReload.build is present, Start Live Reload and live-reload dev always rebuild the HTML5 export into runtime.liveReload.html5Output before injection and watcher startup.
Build prerequisites:
gm-clior an installed GameMaker runtime with Igor must be available.- Command-line GameMaker builds still require a valid GameMaker login/license context.
- Igor builds require either a license plist or a GameMaker user folder. Configure them explicitly when auto-detection is not sufficient.
Debouncing File Changes:
The watch command includes intelligent debouncing to prevent unnecessary transpilations when files change rapidly (e.g., during IDE auto-save or when making multiple quick edits). By default, the watcher waits 100ms after the last file change before transpiling, which:
- Reduces system load - Only transpiles once per burst of edits instead of on every keystroke
- Minimizes WebSocket traffic - Sends one patch instead of many rapid updates
- Improves user experience - Provides smoother hot-reload without flicker
- Prevents race conditions - Avoids overlapping transpilations of the same file
Configure the debounce delay with --debounce-delay:
# Use default 100ms debounce
pnpm run cli -- watch
# Increase debounce for slower systems
pnpm run cli -- watch --debounce-delay 500
# Disable debouncing (transpile immediately on every change)
pnpm run cli -- watch --debounce-delay 0
When the watch command stops (via Ctrl+C or abort signal), any pending debounced transpilations are flushed immediately to ensure no work is lost.
Startup Performance:
The watch command is optimized for fast startup, especially when working with large GameMaker projects containing many GML files. Key optimizations include:
- Parallel file scanning - Script names and symbols are collected using concurrent file I/O, significantly reducing startup time for projects with hundreds of files
- Efficient directory traversal - Files in each directory are processed in parallel while directory traversal happens sequentially to avoid overwhelming the file system
- Lazy transpilation - Initial scan only collects script names; full transpilation happens in the background after the watcher is ready
- Targeted dependent invalidation - When exported symbols change, only files that reference the changed symbol names are retranspiled, avoiding unnecessary downstream work during hot reload
- Bounded dependent retranspilation fan-out - Dependent retranspiles run concurrently but respect the watch concurrency cap, reducing latency spikes and filesystem contention when a change impacts many scripts
For projects with 50+ GML files, these optimizations reduce watch command startup time by 3-5x compared to sequential processing. The startup time scales linearly with the number of files rather than exponentially, making the watcher practical for even the largest GameMaker projects.
Quiet Mode:
For CI/CD pipelines, automated testing, or when running the watcher in the background, use --quiet to suppress non-essential output:
# Quiet mode - only shows server URLs and errors
pnpm run cli -- watch --quiet
# Example output in quiet mode:
# Runtime static server ready at http://127.0.0.1:51234
# WebSocket patch server ready at ws://127.0.0.1:17890
#
# (transpilation happens silently, only errors are shown)
# Error: Transpilation failed: Unexpected token at line 5
Quiet mode is particularly useful for:
- CI/CD pipelines where verbose output clutters logs
- Background processes where you only care about errors
- Automated testing environments
- Production-like monitoring setups
Status Server:
The watch command includes an HTTP status server that provides real-time metrics and monitoring without interrupting the watch process. The status server exposes multiple JSON endpoints for different monitoring use cases, from lightweight connectivity checks to comprehensive health status.
# Start watch command (status server runs on port 17891 by default)
pnpm run cli -- watch /path/to/project
Available Endpoints:
GET /status - Comprehensive runtime status with detailed metrics
curl http://127.0.0.1:17891/status
# Example response:
{
"uptime": 125430,
"patchCount": 42,
"totalPatchCount": 12,
"patchHistorySize": 12,
"errorCount": 2,
"recentPatches": [
{
"id": "gml/script/player_move",
"timestamp": 1703890145123,
"durationMs": 2.34,
"filePath": "player_move.gml"
},
...
],
"recentErrors": [
{
"timestamp": 1703890100456,
"filePath": "broken_script.gml",
"error": "Unexpected token at line 5"
}
],
"websocketClients": 1
}
GET /health - Health check with component status (for monitoring systems)
curl http://127.0.0.1:17891/health
# Example response:
{
"status": "healthy",
"timestamp": 1703890200000,
"uptime": 125430,
"checks": {
"transpilation": {
"status": "pass",
"patchCount": 42,
"errorCount": 2
},
"websocket": {
"status": "pass",
"clients": 1
}
}
}
GET /ping - Lightweight connectivity check (minimal overhead)
curl http://127.0.0.1:17891/ping
# Example response:
{
"status": "ok",
"timestamp": 1703890200000
}
GET /ready - Readiness probe (for Kubernetes/container orchestration)
curl http://127.0.0.1:17891/ready
# Example response when ready:
{
"ready": true,
"timestamp": 1703890200000,
"uptime": 125430
}
# Returns HTTP 503 when not ready (excessive errors)
Endpoint Comparison:
| Endpoint | Purpose | Response Size | Use Case |
|---|---|---|---|
/status | Full metrics | Large | Monitoring dashboards, debugging |
/health | Health checks | Medium | Monitoring systems (Prometheus, Datadog) |
/ping | Connectivity | Minimal | Load balancers, simple health checks |
/ready | Readiness | Small | Kubernetes readiness probes, orchestration |
Status Endpoint Fields:
uptime: Milliseconds since the watch command startedpatchCount: Number of successful transpilations in the bounded metrics window, including the startup dependency scantotalPatchCount: Cumulative number of live-edit patches delivered to the runtime since startuppatchHistorySize: Number of live-edit patches retained for late WebSocket client replayerrorCount: Total number of transpilation errors encounteredrecentPatches: Array of the last 10 successful transpilations with metadatarecentErrors: Array of the last 10 errors with detailswebsocketClients: Number of currently connected WebSocket clients
Use Cases:
- Health Monitoring: Integration with monitoring tools (Prometheus, Datadog, etc.) via
/health - Load Balancing: Use
/pingfor lightweight health checks in load balancer configurations - Container Orchestration: Kubernetes readiness/liveness probes using
/readyand/health - CI/CD Pipelines: Automated tests can verify the watch command is processing files via
/status - Debugging: Quickly inspect recent patches and errors without restarting the watcher
- Dashboard Integration: Build custom monitoring dashboards for development teams
Configuration:
# Use custom port
pnpm run cli -- watch --status-port 8080
# Disable status server
pnpm run cli -- watch --no-status-server
Hot-Reload Integration:
The watch command now integrates with the transpiler module (src/transpiler) to generate JavaScript patches on file changes. Each patch contains:
kind: "script" (or "event" in future iterations)id: Symbol identifier (e.g.,gml/script/my_script)js_body: Transpiled JavaScript codesourceText: Original GML source for debuggingversion: Timestamp of transpilation
When one source file produces several function patches, the websocket payload is an array of these patch objects. The runtime wrapper applies the array as a dependency-aware batch, while the status server counts each changed function patch separately.
Planned: Semantic Analysis Integration
The watch command now includes a dependency tracker that maintains file-to-symbol mappings as a foundation for future semantic integration. When files change, the tracker records which symbols they define, preparing the system for dependency-aware hot-reload.
Current Status:
- ✅ Lightweight dependency tracker integrated into watch command
- ✅ Symbol definitions tracked per file
- ✅ Foundation for dependency graph construction
Planned Enhancements:
Future iterations will integrate with the semantic analyzer (src/semantic) to enable full dependency-aware hot-reload:
- Parse changed files to extract AST
- Analyze files semantically to identify symbol definitions and references
- Track project-wide dependency graph (which scripts call which)
- When a file changes, automatically re-transpile dependent scripts
- Coordinate invalidation cascades through the dependency graph
This integration requires expanding the semantic package's public API to export ScopeTracker or providing an alternative coordinator interface. The current design intentionally keeps ScopeTracker internal to enforce use of higher-level semantic coordinators.
Error Recovery and Resilience:
The watch command includes robust error handling to maintain stability:
- Graceful Degradation: Transpilation errors don't stop the watcher. When a file fails to transpile, the error is logged and the watcher continues monitoring other files.
- Error Notifications: Failed transpilations send error notifications to connected WebSocket clients with the format
{ kind: "error", filePath, error, timestamp }. - Patch Validation: All patches are validated before broadcast to ensure they contain valid data (non-empty JavaScript body, proper structure).
- Last Successful Patch Tracking: The system stores the last successful live-edit patch for each script so late WebSocket clients can catch up without replaying the startup dependency scan over code already present in the HTML5 build.
- Error Metrics: Errors are tracked alongside successful transpilations, with statistics displayed when the watcher stops.
- Statistics Summary: On exit, the watch command displays both success metrics (patches generated, transpilation time) and error metrics (total errors, recent error details in verbose mode).
Current Status:
✅ File watching (native and polling modes) ✅ Change detection and file reading ✅ GML → JavaScript transpilation ✅ Patch generation with script IDs ✅ Runtime context initialization ✅ WebSocket server for patch streaming ✅ Real-time patch broadcast to connected clients ✅ Transpilation metrics tracking ✨ ✅ Performance statistics on watch stop ✨
live-reload prepare - Sync Bootstrap Assets
# Prepare the latest detected HTML5 output
pnpm run cli -- live-reload prepare
# Prepare a specific HTML5 output directory
pnpm run cli -- live-reload prepare --html5-output /path/to/html5/output
Options:
--html5-output <path>- Path to the HTML5 output directory--gm-temp-root <path>- Root directory for GameMaker HTML5 temp outputs--websocket-host <host>- WebSocket host for hot-reload patches--websocket-port <port>- WebSocket port for hot-reload patches--status-host <host>- Status server host embedded in the bootstrap config--status-port <port>- Status server port embedded in the bootstrap config--force- Re-inject even if snippet already exists--quiet- Suppress informational output
live-reload build - Build HTML5 Output
Build the configured GameMaker project into the HTML5 output directory used by live reload.
# Build the current project
pnpm run cli -- live-reload build
# Build a specific project directory or .yyp
pnpm run cli -- live-reload build /path/to/project
live-reload build reads runtime.liveReload.build and runtime.liveReload.html5Output from gmloop.json, selects gm-cli or Igor, runs the external GameMaker build, and verifies that the produced output contains index.html.
Options:
--verbose- Print the selected backend command line--quiet- Suppress informational output
When build paths are not explicitly configured, GMLoop discovers the GameMaker toolchain in this order:
- a project-local
.gmcache/runtimes-gms2/runtime-*runtime, then the standard shared GameMaker runtime caches; - Igor under the selected runtime's
bin/igordirectory, then the project's.gmcache/igorcache; - an HTML5-entitled licence from the standard GameMaker user-support folders,
then an HTML5-entitled project-local
.gmcache/license/licence.plist.
Explicit runtimeRoot, toolPath, licenseFile, and userFolder values keep
their precedence. Automatic licence discovery skips cache or user licences
without the HTML5.build_module entitlement. On arm64 macOS, an x64 Igor
executable is launched through Rosetta automatically. The gm-cli cache option
is passed as --cache-dir.
live-reload session - Attach, Start, Replace, Or Stop
Start the full live-reload session for a GameMaker project.
# Build first when runtime.liveReload.build is configured, then watch
pnpm run cli -- live-reload session --path /path/to/project
# Force an existing output directory when build orchestration is not configured
pnpm run cli -- live-reload session --path /path/to/project --html5-output /path/to/html5/output
# Intentionally start a second session for debugging
pnpm run cli -- live-reload session --path /path/to/project --force-start
When runtime.liveReload.build exists, live-reload dev treats runtime.liveReload.html5Output as the canonical output directory, rebuilds it before injection, and serves that same prepared output as the runtime URL shown by the UI.
By default, live-reload session is attach-or-start. If .gmloop/live-reload-session.json points to a healthy session for the same canonical project root, the command returns the existing runtime URL, status URL, WebSocket URL, and status snapshot instead of starting parallel servers.
Igor builds materialize project prefab packages from .gmcache/prefabs into the project-local prefabs path while the build runs, then remove the temporary materialization after the build. This keeps one-click Live Reload startup aligned with GameMaker projects whose package cache already contains the referenced prefab libraries.
If Node's native recursive watcher exhausts file handles after startup, Live Reload closes that watcher and continues with polling instead of shutting down the already-started runtime, WebSocket, and status servers.
✅ Configurable patch history limit ✨ ✅ Error recovery and graceful degradation ✨ ✅ Patch validation before broadcast ✨ ✅ Error notifications to clients ✨ ✅ Last successful patch tracking ✨ ✅ Error statistics and reporting ✨ ✅ Debounced file change handling ✨ ✅ Quiet mode for CI/CD environments ✨ ✅ HTTP status server for runtime monitoring ✨ NEW
🚧 Future Enhancements:
- Semantic analysis integration for scope-aware transpilation
- Dependency tracking to rebuild dependent scripts
- Event transpilation (not just scripts)
- Shader and asset hot-reloading
Managed Session Status
live-reload session returns the current full status snapshot. Health, ping, and readiness endpoints remain available from the returned status URL.
# Attach to an existing session or start one and return JSON status
pnpm run cli -- live-reload session --path /path/to/project
# Render a concise terminal summary
pnpm run cli -- live-reload session --path /path/to/project --format pretty
Options:
--path <project>- Project directory or.yyppath used to locate.gmloop/live-reload-session.json; when omitted, the CLI uses the shared active-project state before falling back to the working directory--force-start- Gracefully replace an active project session--stop- Stop an active project session without starting another--format <format>- Output format:json(default) orpretty
Agents can use live-reload session and live-reload wait-for-patch by project path, or omit the path when tmp/gm-cli-active-project.json identifies the active project. Through MCP these appear as gmloop_live_reload_session and gmloop_live_reload_wait_for_patch.
tmp/gm-cli-active-project.json is the shared active-target state used by the CLI, UI, and MCP-derived workflows. It stores the required projectPath and may also store activeFilePath for file-scoped commands. Resolution order is explicit path, GMLOOP_GM_CLI_PROJECT_PATH, this shared state, then the working directory; activeFilePath is preferred only for file-scoped commands.
MCP starts the detached worker with the explicit worker command environment. The worker does not inherit the parent CLI capture sentinel, so its status endpoint becomes available to the MCP session call instead of waiting for a non-running child.
Example Output:
$ pnpm run cli -- live-reload session --path /path/to/project
=== Live Reload Status ===
Uptime: 2h 15m 43s
Total patches: 42
Total errors: 2
WebSocket clients: 1
Recent patches:
15s ago - scripts/player_move.gml
ID: gml_Script_player_move
Duration: 2.34ms
2m ago - scripts/enemy_ai.gml
ID: gml_Script_enemy_ai
Duration: 3.12ms
Recent errors:
1h ago - scripts/broken_script.gml
Error: Unexpected token at line 5
Use Cases:
- Development Monitoring: Quickly check if the watch command is still processing files correctly
- Debugging: Inspect recent transpilation errors and their timing
- Performance Analysis: Review transpilation duration for optimization
- CI/CD Integration: Use
--format jsonto programmatically check watch command health - Container Health Checks: Query
/healthor/readyendpoints for orchestration systems
This command is particularly useful when:
- The watch command is running in a background terminal or tmux session
- You want to verify hot-reload is working without checking logs
- You need to debug why a script isn't updating in the game
- You're monitoring performance during development
refactor - Safe Code Transformations
Performs safe, project-wide code transformations such as renaming symbols while preserving scope and semantics.
# Rename a script by symbol ID
pnpm run cli -- refactor --symbol-id gml/script/scr_old_name --new-name scr_new_name
# Rename by current name
pnpm run cli -- refactor --old-name player_hp --new-name playerHealth
# Dry run to preview changes
pnpm run cli -- refactor --old-name player_hp --new-name playerHealth
# Validate hot reload compatibility
pnpm run cli -- refactor --old-name player_hp --new-name playerHealth --check-hot-reload
# Verbose output with diagnostics
pnpm run cli -- refactor --old-name player_hp --new-name playerHealth --verbose
# List configured gmloop.json codemods and effective config
pnpm run cli -- refactor codemod --list
# Dry-run configured codemods inferred from the project config
pnpm run cli -- refactor --path path/to/project
# Dry-run configured codemods
pnpm run cli -- refactor codemod
# Apply configured codemods to selected paths only
pnpm run cli -- refactor codemod scripts/player --write
# Apply only one configured codemod
pnpm run cli -- refactor codemod --only namingConvention --write
Options:
--symbol-id <id>- SCIP-style symbol identifier to rename (e.g., gml/script/scr_player)--old-name <name>- Current name of the symbol to rename--new-name <name>- New name for the symbol (required)--path <path>- Root directory of the GameMaker project (default: current directory)--write- Apply the rename/codemod changes (default behavior without--writeis dry-run preview)--verbose- Enable verbose output with detailed diagnostics--check-hot-reload- Validate that the refactored code is compatible with hot reload
Codemod options (refactor codemod):
--config <path>- Explicit path togmloop.json--write- Apply configured codemods (default is dry-run)--only <ids>- Comma-separated list of configured codemod ids to run--list- Print discovered codemods and their effective normalized config
When no rename target is provided, refactor will automatically run configured codemods if it can resolve a gmloop.json for the project.
gmloop.json refactor config:
{
"printWidth": 95,
"lintRules": {
"gml/no-globalvar": "error"
},
"refactor": {
"codemods": {
"namingConvention": {
"rules": {
"localVariable": {
"caseStyle": "camel"
}
}
},
"loopLengthHoisting": {
"functionSuffixes": {
"array_length": "len"
}
}
}
}
}
Ownership note: refactor is a separate domain from lint.
- Use
lint --writefor lint-owned diagnostics/content rewrites. - Use
refactorfor explicit symbol rename/refactor transactions with cross-file edit planning. - Refactor operations are not lint rule fixes and are not executed through formatter runtime adapters.
Use Cases:
- Safe Renaming: Rename variables, scripts, or other symbols project-wide without breaking scope
- Refactoring Preparation: Preview changes by default, then apply with
--writewhen ready - Hot Reload Validation: Ensure refactored code remains compatible with live updates using
--check-hot-reload - Development Workflow Integration: Coordinate with watch mode for real-time refactoring feedback
Current scope:
- Safe rename planning/execution
- Configured codemod execution via
gmloop.json - Dry-run preview support
- Hot-reload validation integration
- Verbose diagnostics for conflict/impact review
generate-gml-identifiers - Generate Identifier Metadata
Generates GML identifier metadata from the GameMaker manual repository.
pnpm run cli -- generate-gml-identifiers
For interactive manual lookup/search, use gm-cli manual read <query> or gm-cli manual open <query> instead of a GMLoop-specific manual-search implementation.
generate-feather-metadata - Generate Feather Metadata
Generates Feather metadata for GameMaker's static analysis.
pnpm run cli -- generate-feather-metadata
graph - Build and Query the Dual-Root Semantic Graph Index
Manages the SQLite-backed dual-root graph index for symbol search, node inspection, and project analysis.
# Build / rebuild the graph index
pnpm run cli -- graph index
pnpm run cli -- graph index --path path/to/project --force
# Search the index
pnpm run cli -- graph search "player"
pnpm run cli -- graph search "player" --path path/to/project
# Validate graph index health
pnpm run cli -- graph doctor --path path/to/project
# Visualize the graph index in the browser
pnpm run cli -- graph visualize --path path/to/project
pnpm run cli -- graph visualize --port 7890
# Export graph visualization as a standalone bundle
pnpm run cli -- graph visualize --path path/to/project --output ./graph-output
Options:
--path <path>- Project root or.yypfile path--toolset-root <path>- Optional toolset root for dual-root indexing--database-path <path>- Override the SQLite database path (default:.gmloop/graph.dbunder the project root)--force- Force full rebuild even when a database already exists--json- Return machine-readable JSON envelope--config <path>- Path togmloop.jsonfor graph config
JSON envelope format:
All --json commands return a stable envelope:
{
"command": "graph search",
"projectRoot": "/path/to/project",
"databasePath": "/path/to/project/.gmloop/graph.db",
"graphIds": ["project"],
"payload": { ... }
}
Ownership note: The graph index is a semantic analysis and retrieval surface.
It does not perform refactoring, formatting, or linting. It is backed by
@gmloop/semantic and exposed through the CLI for human and agent use.
symbol - Inspect Symbols and Relationships
Queries the graph index to inspect symbol definitions, references, and dependencies.
All symbol subcommands accept either a plain identifier (e.g., scr_player) or
a full graph node id (e.g., project::gml/script/scr_player).
# Inspect a symbol by name or graph id
pnpm run cli -- symbol inspect scr_player
pnpm run cli -- symbol inspect project::gml/script/scr_player
# Get context around a symbol (declaration + neighbors)
pnpm run cli -- symbol context project::gml/script/scr_player --depth 3
# Get neighboring symbols
pnpm run cli -- symbol neighbors project::gml/script/scr_player --depth 2
# Find all usages of a symbol
pnpm run cli -- symbol usages project::gml/script/scr_player
resource - Inspect and Generate Project Resources
Inspects indexed project resources or generates standalone resource assets (e.g. placeholder images).
# List all indexed project resources
pnpm run cli -- resource list --path path/to/project
# Search for resources matching a query
pnpm run cli -- resource find scr_player --path path/to/project
# Generate a solid color PNG image of given dimensions (useful for creating placeholder images)
pnpm run cli -- resource create-image tmp/placeholder.png --width 64 --height 64 --color "#ff0000" --json
Options for create-image:
--width <number>- Width of the image in pixels (default:64)--height <number>- Height of the image in pixels (default:64)--color <color>- Color of the image (supports CSS color names likered,transparent, or hex codes like#FF0000or#FF000080for alpha transparency) (default:red)--json- Emit machine-readable JSON output describing the generated file
Architecture
The CLI package serves as the orchestration layer for the hot-reload development pipeline:
┌─────────────────────────────────────────────────────────────┐
│ CLI (src/cli) │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ watch command │ │
│ │ • File system monitoring │ │
│ │ • Change detection │ │
│ │ • Runtime context management │ │
│ │ • Server lifecycle coordination │ │
│ └───────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Transpilation Coordinator ✅ NEW │ │
│ │ • Transpilation lifecycle management │ │
│ │ • Metrics tracking and statistics │ │
│ │ • Patch validation │ │
│ │ • Error handling and recovery │ │
│ │ • WebSocket broadcast coordination │ │
│ └───────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ WebSocket Server ✅ │ │
│ │ • Real-time patch broadcasting │ │
│ │ • Client connection management │ │
│ │ • Automatic reconnection support │ │
│ └───────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
↓
┌────────────────────────────────┐
│ Transpiler (src/transpiler) │
│ • GML parsing │
│ • AST → JavaScript emission │
│ • Operator mapping │
└────────────────────────────────┘
↓
┌────────────────────────────────┐
│ Runtime Wrapper │
│ • WebSocket client ✅ │
│ • Patch application ✅ │
│ • Hot function swapping ✅ │
│ • State preservation │
└────────────────────────────────┘
Module Organization
The CLI package is organized into focused, single-responsibility modules:
Commands (src/commands/)
watch.ts- File system monitoring and hot-reload orchestrationrefactor.ts- Safe, project-wide code transformationsformat.ts- GML code formattinggenerate-gml-identifiers.ts- Identifier metadata generationgenerate-feather-metadata.ts- Feather metadata generation
Modules (src/modules/)
transpilation/- Transpilation coordination and metrics trackingwebsocket/- WebSocket server for patch streamingstatus/- HTTP status server for runtime monitoringruntime/- HTML5 runtime integrationmanual/- GameMaker manual processingfeather/- Feather metadata handling
Development
Running Tests
# All CLI tests
pnpm run test:cli
# Specific test file
node --test src/cli/test/watch-command.test.js
# Watch mode
pnpm run test:cli -- --watch
Testing Watch Command
# Create a test directory
mkdir -p /tmp/gml-test
echo "var x = 10; show_debug_message(x);" > /tmp/gml-test/test.gml
# In another terminal, modify the file to see transpilation
echo "var y = 20;" >> /tmp/gml-test/test.gml
Debugging Transpilation
To see the transpiled JavaScript output for a GML file:
import { createTranspiler } from "./src/transpiler/src/index.js";
import { readFile } from "node:fs/promises";
const transpiler = createTranspiler();
const content = await readFile("path/to/script.gml", "utf8");
const patch = await transpiler.transpileScript({
sourceText: content,
symbolId: "gml/script/my_script"
});
console.log(patch.js_body);
Transpilation Coordinator
The transpilation coordinator module (src/modules/transpilation/coordinator.ts) manages the complete transpilation lifecycle within the watch command:
Key Responsibilities:
- Transpilation Lifecycle: Coordinates the end-to-end process from GML source to validated JavaScript patches
- Metrics Tracking: Records transpilation duration, file sizes, line counts, and performance statistics
- Error Management: Handles transpilation failures gracefully with detailed error tracking
- Patch Validation: Ensures generated patches meet quality requirements before broadcasting
- WebSocket Integration: Coordinates patch broadcasting to connected runtime clients
- Statistics Reporting: Provides comprehensive statistics on watch command exit
API:
import {
transpileFile,
displayTranspilationStatistics
} from "/cli/modules/transpilation";
// Transpile a single file with lifecycle management
const result = transpileFile(
context, // TranspilationContext with transpiler and metrics storage
filePath, // Path to the GML file
content, // GML source code
lines, // Number of lines in the source
{ verbose, quiet } // Output options
);
// Display statistics when watch stops
displayTranspilationStatistics(
context, // Context with metrics and errors
verbose, // Enable detailed statistics
quiet // Suppress all output
);
The coordinator is designed to be a focused, single-responsibility module that handles all transpilation orchestration concerns, keeping the watch command focused on file system monitoring and server lifecycle management.
Integration with Other Modules
Parser (src/parser)
Provides ANTLR-based GML parsing used by the transpiler.
Transpiler (src/transpiler)
✅ Integrated - Converts GML AST to JavaScript for hot-reload patches.
Semantic (src/semantic)
✅ Integrated - Supplies analysis data consumed by refactor planning and hot-reload dependency tracking.
Runtime Wrapper (src/runtime-wrapper)
✅ Ready - Has WebSocket client and patch application, ready to receive patches.
Refactor (src/refactor)
✅ Integrated - Powers explicit cross-file rename/refactor transactions (cli refactor).
References
- Target State Architecture Plan - Overall hot-reload and semantic architecture
- Transpiler README - GML → JavaScript transpilation details
- Runtime Wrapper README - Patch application and hot-swapping
Change Log
Recent Updates
- 2026-01-06: Integrated dependency tracker for watch command
- Added DependencyTracker module for file-to-symbol mapping
- Tracks symbol definitions to enable future dependency-aware invalidation
- Foundation for semantic analysis integration
- Provides statistics on tracked symbols in verbose mode
- Designed for efficient dependency graph queries
- 2025-11-06: Completed hot-reload integration loop
- Added WebSocket server to watch command for real-time patch streaming
- Integrated patch broadcasting to all connected runtime wrapper clients
- Added connection management and client tracking
- Created end-to-end integration test for patch delivery
- Updated documentation with WebSocket configuration options
- Watch command now provides complete hot-reload pipeline from file change to patch delivery
- 2025-11-05: Integrated transpiler into watch command for hot-reload pipeline
- Watch command now transpiles GML files to JavaScript on change
- Generates patch objects with script IDs
- Added verbose logging for transpilation details
- Added test coverage for transpilation integration
- Stores patches in runtime context for future streaming