feat: add built-in Grok CLI (xAI) agent plugin - #404
Conversation
Integrate xAI's official grok CLI as a built-in agent plugin for ralph-tui autonomous loops. Uses --always-approve, streaming-json output, and stdin prompt delivery via --prompt-file /dev/stdin. Verified with: bun run typecheck, bun run build, unit tests, and ralph-tui doctor --agent grok (HEALTHY).
|
@djbclark is attempting to deploy a commit to the plgeek Team on Vercel. A member of the Team first needs to authorize it. |
📝 WalkthroughWalkthroughAdds a built-in Grok CLI agent with streaming JSONL parsing, tool-event handling, platform-specific prompt delivery, setup validation, registration, skill installation mapping, tests, and documentation. ChangesGrok agent integration
Sequence Diagram(s)sequenceDiagram
participant RalphTUI
participant GrokAgentPlugin
participant GrokCLI
RalphTUI->>GrokAgentPlugin: initialize and validate Grok setup
GrokAgentPlugin->>GrokCLI: invoke grok with streaming JSON flags
GrokAgentPlugin->>GrokCLI: send prompt through stdin or -p
GrokCLI-->>GrokAgentPlugin: return JSONL events
GrokAgentPlugin-->>RalphTUI: emit parsed display events
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (5)
src/plugins/agents/builtin/grok.ts (3)
407-413: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueSimplify
validateModel.Every path returns
null. The empty-string branch has no effect. Reduce the body to a singlereturn nulland keep the comment that explains why Grok model IDs are not validated.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/plugins/agents/builtin/grok.ts` around lines 407 - 413, Simplify the Grok validateModel method by removing the redundant empty-string check and retaining a single return null statement. Preserve the existing comment explaining why Grok model IDs are not validated.
309-340: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueExtract the shared emit logic.
flushBufferand the wrappedonStdoutcontain the same parse-and-emit block. Extract one helper that accepts a string and emitsonJsonlMessage, segments, and formatted text. This removes the duplication and keeps both paths in sync.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/plugins/agents/builtin/grok.ts` around lines 309 - 340, Extract the duplicated parse-and-emit logic from flushBuffer and the wrapped onStdout into a shared helper that accepts the output string and invokes onJsonlMessage, onStdoutSegments, and onStdout as applicable. Update both paths to call this helper, preserving their existing buffering and trimming behavior while keeping event handling synchronized.
184-223: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueDeclare
timerbeforesafeResolve.
safeResolvereadstimer, which is declared at Line 220. The current code is safe because the process events fire asynchronously. If a future change callssafeResolvesynchronously, a temporal dead zone error occurs. Move the declaration above the handlers.Also consider using
process.platformandplatform()consistently across the file;buildArgsusesplatform()fromnode:oswhile this method usesprocess.platform.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/plugins/agents/builtin/grok.ts` around lines 184 - 223, Move the timer declaration before safeResolve and the process event handlers so safeResolve cannot access timer before initialization if invoked synchronously. In the same file, make the platform check in this method consistent with buildArgs by reusing the existing node:os platform() approach rather than process.platform.src/plugins/agents/builtin/grok.test.ts (2)
346-404: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd coverage for the streaming buffer in
execute.The tests cover the pure parsers only. The chunk-splitting buffer, the
flushBuffercall ononEnd, andonJsonlMessageforwarding inexecutecarry the real risk ingrok.ts. Add tests that feed a JSON object split across twoonStdoutchunks and assert that exactly one parsed event reaches the callback.Do you want me to generate these tests?
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/plugins/agents/builtin/grok.test.ts` around lines 346 - 404, Add tests for execute that split one JSON object across two onStdout chunks and verify the callback receives exactly one parsed event after buffering completes. Also cover that onEnd invokes flushBuffer and that onJsonlMessage forwards parsed events, using the existing parseGrokOutputToEvents test setup and execute-related symbols.
187-201: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueThe Windows branch is never exercised.
This test branches on
process.platform, so CI on Linux only covers the--prompt-filepath. Extract the platform decision into an injectable or overridable member, then assert both branches deterministically.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/plugins/agents/builtin/grok.test.ts` around lines 187 - 201, Update the platform-dependent logic used by testBuildArgs and testGetStdinInput in TestableGrokPlugin to rely on an injectable or overridable platform value instead of directly reading process.platform. Modify the test to instantiate or configure the plugin for both Windows and non-Windows platforms, asserting each branch deterministically.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@website/content/docs/plugins/agents/grok.mdx`:
- Around line 125-126: Update the Grok CLI command documentation to use the
supported -p/--prompt argument instead of --prompt-file /dev/stdin, including
the command construction and prompt-passing explanation near the later execution
example. Keep the prompt unescaped and ensure all documented command paths
consistently use the supported argument.
- Around line 137-143: Remove the trailing comments from the backslash-continued
Grok CLI lines in the command example, moving any needed explanations above the
command or omitting them. Ensure each continuation backslash is the final
character before the newline so the Bash command remains valid.
---
Nitpick comments:
In `@src/plugins/agents/builtin/grok.test.ts`:
- Around line 346-404: Add tests for execute that split one JSON object across
two onStdout chunks and verify the callback receives exactly one parsed event
after buffering completes. Also cover that onEnd invokes flushBuffer and that
onJsonlMessage forwards parsed events, using the existing
parseGrokOutputToEvents test setup and execute-related symbols.
- Around line 187-201: Update the platform-dependent logic used by testBuildArgs
and testGetStdinInput in TestableGrokPlugin to rely on an injectable or
overridable platform value instead of directly reading process.platform. Modify
the test to instantiate or configure the plugin for both Windows and non-Windows
platforms, asserting each branch deterministically.
In `@src/plugins/agents/builtin/grok.ts`:
- Around line 407-413: Simplify the Grok validateModel method by removing the
redundant empty-string check and retaining a single return null statement.
Preserve the existing comment explaining why Grok model IDs are not validated.
- Around line 309-340: Extract the duplicated parse-and-emit logic from
flushBuffer and the wrapped onStdout into a shared helper that accepts the
output string and invokes onJsonlMessage, onStdoutSegments, and onStdout as
applicable. Update both paths to call this helper, preserving their existing
buffering and trimming behavior while keeping event handling synchronized.
- Around line 184-223: Move the timer declaration before safeResolve and the
process event handlers so safeResolve cannot access timer before initialization
if invoked synchronously. In the same file, make the platform check in this
method consistent with buildArgs by reusing the existing node:os platform()
approach rather than process.platform.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 4d33945a-210d-4033-bb70-6699f4bbdd11
📒 Files selected for processing (6)
src/plugins/agents/builtin/grok.test.tssrc/plugins/agents/builtin/grok.tssrc/plugins/agents/builtin/index.tssrc/setup/skill-installer.tswebsite/content/docs/plugins/agents/grok.mdxwebsite/lib/navigation.ts
| 1. **Build command**: Constructs `grok --always-approve --output-format streaming-json --prompt-file /dev/stdin [options]` | ||
| 2. **Pass prompt via stdin**: Uses `--prompt-file /dev/stdin` (Grok does not read bare stdin) so the prompt is not shell-escaped |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
rg -n -C 3 -- 'prompt-file|--single|-p|stdin' \
src/plugins/agents/builtin/grok.ts \
src/plugins/agents/builtin/grok.test.ts \
website/content/docs/plugins/agents/grok.mdx
if command -v grok >/dev/null 2>&1; then
grok --help 2>&1 | rg -n -C 3 -- 'prompt-file|--single|-p' || true
fiRepository: subsy/ralph-tui
Length of output: 10606
🏁 Script executed:
#!/usr/bin/env bash
set -euo pipefail
# Inspect Grok metadata/version constraints and any supported-CLI checks.
rg -n -C 2 -- 'grok|Grok|packageManager|pnpm|node-version|engine|required' \
package.json pnpm-lock.yaml package-lock.json yarn.lock bun.lockb 2>/dev/null || true
printf '\n--- candidate files ---\n'
git ls-files | rg '(^|/)(package\.json|pnpm-lock\.yaml|package-lock\.json|yarn\.lock|bun\.lockb|.*[Gg]rok.*|.*agent.*)$' | sed -n '1,120p'Repository: subsy/ralph-tui
Length of output: 40123
🌐 Web query:
xAI grok CLI --prompt-file /dev/stdin -p --single prompt-file
💡 Result:
The command xAI grok CLI --prompt-file /dev/stdin --single prompt-file does not exist as a standard command in the official xAI Grok Build CLI [1][2]. The official xAI Grok Build CLI uses the grok command [2]. For headless or scriptable execution, the standard method to pass a prompt is using the -p or --prompt flag [2]. If you are attempting to pass input from stdin, common patterns for CLI tools involve piping text or using specific flags, but the official Grok CLI does not support a --prompt-file flag [1][2]. Based on the official documentation, you can run prompts headlessly using: echo "Your prompt here" | grok -p "$(cat -)" or simply: grok -p "Your prompt here" It is important to note that many third-party community-developed CLI tools for Grok exist (e.g., grok-cli repositories on GitHub) [3][4][5][6][7]. These third-party tools are distinct from the official xAI Grok Build CLI and may have different syntax or features [7]. If you are using one of these community tools, please consult the --help output of your specific installed binary (e.g., grok-cli --help) to verify supported flags, as they do not share a unified interface [8][3][4].
Citations:
- 1: https://docs.x.ai/build/cli/reference
- 2: https://docs.x.ai/build/overview
- 3: https://github.com/moore-developers/grok-cli
- 4: https://github.com/ele-yufo/grokcli/blob/refs/heads/main/README.md
- 5: https://github.com/grokstream/grok-cli
- 6: https://github.com/mmoz-root/grok-cli
- 7: https://github.com/andystore888/grok-cli
- 8: https://github.com/baba20o/grok-cli
Use the supported Grok CLI prompt argument.
--prompt-file /dev/stdin is not a Grok Build CLI option. On Unix, pass the prompt with -p/--prompt instead, or add a required Grok version that supports --prompt-file; otherwise the documented execution path will fail on the supported CLI. Also applies to website/content/docs/plugins/agents/grok.mdx lines 140-142.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@website/content/docs/plugins/agents/grok.mdx` around lines 125 - 126, Update
the Grok CLI command documentation to use the supported -p/--prompt argument
instead of --prompt-file /dev/stdin, including the command construction and
prompt-passing explanation near the later execution example. Keep the prompt
unescaped and ensure all documented command paths consistently use the supported
argument.
| grok \ | ||
| --always-approve \ # Auto-approve tool executions | ||
| --output-format streaming-json \ # Structured NDJSON for parsing | ||
| --prompt-file /dev/stdin \ # Read prompt from stdin (Unix) | ||
| --model grok-4.5 \ # If model specified | ||
| < prompt.txt | ||
| ``` |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -e
printf 'File excerpt:\n'
sed -n '125,150p' website/content/docs/plugins/agents/grok.mdx | cat -n
printf '\nBackslash-newline behavior probe:\n'
bash -c '
line1="grok \ \ \ \ --always-approve"
line2=" --output-format streaming-json"
printf "%s\n%s\n" "$line1" "$line2"
bash -n <<BASH
$line1
$line2
BASH
status=$?
if [ "$status" -eq 0 ]; then
echo "parse succeeded"
else
echo "parse failed"
fi
' 2>&1 || true
printf '\nRelevant line backslash context:\n'
python3 - <<'PY'
from pathlib import Path
path = Path("website/content/docs/plugins/agents/grok.mdx")
lines = path.read_text().splitlines()
for i in [138,139,140,141,142]:
if 1 <= i <= len(lines):
s = repr(lines[i-1])
print(f"{i}: {s}")
PYRepository: subsy/ralph-tui
Length of output: 1951
🏁 Script executed:
#!/bin/bash
set -e
printf 'Markdown code fence line before artifact:\n'
sed -n '115,125p' website/content/docs/plugins/agents/grok.mdx | cat -n
printf '\nLine lengths and trailing whitespace around snippet:\n'
python3 - <<'PY'
from pathlib import Path
lines = Path("website/content/docs/plugins/agents/grok.mdx").read_text().splitlines()
for i in range(120, 155):
s = lines[i-1]
print(f"{i:4}: len={len(s):3} rws={len(s)-len(s.rstrip()):2} trail_mark={((s.rstrip() if s.strip() else '')}.rstrip())[-10:] if s.strip() else '<empty>'")
PYRepository: subsy/ralph-tui
Length of output: 1063
🏁 Script executed:
#!/bin/bash
set -e
exec 3>&1
printf 'Markdown code fence line before artifact:\n'
sed -n '115,135p' website/content/docs/plugins/agents/grok.mdx | cat -n >&3
printf '\nLine lengths and trailing behavior around snippet:\n'
python3 - <<'PY'
from pathlib import Path
lines = Path("website/content/docs/plugins/agents/grok.mdx").read_text().splitlines()
for i in range(120, 155):
s = lines[i-1]
rws = len(s) - len(s.rstrip())
trailing = s.rstrip()[-12:] if s.strip() else '<empty>'
print(f"{i:4}: len={len(s):3} trailing_ws={rws:2} trailing_mark={trailing}")
PY
printf '\nMinimal bash continuation examples:\n'
bash <<'BASH'
set -v
echo 'case with comment after backslash:'
cmd="/bin/echo"
cmd="/bin/echo" \ # comment
"hello" \
"world"
"$cmd" "$cmd"
echo "status=$?"
BASH
bash <<'BASH'
set -v
echo 'case without comment after backslash:'
cmd="/bin/echo"
cmd="/bin/echo" \
"hello" \
"world"
"$cmd" "$cmd"
echo "status=$?"
BASH
bash <<'BASH'
set -v
echo 'case with comment without spaces before newline:'
cmd="/bin/echo"
cmd="/bin/echo" \
# comment
"hello" \
"world"
"$cmd" "$cmd"
echo "status=$?"
BASHRepository: subsy/ralph-tui
Length of output: 3808
Remove trailing comments from continued lines.
Bash only treats a backslash as a newline when it appears before the newline. Lines 138-141 keep the continuation backslash, so the shell continues to the next text line as a new command. Move the # comments above these lines or remove them from the continued lines.
Proposed fix
grok \
- --always-approve \ # Auto-approve tool executions
- --output-format streaming-json \ # Structured NDJSON for parsing
- --prompt-file /dev/stdin \ # Read prompt from stdin (Unix)
- --model grok-4.5 \ # If model specified
+ --always-approve \
+ --output-format streaming-json \
+ --prompt-file /dev/stdin \
+ --model grok-4.5 \
< prompt.txt📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| grok \ | |
| --always-approve \ # Auto-approve tool executions | |
| --output-format streaming-json \ # Structured NDJSON for parsing | |
| --prompt-file /dev/stdin \ # Read prompt from stdin (Unix) | |
| --model grok-4.5 \ # If model specified | |
| < prompt.txt | |
| ``` | |
| grok \ | |
| --always-approve \ | |
| --output-format streaming-json \ | |
| --prompt-file /dev/stdin \ | |
| --model grok-4.5 \ | |
| < prompt.txt |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@website/content/docs/plugins/agents/grok.mdx` around lines 137 - 143, Remove
the trailing comments from the backslash-continued Grok CLI lines in the command
example, moving any needed explanations above the command or omitting them.
Ensure each continuation backslash is the final character before the newline so
the Bash command remains valid.
Summary
Closes #402. Adds a built-in agent plugin for xAI's official
grokCLI, following the project's own "Adding a New Agent Plugin" checklist in CONTRIBUTING.md.src/plugins/agents/builtin/grok.ts— modeled onpi.ts/kimi.tsbuiltin/index.ts,AGENT_ID_MAPinsetup/skill-installer.tswebsite/content/docs/plugins/agents/grok.mdx) + nav entryAuthenticates via the
grokCLI's own OAuth session (SuperGrok subscription) — no separate API key required.Test plan
bun run typecheck— cleanbun run build— cleanbun test src/plugins/agents/builtin/grok.test.ts— 46 passbun run dist/cli.js doctor --agent grok— HEALTHY (real preflight round-trip, not mocked)Summary by CodeRabbit