diff --git a/.changeset/fn-7712-grok-model-parse.md b/.changeset/fn-7712-grok-model-parse.md new file mode 100644 index 0000000000..9d012a240c --- /dev/null +++ b/.changeset/fn-7712-grok-model-parse.md @@ -0,0 +1,7 @@ +--- +"@runfusion/fusion": patch +--- + +summary: Fix Grok CLI model picker showing prompt text instead of real model names. +category: fix +dev: Rewrote parseModelLines in fusion-plugin-grok-runtime/process-manager.ts to strip the login/"Default model:"/"Available models:" preamble and `*`/`-` bullet markers plus the `(default)` annotation from verified `grok models` output; legacy `id - Label`, columnar, and JSON paths preserved. diff --git a/plugins/fusion-plugin-grok-runtime/src/__tests__/process-manager.test.ts b/plugins/fusion-plugin-grok-runtime/src/__tests__/process-manager.test.ts index 4ac8c7298e..82f4868d8e 100644 --- a/plugins/fusion-plugin-grok-runtime/src/__tests__/process-manager.test.ts +++ b/plugins/fusion-plugin-grok-runtime/src/__tests__/process-manager.test.ts @@ -16,6 +16,20 @@ const DASH_MODELS_OUTPUT = [ const COLUMN_MODELS_OUTPUT = ["grok-4 $5.00/M in", "grok-4-fast $0.20/M in"].join("\n"); +// FN-7712: verified real `grok models` output shape (attachment 1871.png) — +// login/session preamble, "Default model:" line, "Available models:" header, +// then a bulleted list with `* (default)` for the active model and +// `- ` for the rest. +const REAL_BULLETED_OUTPUT = [ + "You are logged in with grok-cli v1.2.3", + "Default model: grok-4.5", + "Available models:", + "* grok-4.5 (default)", + "- grok-composer-2.5-fast", + "- grok-4-fast-reasoning", + "- grok-3-mini", +].join("\n"); + describe("discoverGrokModels", () => { beforeEach(() => { vi.clearAllMocks(); @@ -39,6 +53,31 @@ describe("discoverGrokModels", () => { expect(result.fallbackUsed).toBe(false); }); + it("extracts clean model ids from the verified real bulleted output, dropping preamble and markers", async () => { + vi.mocked(runGrokCommand).mockResolvedValueOnce({ code: 0, stdout: REAL_BULLETED_OUTPUT, stderr: "" }); + const result = await discoverGrokModels("grok"); + + expect(result.models).toEqual(["grok-4.5", "grok-composer-2.5-fast", "grok-4-fast-reasoning", "grok-3-mini"]); + expect(result.source).toBe("models-text"); + expect(result.fallbackUsed).toBe(false); + for (const model of result.models) { + expect(model).not.toMatch(/^[*-]/); + expect(model).not.toMatch(/\(default\)/i); + } + expect(result.models.some((m) => /logged in|default model|available models/i.test(m))).toBe(false); + }); + + it("strips the ` (default)` suffix marker from a bulleted default-model line", async () => { + vi.mocked(runGrokCommand).mockResolvedValueOnce({ + code: 0, + stdout: ["Available models:", "* grok-4.5 (default)"].join("\n"), + stderr: "", + }); + const result = await discoverGrokModels("grok"); + + expect(result.models).toEqual(["grok-4.5"]); + }); + it("extracts bare ids from columnar/pricing-separated output", async () => { vi.mocked(runGrokCommand).mockResolvedValueOnce({ code: 0, stdout: COLUMN_MODELS_OUTPUT, stderr: "" }); const result = await discoverGrokModels("grok"); diff --git a/plugins/fusion-plugin-grok-runtime/src/process-manager.ts b/plugins/fusion-plugin-grok-runtime/src/process-manager.ts index fe6b681552..923d11e36d 100644 --- a/plugins/fusion-plugin-grok-runtime/src/process-manager.ts +++ b/plugins/fusion-plugin-grok-runtime/src/process-manager.ts @@ -1,28 +1,38 @@ import { runGrokCommand } from "./cli-spawn.js"; /* -FNXC:GrokCli 2026-07-08-00:00: -FN-7705: the exact `grok models` output line shape is -`upstream-pending-verification` — the upstream README documents the command -lists available Grok models "with pricing hints" but does not pin an exact -column/separator format. We parse CONSERVATIVELY: strip obvious header/tip/ -empty-state lines, then take the leading token before a ` - ` label -separator (mirroring the Cursor CLI's ` -