From 0143f8de4b676772dcd33c3210e6221a025922f9 Mon Sep 17 00:00:00 2001 From: Alessandro Lucchetta <157152299+Gigant88@users.noreply.github.com> Date: Fri, 2 Oct 2026 23:47:14 +0200 Subject: [PATCH 01/12] Fix gm-options parse error message --- src/gm-options.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gm-options.ts b/src/gm-options.ts index bb6eea3..fcb2f9e 100644 --- a/src/gm-options.ts +++ b/src/gm-options.ts @@ -217,7 +217,7 @@ export async function readGmOptions( gmOptions = parseResults.options; } else { throw new KnownError( - "Failed to parse 'gm-options.json file. Errors:\n{parseResults.error}", + `Failed to parse 'gm-options.json file. Errors:\n${parseResults.error}`, ); } From 7dd2daa0d334c28f33bce26c63d893e44679091b Mon Sep 17 00:00:00 2001 From: Alessandro Lucchetta <157152299+Gigant88@users.noreply.github.com> Date: Fri, 2 Oct 2026 23:51:50 +0200 Subject: [PATCH 02/12] Fix repeated package runs with different output names (#243) --- src/gms2/use-gms2.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/gms2/use-gms2.ts b/src/gms2/use-gms2.ts index b9367fb..38e007b 100644 --- a/src/gms2/use-gms2.ts +++ b/src/gms2/use-gms2.ts @@ -144,6 +144,16 @@ export async function useGms2( ); igorAction = action; extraArgs = ["-tf", targetFile, ...packageArgs]; + + // Igor uses the build cache output directory for intermediate package files. + // Clear those intermediates before every package operation so a previous + // package with a different output name cannot leave a file that Igor tries + // to move again. See https://github.com/YoYoGames/gm-cli/issues/243. + await ctx.fs.rm(ctx.path.join(buildCacheDir, "output"), { + recursive: true, + force: true, + }); + successMessage = `Package created: ${targetFile}`; } From 4fe40f1ebda8abcd66b034989b53af74a80d2842 Mon Sep 17 00:00:00 2001 From: Alessandro Lucchetta <157152299+Gigant88@users.noreply.github.com> Date: Sat, 3 Oct 2026 00:00:39 +0200 Subject: [PATCH 03/12] chore: retrigger pull request checks From e1b2fc2018160ed11601750f1bf3d354ee5122f6 Mon Sep 17 00:00:00 2001 From: Alessandro Lucchetta <157152299+Gigant88@users.noreply.github.com> Date: Sat, 3 Oct 2026 00:55:18 +0200 Subject: [PATCH 04/12] Add global plain-output integration --- src/app.ts | 110 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 81 insertions(+), 29 deletions(-) diff --git a/src/app.ts b/src/app.ts index 57ea3b1..bfcba8a 100644 --- a/src/app.ts +++ b/src/app.ts @@ -14,7 +14,13 @@ * limitations under the License. */ -import { buildApplication, buildRouteMap, text_en } from "@stricli/core"; +import { + buildApplication, + buildRouteMap, + help, + text_en, + version as stricliVersion, +} from "@stricli/core"; import { version, description } from "../package.json"; import { initCommand } from "./commands/init/command"; import { compileProjectCommand } from "./commands/compile/command"; @@ -27,6 +33,8 @@ import { KnownError } from "./error"; import { getLatestVersion } from "./get-latest-version"; import { manualCommand } from "./commands/manual/command"; import { cacheCommand } from "./commands/cache/command"; +import { plainTaskLogger } from "./log"; +import type { Context } from "./context"; const routes = buildRouteMap({ routes: { @@ -45,35 +53,79 @@ const routes = buildRouteMap({ }, }); -export const app = buildApplication(routes, { - name: "gm-cli", - versionInfo: { - currentVersion: version, - getLatestVersion, - upgradeCommand: "npm install -g @gamemaker/gm-cli", - }, - documentation: { - caseStyle: "convert-camel-to-kebab", - onlyRequiredInUsageLine: true, - }, - scanner: { - caseStyle: "allow-kebab-for-camel", +export const app = buildApplication( + routes, + { + name: "gm-cli", + versionInfo: { + currentVersion: version, + getLatestVersion, + upgradeCommand: "npm install -g @gamemaker/gm-cli", + }, + documentation: { + caseStyle: "convert-camel-to-kebab", + onlyRequiredInUsageLine: true, + }, + scanner: { + caseStyle: "allow-kebab-for-camel", + }, + localization: { + loadText() { + return { + ...text_en, + exceptionWhileRunningCommand(exc: unknown, _ansiColor: boolean) { + if (exc instanceof KnownError) { + return `Command failed:\\n${exc.message}`; + } + const detail = + exc instanceof Error + ? (exc.stack ?? exc.message) + : JSON.stringify(exc, null, 2); + return `An unexpected error occurred. Please report it as a bug on https://github.com/YoYoGames/gm-cli/issues\\nGM-CLI v${version} ${process.platform}/${process.arch}\\n\\n${detail}`; + }, + }; + }, + }, }, - localization: { - loadText() { - return { - ...text_en, - exceptionWhileRunningCommand(exc: unknown, _ansiColor: boolean) { - if (exc instanceof KnownError) { - return `Command failed:\n${exc.message}`; - } - const detail = - exc instanceof Error - ? (exc.stack ?? exc.message) - : JSON.stringify(exc, null, 2); - return `An unexpected error occurred. Please report it as a bug on https://github.com/YoYoGames/gm-cli/issues\nGM-CLI v${version} ${process.platform}/${process.arch}\n\n${detail}`; + { + help: help({ + brief: text_en.briefs.help, + alias: "h", + defaultForRouteMap: true, + includeHidden: false, + formatting: { + caseStyle: "convert-camel-to-kebab", + onlyRequiredInUsageLine: true, + }, + }), + helpAll: help({ + brief: text_en.briefs.helpAll, + alias: "H", + hidden: true, + includeHidden: true, + formatting: { + caseStyle: "convert-camel-to-kebab", + onlyRequiredInUsageLine: true, + }, + }), + version: stricliVersion({ + brief: text_en.briefs.version, + info: { + currentVersion: version, + getLatestVersion, + upgradeCommand: "npm install -g @gamemaker/gm-cli", + }, + alias: "v", + hook: "app:start", + }), + plainOutput: { + flag: { + brief: "Use plain output.", + global: true, + run() { + (this as Context).makeTaskLogger = plainTaskLogger(); }, - }; + }, }, }, -}); +); From f30c7abfc8531f86195cf3eb25888b4a714fc47d Mon Sep 17 00:00:00 2001 From: Alessandro Lucchetta <157152299+Gigant88@users.noreply.github.com> Date: Sat, 3 Oct 2026 00:55:20 +0200 Subject: [PATCH 05/12] Allow plain-output to replace task logger --- src/context.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/context.ts b/src/context.ts index ed69458..daa9915 100644 --- a/src/context.ts +++ b/src/context.ts @@ -34,7 +34,7 @@ export interface Context extends CommandContext { readonly fetch: typeof globalThis.fetch; readonly open: typeof open; readonly http: typeof http; - readonly makeTaskLogger: TaskLogger; + makeTaskLogger: TaskLogger; readonly env: ParsedEnv; } From 58f791512d15e98cb0c23390c5ae0b4705605942 Mon Sep 17 00:00:00 2001 From: Alessandro Lucchetta <157152299+Gigant88@users.noreply.github.com> Date: Sat, 3 Oct 2026 00:58:49 +0200 Subject: [PATCH 06/12] Use Stricli 1.2.6 compatible plain output handling From 59efb9b84d5b8bd2f4e491b1c5ed8a8b87ad4093 Mon Sep 17 00:00:00 2001 From: Alessandro Lucchetta <157152299+Gigant88@users.noreply.github.com> Date: Sat, 3 Oct 2026 00:58:51 +0200 Subject: [PATCH 07/12] Pass plain output mode into context --- src/context.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/context.ts b/src/context.ts index daa9915..e1965fc 100644 --- a/src/context.ts +++ b/src/context.ts @@ -50,7 +50,7 @@ export async function exists( } } -export function buildContext(process: NodeJS.Process): Context { +export function buildContext(\n process: NodeJS.Process,\n plainOutput = false,\n): Context { const env = getParsedEnv(); return { process, @@ -63,6 +63,6 @@ export function buildContext(process: NodeJS.Process): Context { path, fetch, makeTaskLogger: - env.NO_COLOR === true ? plainTaskLogger() : fancyTaskLogger(), + plainOutput || env.NO_COLOR === true\n ? plainTaskLogger()\n : fancyTaskLogger(), }; } From 74c2a83c2311d7e7db503f2945580c0b3351ad6c Mon Sep 17 00:00:00 2001 From: Alessandro Lucchetta <157152299+Gigant88@users.noreply.github.com> Date: Sat, 3 Oct 2026 00:58:54 +0200 Subject: [PATCH 08/12] Handle global plain-output CLI flag --- src/bin/cli.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/bin/cli.ts b/src/bin/cli.ts index 20778d1..edf43be 100644 --- a/src/bin/cli.ts +++ b/src/bin/cli.ts @@ -17,4 +17,7 @@ import { run } from "@stricli/core"; import { buildContext } from "~/context"; import { app } from "~/app"; -await run(app, process.argv.slice(2), buildContext(process)); +const plainOutput = process.argv.includes("--plain-output"); +const inputs = process.argv.slice(2).filter((arg) => arg !== "--plain-output"); + +await run(app, inputs, buildContext(process, plainOutput)); From 99e6601ee8174831bb4b9b6be2ff835bc4840508 Mon Sep 17 00:00:00 2001 From: Alessandro Lucchetta <157152299+Gigant88@users.noreply.github.com> Date: Sat, 3 Oct 2026 00:59:36 +0200 Subject: [PATCH 09/12] Fix context formatting in plain output change --- src/context.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/context.ts b/src/context.ts index e1965fc..0f95fdb 100644 --- a/src/context.ts +++ b/src/context.ts @@ -50,7 +50,10 @@ export async function exists( } } -export function buildContext(\n process: NodeJS.Process,\n plainOutput = false,\n): Context { +export function buildContext( + process: NodeJS.Process, + plainOutput = false, +): Context { const env = getParsedEnv(); return { process, @@ -63,6 +66,8 @@ export function buildContext(\n process: NodeJS.Process,\n plainOutput = false path, fetch, makeTaskLogger: - plainOutput || env.NO_COLOR === true\n ? plainTaskLogger()\n : fancyTaskLogger(), + plainOutput || env.NO_COLOR === true + ? plainTaskLogger() + : fancyTaskLogger(), }; } From 2df3b8dd1cbb3eef25fbd0035f6ab97c690ad62d Mon Sep 17 00:00:00 2001 From: Alessandro Lucchetta <157152299+Gigant88@users.noreply.github.com> Date: Sat, 3 Oct 2026 01:02:59 +0200 Subject: [PATCH 10/12] Restore Stricli 1.2.6 compatible application config --- src/app.ts | 110 ++++++++++++++--------------------------------------- 1 file changed, 29 insertions(+), 81 deletions(-) diff --git a/src/app.ts b/src/app.ts index bfcba8a..57ea3b1 100644 --- a/src/app.ts +++ b/src/app.ts @@ -14,13 +14,7 @@ * limitations under the License. */ -import { - buildApplication, - buildRouteMap, - help, - text_en, - version as stricliVersion, -} from "@stricli/core"; +import { buildApplication, buildRouteMap, text_en } from "@stricli/core"; import { version, description } from "../package.json"; import { initCommand } from "./commands/init/command"; import { compileProjectCommand } from "./commands/compile/command"; @@ -33,8 +27,6 @@ import { KnownError } from "./error"; import { getLatestVersion } from "./get-latest-version"; import { manualCommand } from "./commands/manual/command"; import { cacheCommand } from "./commands/cache/command"; -import { plainTaskLogger } from "./log"; -import type { Context } from "./context"; const routes = buildRouteMap({ routes: { @@ -53,79 +45,35 @@ const routes = buildRouteMap({ }, }); -export const app = buildApplication( - routes, - { - name: "gm-cli", - versionInfo: { - currentVersion: version, - getLatestVersion, - upgradeCommand: "npm install -g @gamemaker/gm-cli", - }, - documentation: { - caseStyle: "convert-camel-to-kebab", - onlyRequiredInUsageLine: true, - }, - scanner: { - caseStyle: "allow-kebab-for-camel", - }, - localization: { - loadText() { - return { - ...text_en, - exceptionWhileRunningCommand(exc: unknown, _ansiColor: boolean) { - if (exc instanceof KnownError) { - return `Command failed:\\n${exc.message}`; - } - const detail = - exc instanceof Error - ? (exc.stack ?? exc.message) - : JSON.stringify(exc, null, 2); - return `An unexpected error occurred. Please report it as a bug on https://github.com/YoYoGames/gm-cli/issues\\nGM-CLI v${version} ${process.platform}/${process.arch}\\n\\n${detail}`; - }, - }; - }, - }, +export const app = buildApplication(routes, { + name: "gm-cli", + versionInfo: { + currentVersion: version, + getLatestVersion, + upgradeCommand: "npm install -g @gamemaker/gm-cli", }, - { - help: help({ - brief: text_en.briefs.help, - alias: "h", - defaultForRouteMap: true, - includeHidden: false, - formatting: { - caseStyle: "convert-camel-to-kebab", - onlyRequiredInUsageLine: true, - }, - }), - helpAll: help({ - brief: text_en.briefs.helpAll, - alias: "H", - hidden: true, - includeHidden: true, - formatting: { - caseStyle: "convert-camel-to-kebab", - onlyRequiredInUsageLine: true, - }, - }), - version: stricliVersion({ - brief: text_en.briefs.version, - info: { - currentVersion: version, - getLatestVersion, - upgradeCommand: "npm install -g @gamemaker/gm-cli", - }, - alias: "v", - hook: "app:start", - }), - plainOutput: { - flag: { - brief: "Use plain output.", - global: true, - run() { - (this as Context).makeTaskLogger = plainTaskLogger(); + documentation: { + caseStyle: "convert-camel-to-kebab", + onlyRequiredInUsageLine: true, + }, + scanner: { + caseStyle: "allow-kebab-for-camel", + }, + localization: { + loadText() { + return { + ...text_en, + exceptionWhileRunningCommand(exc: unknown, _ansiColor: boolean) { + if (exc instanceof KnownError) { + return `Command failed:\n${exc.message}`; + } + const detail = + exc instanceof Error + ? (exc.stack ?? exc.message) + : JSON.stringify(exc, null, 2); + return `An unexpected error occurred. Please report it as a bug on https://github.com/YoYoGames/gm-cli/issues\nGM-CLI v${version} ${process.platform}/${process.arch}\n\n${detail}`; }, - }, + }; }, }, -); +}); From f7b5b0e62f51de36e16e444e3a5aa9c9fb8af3c3 Mon Sep 17 00:00:00 2001 From: Alessandro Lucchetta <157152299+Gigant88@users.noreply.github.com> Date: Sat, 3 Oct 2026 12:08:25 +0200 Subject: [PATCH 11/12] Recover from corrupted cached runtimes --- src/gms2/install-runtime.ts | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/gms2/install-runtime.ts b/src/gms2/install-runtime.ts index 5f644e4..06222c3 100644 --- a/src/gms2/install-runtime.ts +++ b/src/gms2/install-runtime.ts @@ -233,10 +233,20 @@ export async function installRuntimeIfNeeded( }); let runtimeLocation = await findRuntimeLocation(ctx, runtimeDir, version); - // FIXME: if this fails, we should delete the runtime dir and try again - const installedModules = runtimeLocation - ? await getInstalledRuntimeModules(ctx, runtimeLocation) - : []; + // A runtime directory can be left behind by an interrupted or failed + // installation. In that case receipt.json may be missing or corrupt. + // Treat the cached runtime as invalid and reinstall it instead of failing + // every subsequent build. + let installedModules: Module[] = []; + if (runtimeLocation) { + try { + installedModules = await getInstalledRuntimeModules(ctx, runtimeLocation); + } catch { + log.message(`Cached runtime at '${runtimeLocation}' is invalid; reinstalling...`); + await ctx.fs.rm(runtimeLocation, { recursive: true, force: true }); + runtimeLocation = undefined; + } + } if (runtimeLocation && installedModules.includes(target)) { log.success("Runtime found"); From 3310fa9148bb376f332d1150f6a38683e32ee7b8 Mon Sep 17 00:00:00 2001 From: Alessandro Lucchetta <157152299+Gigant88@users.noreply.github.com> Date: Sat, 3 Oct 2026 12:10:49 +0200 Subject: [PATCH 12/12] Format runtime cache recovery fix --- src/gms2/install-runtime.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gms2/install-runtime.ts b/src/gms2/install-runtime.ts index 06222c3..e2e7b81 100644 --- a/src/gms2/install-runtime.ts +++ b/src/gms2/install-runtime.ts @@ -242,7 +242,9 @@ export async function installRuntimeIfNeeded( try { installedModules = await getInstalledRuntimeModules(ctx, runtimeLocation); } catch { - log.message(`Cached runtime at '${runtimeLocation}' is invalid; reinstalling...`); + log.message( + `Cached runtime at '${runtimeLocation}' is invalid; reinstalling...`, + ); await ctx.fs.rm(runtimeLocation, { recursive: true, force: true }); runtimeLocation = undefined; }