@gobing-ai/ts-ai-runner 0.4.7 → 0.4.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -32,7 +32,7 @@ bun add @gobing-ai/ts-ai-runner
32
32
  | `AGENT_SHIMS` / `TIER1_PRIORITY` / `TIER2_AGENTS` / `DISPLAY_ORDER` | Agent registry constants |
33
33
  | `isAgentName()` | Type guard for supported agent identifiers |
34
34
 
35
- Supported agent identifiers: `claude`, `codex`, `gemini` (deprecated), `pi`, `omp`, `opencode`, `antigravity-cli`, `openclaw`, `hermes`. The `antigravity` id is a deprecated alias of `antigravity-cli`. See [Deprecation & Aliases](#deprecation--aliases).
35
+ Supported agent identifiers: `claude`, `codex`, `gemini` (deprecated), `pi`, `omp`, `opencode`, `antigravity-cli`, `openclaw`, `hermes`, `grok`. The `antigravity` id is a deprecated alias of `antigravity-cli`. See [Deprecation & Aliases](#deprecation--aliases).
36
36
 
37
37
  ## Architecture
38
38
 
@@ -433,6 +433,9 @@ Agent-specific behavior:
433
433
  | `opencode` | `opencode` | 1 | `opencode providers` | `run`, `-c`, `-m`, `--format json` |
434
434
  | `antigravity-cli` | `agy` | 1 | env-only | `-p`, `--continue`, `--model` |
435
435
  | `openclaw` | `openclaw` | 2 | `openclaw health` | `agent --local -m` |
436
+ | `hermes` | `hermes` | 1 | `hermes doctor` | `chat -q`, `--continue`, `-m` |
437
+ | `grok` | `grok` | 1 | env/file (`XAI_API_KEY` or `~/.grok/auth.json`) | `-p`, `-c` (resume), `-m`, `--output-format plain\|json` (maps ai-runner `text` → `plain`) |
438
+
436
439
  This is the right layer for UI previews, audit logging, and custom launchers.
437
440
 
438
441
  ## Deprecation & Aliases
@@ -458,6 +461,7 @@ resolveAgentName('cursor'); // → undefined
458
461
  | `gemini` | deprecated | `gemini` (self) → replaced by `antigravity-cli` | Gemini CLI sunset 2026-06-18. Shim stays functional. |
459
462
  | `omp` | canonical | `omp` | First-class; NOT a pi alias. |
460
463
  | `hermes` | canonical | `hermes` | First-class; OpenClaw-compatible but distinct binary. |
464
+ | `grok` | canonical | `grok` | Grok Build CLI; headless via `-p`; auth is env/file only (no status verb). |
461
465
 
462
466
  `getAgentShim()` and `isAgentName()` are alias-aware: passing `'antigravity'` resolves to the `antigravity-cli` shim. `DoctorResult` and `DetectedAgent` surface `deprecated` + `replacedBy` when the resolved canonical id is marked deprecated.
463
467
 
@@ -37,6 +37,8 @@ export interface AuthContext {
37
37
  * - **gemini** — credential-like content in `~/.gemini/settings.json`.
38
38
  * - **codex** — `codex login status` CLI output, falling back to an
39
39
  * `~/.codex/auth{.json,}` credential file.
40
+ * - **grok** — non-empty `XAI_API_KEY` env, else non-empty `~/.grok/auth.json`;
41
+ * no CLI auth-status verb (shim `getAuthCommand` is null).
40
42
  * - **pi / omp** — a non-empty `GOOGLE_API_KEY` or `ANTHROPIC_API_KEY` in env
41
43
  * (an empty export is not a usable credential), else the CLI auth probe.
42
44
  * - **others** — the shim's auth command; matched against {@link AUTH_PATTERNS}.
@@ -1 +1 @@
1
- {"version":3,"file":"auth-shims.d.ts","sourceRoot":"","sources":["../../src/agents/auth-shims.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAA2B,MAAM,uBAAuB,CAAC;AACjF,OAAO,KAAK,EAAmC,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC9E,OAAO,EAAE,KAAK,SAAS,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvD;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,SAAS,GAAG,eAAe,GAAG,iBAAiB,GAAG,SAAS,CAAC;AAExE,qDAAqD;AACrD,MAAM,WAAW,WAAW;IACxB,0CAA0C;IAC1C,MAAM,EAAE,QAAQ,CAAC;IACjB,6EAA6E;IAC7E,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,0EAA0E;IAC1E,UAAU,EAAE,UAAU,CAAC;IACvB,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AA6CD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,eAAe,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,SAAS,CAAC,CAgB5F;AA8CD,OAAO,EAAE,YAAY,EAAE,CAAC"}
1
+ {"version":3,"file":"auth-shims.d.ts","sourceRoot":"","sources":["../../src/agents/auth-shims.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAA2B,MAAM,uBAAuB,CAAC;AACjF,OAAO,KAAK,EAAmC,QAAQ,EAAE,MAAM,cAAc,CAAC;AAC9E,OAAO,EAAE,KAAK,SAAS,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvD;;;;;;;;;;;;;GAaG;AACH,MAAM,MAAM,SAAS,GAAG,eAAe,GAAG,iBAAiB,GAAG,SAAS,CAAC;AAExE,qDAAqD;AACrD,MAAM,WAAW,WAAW;IACxB,0CAA0C;IAC1C,MAAM,EAAE,QAAQ,CAAC;IACjB,6EAA6E;IAC7E,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;IACzC,0EAA0E;IAC1E,UAAU,EAAE,UAAU,CAAC;IACvB,0CAA0C;IAC1C,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB;AA6CD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,eAAe,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,CAAC,SAAS,CAAC,CAiB5F;AA+DD,OAAO,EAAE,YAAY,EAAE,CAAC"}
@@ -49,6 +49,8 @@ function isNonEmpty(value) {
49
49
  * - **gemini** — credential-like content in `~/.gemini/settings.json`.
50
50
  * - **codex** — `codex login status` CLI output, falling back to an
51
51
  * `~/.codex/auth{.json,}` credential file.
52
+ * - **grok** — non-empty `XAI_API_KEY` env, else non-empty `~/.grok/auth.json`;
53
+ * no CLI auth-status verb (shim `getAuthCommand` is null).
52
54
  * - **pi / omp** — a non-empty `GOOGLE_API_KEY` or `ANTHROPIC_API_KEY` in env
53
55
  * (an empty export is not a usable credential), else the CLI auth probe.
54
56
  * - **others** — the shim's auth command; matched against {@link AUTH_PATTERNS}.
@@ -63,6 +65,8 @@ export async function isAuthenticated(agent, ctx) {
63
65
  return geminiSettingsContainCredentials(fs, home);
64
66
  if (agent === 'codex')
65
67
  return checkCodexAuth(ctx.runner, fs, home, timeout);
68
+ if (agent === 'grok')
69
+ return checkGrokAuth(fs, home, env);
66
70
  // pi and omp read provider keys from the environment; require a non-empty
67
71
  // value rather than mere presence (an empty export is not a usable credential).
68
72
  if ((agent === 'pi' || agent === 'omp') && (isNonEmpty(env.GOOGLE_API_KEY) || isNonEmpty(env.ANTHROPIC_API_KEY))) {
@@ -78,6 +82,20 @@ async function checkCodexAuth(runner, fs, home, timeout) {
78
82
  (await hasNonEmptyFile(fs, joinPath(home, '.codex', 'auth')));
79
83
  return hasFile ? 'authenticated' : 'unknown';
80
84
  }
85
+ /**
86
+ * Grok has no auth-status CLI verb. Credential sources (never false-negative
87
+ * to `unauthenticated` when missing):
88
+ * 1. non-empty `XAI_API_KEY`
89
+ * 2. non-empty `~/.grok/auth.json`
90
+ * Else `unknown`.
91
+ */
92
+ async function checkGrokAuth(fs, home, env) {
93
+ if (isNonEmpty(env.XAI_API_KEY))
94
+ return 'authenticated';
95
+ if (await hasNonEmptyFile(fs, joinPath(home, '.grok', 'auth.json')))
96
+ return 'authenticated';
97
+ return 'unknown';
98
+ }
81
99
  async function geminiSettingsContainCredentials(fs, home) {
82
100
  try {
83
101
  const content = await fs.readFile(joinPath(home, '.gemini', 'settings.json'));
@@ -1,5 +1,5 @@
1
1
  /** Identifier for one supported coding agent (canonical id). */
2
- export type AgentName = 'claude' | 'codex' | 'gemini' | 'pi' | 'opencode' | 'antigravity-cli' | 'openclaw' | 'hermes' | 'omp';
2
+ export type AgentName = 'claude' | 'codex' | 'gemini' | 'pi' | 'opencode' | 'antigravity-cli' | 'openclaw' | 'hermes' | 'omp' | 'grok';
3
3
  /** Output mode for prompt invocations. */
4
4
  export type OutputMode = 'text' | 'json';
5
5
  /** Concrete executable and argv pair returned by an agent shim. */
@@ -1 +1 @@
1
- {"version":3,"file":"shims.d.ts","sourceRoot":"","sources":["../../src/agents/shims.ts"],"names":[],"mappings":"AAEA,gEAAgE;AAChE,MAAM,MAAM,SAAS,GACf,QAAQ,GACR,OAAO,GACP,QAAQ,GACR,IAAI,GACJ,UAAU,GACV,iBAAiB,GACjB,UAAU,GACV,QAAQ,GACR,KAAK,CAAC;AAEZ,0CAA0C;AAC1C,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC;AAEzC,mEAAmE;AACnE,MAAM,WAAW,WAAW;IACxB,4BAA4B;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,2CAA2C;IAC3C,IAAI,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,4CAA4C;AAC5C,MAAM,WAAW,aAAa;IAC1B,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACjE;AAED,2EAA2E;AAC3E,MAAM,WAAW,gBAAgB;IAC7B,4DAA4D;IAC5D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,8CAA8C;IAC9C,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,CAAC;CACnC;AAED,qDAAqD;AACrD,MAAM,WAAW,SAAS;IACtB,yCAAyC;IACzC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,+BAA+B;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,oEAAoE;IACpE,QAAQ,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC;IACrB,sFAAsF;IACtF,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,wFAAwF;IACxF,QAAQ,CAAC,UAAU,CAAC,EAAE,gBAAgB,CAAC;IACvC,oCAAoC;IACpC,cAAc,IAAI,WAAW,CAAC;IAC9B,yCAAyC;IACzC,iBAAiB,IAAI,WAAW,CAAC;IACjC,yCAAyC;IACzC,gBAAgB,CAAC,OAAO,EAAE,aAAa,GAAG,WAAW,CAAC;IACtD,8DAA8D;IAC9D,cAAc,IAAI,WAAW,GAAG,IAAI,CAAC;CACxC;AA+JD,6DAA6D;AAC7D,eAAO,MAAM,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC,CAU9D,CAAC;AAEF,mEAAmE;AACnE,eAAO,MAAM,cAAc,EAAE,SAAS,SAAS,EAQ9C,CAAC;AAEF,kDAAkD;AAClD,eAAO,MAAM,aAAa,EAAE,SAAS,SAAS,EAU7C,CAAC;AAEF,6CAA6C;AAC7C,eAAO,MAAM,YAAY,EAAE,WAAW,CAAC,SAAS,CAAyB,CAAC;AAU1E;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAQrE;AAED;mEACmE;AACnE,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAElD;AAED,6EAA6E;AAC7E,wBAAgB,YAAY,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,CAMxD"}
1
+ {"version":3,"file":"shims.d.ts","sourceRoot":"","sources":["../../src/agents/shims.ts"],"names":[],"mappings":"AAEA,gEAAgE;AAChE,MAAM,MAAM,SAAS,GACf,QAAQ,GACR,OAAO,GACP,QAAQ,GACR,IAAI,GACJ,UAAU,GACV,iBAAiB,GACjB,UAAU,GACV,QAAQ,GACR,KAAK,GACL,MAAM,CAAC;AAEb,0CAA0C;AAC1C,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,CAAC;AAEzC,mEAAmE;AACnE,MAAM,WAAW,WAAW;IACxB,4BAA4B;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,2CAA2C;IAC3C,IAAI,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,4CAA4C;AAC5C,MAAM,WAAW,aAAa;IAC1B,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,2DAA2D;IAC3D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,qDAAqD;IACrD,KAAK,CAAC,EAAE,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACjE;AAED,2EAA2E;AAC3E,MAAM,WAAW,gBAAgB;IAC7B,4DAA4D;IAC5D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,8CAA8C;IAC9C,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,CAAC;CACnC;AAED,qDAAqD;AACrD,MAAM,WAAW,SAAS;IACtB,yCAAyC;IACzC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,+BAA+B;IAC/B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,oEAAoE;IACpE,QAAQ,CAAC,IAAI,EAAE,CAAC,GAAG,CAAC,CAAC;IACrB,sFAAsF;IACtF,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACrC,wFAAwF;IACxF,QAAQ,CAAC,UAAU,CAAC,EAAE,gBAAgB,CAAC;IACvC,oCAAoC;IACpC,cAAc,IAAI,WAAW,CAAC;IAC9B,yCAAyC;IACzC,iBAAiB,IAAI,WAAW,CAAC;IACjC,yCAAyC;IACzC,gBAAgB,CAAC,OAAO,EAAE,aAAa,GAAG,WAAW,CAAC;IACtD,8DAA8D;IAC9D,cAAc,IAAI,WAAW,GAAG,IAAI,CAAC;CACxC;AAuLD,6DAA6D;AAC7D,eAAO,MAAM,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC,CAW9D,CAAC;AAEF,mEAAmE;AACnE,eAAO,MAAM,cAAc,EAAE,SAAS,SAAS,EAS9C,CAAC;AAEF,kDAAkD;AAClD,eAAO,MAAM,aAAa,EAAE,SAAS,SAAS,EAW7C,CAAC;AAEF,6CAA6C;AAC7C,eAAO,MAAM,YAAY,EAAE,WAAW,CAAC,SAAS,CAAyB,CAAC;AAU1E;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS,CAQrE;AAED;mEACmE;AACnE,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAElD;AAED,6EAA6E;AAC7E,wBAAgB,YAAY,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,CAMxD"}
@@ -166,6 +166,31 @@ const ompShim = {
166
166
  },
167
167
  getAuthCommand: () => ({ command: 'omp', args: ['--list-models'] }),
168
168
  };
169
+ /**
170
+ * Grok Build CLI (`grok`) — xAI coding agent. Headless one-shot via `-p`/`--single`;
171
+ * continue with `-c`; model via `-m`. Output formats are `plain`/`json`/`streaming-json`
172
+ * (map ai-runner `text` → `plain`). No auth-status verb — `getAuthCommand` is null;
173
+ * credential probing lives in auth-shims (env / `~/.grok/auth.json`).
174
+ */
175
+ const grokShim = {
176
+ name: 'grok',
177
+ command: 'grok',
178
+ tier: 1,
179
+ getHelpCommand: () => ({ command: 'grok', args: ['--help'] }),
180
+ getVersionCommand: () => ({ command: 'grok', args: ['--version'] }),
181
+ getPromptCommand: (options) => {
182
+ const args = ['-p', options.input ?? ''];
183
+ if (options.continue === true)
184
+ args.push('-c');
185
+ if (options.model !== undefined)
186
+ args.push('-m', options.model);
187
+ // Grok has no `text` format; map ai-runner OutputMode `text` → `plain`.
188
+ const format = (options.mode ?? 'text') === 'json' ? 'json' : 'plain';
189
+ args.push('--output-format', format);
190
+ return { command: 'grok', args };
191
+ },
192
+ getAuthCommand: () => null,
193
+ };
169
194
  /** All bundled agent shims keyed by canonical agent name. */
170
195
  export const AGENT_SHIMS = {
171
196
  claude: claudeShim,
@@ -177,6 +202,7 @@ export const AGENT_SHIMS = {
177
202
  openclaw: openclawShim,
178
203
  hermes: hermesShim,
179
204
  omp: ompShim,
205
+ grok: grokShim,
180
206
  };
181
207
  /** Tier-1 auto-selection priority. Deprecated ids are excluded. */
182
208
  export const TIER1_PRIORITY = [
@@ -187,6 +213,7 @@ export const TIER1_PRIORITY = [
187
213
  'claude',
188
214
  'hermes',
189
215
  'opencode',
216
+ 'grok',
190
217
  ];
191
218
  /** Display order for doctor and list commands. */
192
219
  export const DISPLAY_ORDER = [
@@ -199,6 +226,7 @@ export const DISPLAY_ORDER = [
199
226
  'antigravity-cli',
200
227
  'openclaw',
201
228
  'hermes',
229
+ 'grok',
202
230
  ];
203
231
  /** Set of gateway/TUI-constrained agents. */
204
232
  export const TIER2_AGENTS = new Set(['openclaw']);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gobing-ai/ts-ai-runner",
3
- "version": "0.4.7",
3
+ "version": "0.4.8",
4
4
  "description": "@gobing-ai/ts-ai-runner — Coding-agent shims, detection, doctor checks, and prompt execution.",
5
5
  "keywords": [
6
6
  "typescript",
@@ -47,9 +47,9 @@
47
47
  "release": "echo 'Manual publish is disabled. Releases go through GitHub Actions via Trusted Publishing — push a tag: git tag @gobing-ai/ts-ai-runner-v<version> && git push --tags' && exit 1"
48
48
  },
49
49
  "dependencies": {
50
- "@gobing-ai/ts-db": "^0.4.7",
51
- "@gobing-ai/ts-infra": "^0.4.7",
52
- "@gobing-ai/ts-runtime": "^0.4.7"
50
+ "@gobing-ai/ts-db": "^0.4.8",
51
+ "@gobing-ai/ts-infra": "^0.4.8",
52
+ "@gobing-ai/ts-runtime": "^0.4.8"
53
53
  },
54
54
  "devDependencies": {
55
55
  "@types/bun": "1.3.14"
@@ -83,6 +83,8 @@ function isNonEmpty(value: string | undefined): boolean {
83
83
  * - **gemini** — credential-like content in `~/.gemini/settings.json`.
84
84
  * - **codex** — `codex login status` CLI output, falling back to an
85
85
  * `~/.codex/auth{.json,}` credential file.
86
+ * - **grok** — non-empty `XAI_API_KEY` env, else non-empty `~/.grok/auth.json`;
87
+ * no CLI auth-status verb (shim `getAuthCommand` is null).
86
88
  * - **pi / omp** — a non-empty `GOOGLE_API_KEY` or `ANTHROPIC_API_KEY` in env
87
89
  * (an empty export is not a usable credential), else the CLI auth probe.
88
90
  * - **others** — the shim's auth command; matched against {@link AUTH_PATTERNS}.
@@ -96,6 +98,7 @@ export async function isAuthenticated(agent: AgentName, ctx: AuthContext): Promi
96
98
 
97
99
  if (agent === 'gemini') return geminiSettingsContainCredentials(fs, home);
98
100
  if (agent === 'codex') return checkCodexAuth(ctx.runner, fs, home, timeout);
101
+ if (agent === 'grok') return checkGrokAuth(fs, home, env);
99
102
 
100
103
  // pi and omp read provider keys from the environment; require a non-empty
101
104
  // value rather than mere presence (an empty export is not a usable credential).
@@ -115,6 +118,23 @@ async function checkCodexAuth(runner: AiRunner, fs: FileSystem, home: string, ti
115
118
  return hasFile ? 'authenticated' : 'unknown';
116
119
  }
117
120
 
121
+ /**
122
+ * Grok has no auth-status CLI verb. Credential sources (never false-negative
123
+ * to `unauthenticated` when missing):
124
+ * 1. non-empty `XAI_API_KEY`
125
+ * 2. non-empty `~/.grok/auth.json`
126
+ * Else `unknown`.
127
+ */
128
+ async function checkGrokAuth(
129
+ fs: FileSystem,
130
+ home: string,
131
+ env: Record<string, string | undefined>,
132
+ ): Promise<AuthState> {
133
+ if (isNonEmpty(env.XAI_API_KEY)) return 'authenticated';
134
+ if (await hasNonEmptyFile(fs, joinPath(home, '.grok', 'auth.json'))) return 'authenticated';
135
+ return 'unknown';
136
+ }
137
+
118
138
  async function geminiSettingsContainCredentials(fs: FileSystem, home: string): Promise<AuthState> {
119
139
  try {
120
140
  const content = await fs.readFile(joinPath(home, '.gemini', 'settings.json'));
@@ -10,7 +10,8 @@ export type AgentName =
10
10
  | 'antigravity-cli'
11
11
  | 'openclaw'
12
12
  | 'hermes'
13
- | 'omp';
13
+ | 'omp'
14
+ | 'grok';
14
15
 
15
16
  /** Output mode for prompt invocations. */
16
17
  export type OutputMode = 'text' | 'json';
@@ -232,6 +233,30 @@ const ompShim: AgentShim = {
232
233
  getAuthCommand: () => ({ command: 'omp', args: ['--list-models'] }),
233
234
  };
234
235
 
236
+ /**
237
+ * Grok Build CLI (`grok`) — xAI coding agent. Headless one-shot via `-p`/`--single`;
238
+ * continue with `-c`; model via `-m`. Output formats are `plain`/`json`/`streaming-json`
239
+ * (map ai-runner `text` → `plain`). No auth-status verb — `getAuthCommand` is null;
240
+ * credential probing lives in auth-shims (env / `~/.grok/auth.json`).
241
+ */
242
+ const grokShim: AgentShim = {
243
+ name: 'grok',
244
+ command: 'grok',
245
+ tier: 1,
246
+ getHelpCommand: () => ({ command: 'grok', args: ['--help'] }),
247
+ getVersionCommand: () => ({ command: 'grok', args: ['--version'] }),
248
+ getPromptCommand: (options) => {
249
+ const args = ['-p', options.input ?? ''];
250
+ if (options.continue === true) args.push('-c');
251
+ if (options.model !== undefined) args.push('-m', options.model);
252
+ // Grok has no `text` format; map ai-runner OutputMode `text` → `plain`.
253
+ const format = (options.mode ?? 'text') === 'json' ? 'json' : 'plain';
254
+ args.push('--output-format', format);
255
+ return { command: 'grok', args };
256
+ },
257
+ getAuthCommand: () => null,
258
+ };
259
+
235
260
  /** All bundled agent shims keyed by canonical agent name. */
236
261
  export const AGENT_SHIMS: Readonly<Record<AgentName, AgentShim>> = {
237
262
  claude: claudeShim,
@@ -243,6 +268,7 @@ export const AGENT_SHIMS: Readonly<Record<AgentName, AgentShim>> = {
243
268
  openclaw: openclawShim,
244
269
  hermes: hermesShim,
245
270
  omp: ompShim,
271
+ grok: grokShim,
246
272
  };
247
273
 
248
274
  /** Tier-1 auto-selection priority. Deprecated ids are excluded. */
@@ -254,6 +280,7 @@ export const TIER1_PRIORITY: readonly AgentName[] = [
254
280
  'claude',
255
281
  'hermes',
256
282
  'opencode',
283
+ 'grok',
257
284
  ];
258
285
 
259
286
  /** Display order for doctor and list commands. */
@@ -267,6 +294,7 @@ export const DISPLAY_ORDER: readonly AgentName[] = [
267
294
  'antigravity-cli',
268
295
  'openclaw',
269
296
  'hermes',
297
+ 'grok',
270
298
  ];
271
299
 
272
300
  /** Set of gateway/TUI-constrained agents. */