Skip to content

Grok CLI Support - WIP#1203

Merged
eyaltoledano merged 3 commits intoeyaltoledano:tm-startfrom
maxtuzz:max/grok-cli-support
Sep 12, 2025
Merged

Grok CLI Support - WIP#1203
eyaltoledano merged 3 commits intoeyaltoledano:tm-startfrom
maxtuzz:max/grok-cli-support

Conversation

@maxtuzz
Copy link
Collaborator

@maxtuzz maxtuzz commented Sep 12, 2025

What type of PR is this?

  • 🐛 Bug fix
  • ✨ Feature
  • 🔌 Integration
  • 📝 Docs
  • 🧹 Refactor
  • Other:

Description

Adds grok cli support following existing patterns WIP

Related Issues

How to Test This

  • Install grok-cli
  • Add GROK_API_KEY
  • Select model
tm models --setup

Select your grok models

Go ham

tm add-task --prompt="Create a simple hello world cli application"

Expected result:

Contributor Checklist

  • Created changeset: npm run changeset
  • Tests pass: npm test
  • Format check passes: npm run format-check (or npm run format to fix)
  • Addressed CodeRabbit comments (if any)
  • Linked related issues (if any)
  • Manually tested the changes

Changelog Entry


For Maintainers

  • PR title follows conventional commits
  • Target branch correct
  • Labels added
  • Milestone assigned (if applicable)

Summary by CodeRabbit

  • New Features

    • Added Grok CLI as a new AI provider with language model support and simulated streaming.
    • Added four Grok models (Grok 4 Latest, Grok 3 Latest, Grok 3 Fast, Grok 3 Mini Fast).
    • Works without an API key by default; environment variable support optional.
  • Configuration

    • New grokCli settings with per-command overrides (model, timeout, workingDirectory).
    • Grok CLI included in provider lists and codebase analysis options.
    • Improved error handling and JSON extraction for Grok CLI responses.

@changeset-bot
Copy link

changeset-bot bot commented Sep 12, 2025

⚠️ No Changeset found

Latest commit: 6c7e15c

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 12, 2025

Walkthrough

Adds Grok CLI as a new provider. Introduces provider constants, model listings, configuration support with getters and per-command overrides, a full custom SDK (language model, errors, message conversion, JSON extraction), a top-level GrokCliProvider, and wires it into provider registries.

Changes

Cohort / File(s) Summary
Provider wiring and constants
`scripts/modules/ai-services-unified.js`, `src/constants/providers.js`, `src/ai-providers/index.js`
Registers provider key 'grok-cli', exports GrokCliProvider, and adds it to provider maps.
Configuration management
`scripts/modules/config-manager.js`
Adds grokCli default/merge config, getters (`getGrokCliSettings`, `getGrokCliSettingsForCommand`), includes GROK_CLI in codebase analysis and providersWithoutApiKeys.
Supported models
`scripts/modules/supported-models.json`
Adds `grok-cli` models: grok-4-latest, grok-3-latest, grok-3-fast, grok-3-mini-fast with metadata.
Grok CLI SDK
`src/ai-providers/custom-sdk/grok-cli/*` (`errors.js`, `index.js`, `json-extractor.js`, `language-model.js`, `message-converter.js`, `types.js`)
Implements provider factory, language model with CLI execution, error helpers, message conversion, JSON extraction, and typedefs.
Top-level provider
`src/ai-providers/grok-cli.js`
Adds GrokCliProvider class that builds clients via `createGrokCli` and reads settings via config-manager.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant CLI as Command (e.g., "analyze")
  participant Provider as GrokCliProvider
  participant Config as ConfigManager
  participant Factory as createGrokCli
  participant Model as GrokCliLanguageModel
  participant Grok as grok (CLI process)

  User->>CLI: invokes command
  CLI->>Provider: getClient({ apiKey?, baseURL?, workingDirectory?, timeout?, commandName })
  Provider->>Config: getGrokCliSettingsForCommand(commandName)
  Config-->>Provider: merged grokCli settings
  Provider->>Factory: createGrokCli({ defaultSettings })
  Factory-->>Provider: provider instance
  Provider-->>CLI: client (model factory)

  CLI->>Model: doGenerate({ messages, modelId, options })
  Model->>Model: checkGrokCliInstallation()
  Model->>Model: getApiKey() (env/config/params)
  Model->>Grok: spawn grok --model ... --prompt ...
  Grok-->>Model: stdout/stderr, exitCode
  alt exitCode == 0
    Model-->>CLI: { text, usage, warnings?, rawCall }
  else exitCode != 0
    Model-->>CLI: APICallError / Authentication / Installation / Timeout
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested reviewers

  • eyaltoledano
  • Crunchyman-ralph

Pre-merge checks (2 passed, 1 warning)

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 77.27% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "Grok CLI Support - WIP" correctly identifies the primary change (adding Grok CLI support) and is short and scannable, but the "- WIP" suffix is noisy and implies the PR is not yet ready for final review.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@maxtuzz maxtuzz marked this pull request as ready for review September 12, 2025 03:40
@maxtuzz maxtuzz changed the base branch from next to tm-start September 12, 2025 03:45
@eyaltoledano eyaltoledano merged commit 00a34e6 into eyaltoledano:tm-start Sep 12, 2025
1 check was pending
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 12

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (4)
scripts/modules/ai-services-unified.js (3)

458-470: commandName not passed to providers; breaks Grok CLI per-command settings.

You destructure commandName but don’t include it in callParams, so providers can’t read it in getClient(). This prevents getGrokCliSettingsForCommand(commandName) from working.

-  const {
+  const {
     role: initialRole,
     session,
     projectRoot,
     systemPrompt,
     prompt,
     schema,
     objectName,
     commandName,
     outputType,
     ...restApiParams
   } = params;
...
-  const callParams = {
+  const callParams = {
     apiKey,
     modelId,
     maxTokens: roleParams.maxTokens,
     temperature: roleParams.temperature,
     messages,
+    commandName,
     ...(baseURL && { baseURL }),
     ...((serviceType === 'generateObject' ||
       serviceType === 'streamObject') && { schema, objectName }),
     ...providerSpecificParams,
     ...restApiParams
   };

Also applies to: 640-651


610-614: System message can contain literal “undefined”.

If systemPrompt is falsy, you end up sending “undefined” to the model. Build the system content conditionally.

-  const systemPromptWithLanguage = `${systemPrompt} \n\n Always respond in ${responseLanguage}.`;
-  messages.push({
-    role: 'system',
-    content: systemPromptWithLanguage.trim()
-  });
+  const sysParts = [];
+  if (systemPrompt) sysParts.push(systemPrompt);
+  sysParts.push(`Always respond in ${responseLanguage}.`);
+  messages.push({ role: 'system', content: sysParts.join('\n\n') });

557-570: Add CUSTOM_PROVIDERS.GROK_CLI to providersWithoutApiKeys

unifiedServiceRunner skips providers listed there for API-key checks; grok-cli relies on an optional key/config — add CUSTOM_PROVIDERS.GROK_CLI to the providersWithoutApiKeys arrays in scripts/modules/config-manager.js (around lines 714 and 1021).

scripts/modules/config-manager.js (1)

772-786: Add keyless short-circuit for Grok/Gemini CLI in MCP key checker.

getMcpApiKeyStatus should immediately return true for providers that don’t use keys, per guidelines.

Apply:

 function getMcpApiKeyStatus(providerName, projectRoot = null) {
+  // Providers that don't require API keys
+  if (['ollama', 'claude-code', 'grok-cli', 'gemini-cli'].includes((providerName || '').toLowerCase())) {
+    return true;
+  }
   const rootDir = projectRoot || findProjectRoot();
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dd03374 and 6c7e15c.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (12)
  • scripts/modules/ai-services-unified.js (2 hunks)
  • scripts/modules/config-manager.js (7 hunks)
  • scripts/modules/supported-models.json (1 hunks)
  • src/ai-providers/custom-sdk/grok-cli/errors.js (1 hunks)
  • src/ai-providers/custom-sdk/grok-cli/index.js (1 hunks)
  • src/ai-providers/custom-sdk/grok-cli/json-extractor.js (1 hunks)
  • src/ai-providers/custom-sdk/grok-cli/language-model.js (1 hunks)
  • src/ai-providers/custom-sdk/grok-cli/message-converter.js (1 hunks)
  • src/ai-providers/custom-sdk/grok-cli/types.js (1 hunks)
  • src/ai-providers/grok-cli.js (1 hunks)
  • src/ai-providers/index.js (1 hunks)
  • src/constants/providers.js (1 hunks)
🧰 Additional context used
📓 Path-based instructions (9)
src/ai-providers/*.js

📄 CodeRabbit inference engine (.cursor/rules/ai_providers.mdc)

src/ai-providers/*.js: Create a new provider module file in src/ai-providers/ named .js when adding a new AI provider.
Provider modules must export three functions: generateText, streamText, and generateObject.
Provider modules must import the provider's create function from @ai-sdk/, and import generateText, streamText, generateObject from the core ai package, as well as the log utility from ../../scripts/modules/utils.js.
Implement generateText, streamText, and generateObject functions in provider modules with basic validation and try/catch error handling.

Provider-specific wrappers for Vercel AI SDK functions must be implemented in src/ai-providers/*.js, each file corresponding to a provider.

Files:

  • src/ai-providers/index.js
  • src/ai-providers/grok-cli.js
**/*.js

📄 CodeRabbit inference engine (.cursor/rules/tests.mdc)

**/*.js: Declare and initialize global variables at the top of modules to avoid hoisting issues.
Use proper function declarations to avoid hoisting issues and initialize variables before they are referenced.
Do not reference variables before their declaration in module scope.
Use dynamic imports (import()) to avoid initialization order issues in modules.

Files:

  • src/ai-providers/index.js
  • scripts/modules/config-manager.js
  • src/ai-providers/custom-sdk/grok-cli/errors.js
  • src/ai-providers/custom-sdk/grok-cli/json-extractor.js
  • scripts/modules/ai-services-unified.js
  • src/ai-providers/grok-cli.js
  • src/ai-providers/custom-sdk/grok-cli/types.js
  • src/constants/providers.js
  • src/ai-providers/custom-sdk/grok-cli/index.js
  • src/ai-providers/custom-sdk/grok-cli/message-converter.js
  • src/ai-providers/custom-sdk/grok-cli/language-model.js
scripts/modules/config-manager.js

📄 CodeRabbit inference engine (.cursor/rules/ai_providers.mdc)

scripts/modules/config-manager.js: Update scripts/modules/config-manager.js to add the new provider to MODEL_MAP, ensure it is included in VALID_PROVIDERS, and update API key handling logic.
If adding Ollama or another provider not requiring an API key, add a specific check at the beginning of isApiKeySet and getMcpApiKeyStatus in scripts/modules/config-manager.js to return true immediately for that provider.

scripts/modules/config-manager.js: Import and use specific getters from scripts/modules/config-manager.js to access configuration values needed for application logic; pass the explicitRoot parameter to getters if calling from MCP direct functions.
Use isApiKeySet(providerName, session) from config-manager.js to check if a provider's key is available before attempting an AI call.
Handle potential ConfigurationError if the .taskmasterconfig file is missing or invalid when accessed via getConfig.

Files:

  • scripts/modules/config-manager.js
scripts/modules/*.js

📄 CodeRabbit inference engine (.cursor/rules/architecture.mdc)

Each module in scripts/modules/ should be focused on a single responsibility, following the modular architecture (e.g., commands.js for CLI command handling, task-manager.js for task data and core logic, dependency-manager.js for dependency management, ui.js for CLI output formatting, ai-services-unified.js for AI service integration, config-manager.js for configuration management, utils.js for utility functions).

scripts/modules/*.js: Export all core functions, helper functions, and utility methods needed by your new function or command from their respective modules. Explicitly review the module's export block to ensure every required dependency is included.
Pass all required parameters to functions you call within your implementation and verify that direct function parameters match their core function counterparts.
Use consistent file naming conventions: 'task_${id.toString().padStart(3, '0')}.txt', use path.join for composing file paths, and use appropriate file extensions (.txt for tasks, .json for data).
Use structured error objects with code and message properties, include clear error messages, and handle both function-specific and file system errors.
Import all silent mode utilities together from 'scripts/modules/utils.js' and always use isSilentMode() to check global silent mode status. Wrap core function calls within direct functions using enableSilentMode() and disableSilentMode() in a try/finally block if the core function might produce console output.
Core functions should check outputFormat === 'text' before displaying UI elements and use internal logging that respects silent mode.
Design functions to accept dependencies as parameters (dependency injection) and avoid hard-coded dependencies that are difficult to mock.
Keep pure logic separate from I/O operations or UI rendering to allow testing the logic without mocking complex dependencies.
When implementing core logic for new features, do so in 'scripts/modules/' before CLI or MCP interfaces, and d...

Files:

  • scripts/modules/config-manager.js
  • scripts/modules/ai-services-unified.js
scripts/modules/**

📄 CodeRabbit inference engine (.cursor/rules/dev_workflow.mdc)

When using the MCP server, restart it if core logic in scripts/modules or MCP tool/direct function definitions change.

Files:

  • scripts/modules/config-manager.js
  • scripts/modules/supported-models.json
  • scripts/modules/ai-services-unified.js
scripts/modules/*

📄 CodeRabbit inference engine (.cursor/rules/tags.mdc)

scripts/modules/*: Every command that reads or writes tasks.json must be tag-aware
All command files must import getCurrentTag from utils.js
Every CLI command that operates on tasks must include the --tag CLI option
All commands must resolve the tag using the pattern: options.tag || getCurrentTag(projectRoot) || 'master'
All commands must find projectRoot with error handling before proceeding
All commands must pass { projectRoot, tag } as context to core functions
MCP direct functions must accept and use a context object containing projectRoot and tag, and pass them to core functions
Do not hard-code tag resolution (e.g., const tag = options.tag || 'master';); always use getCurrentTag
Do not omit the --tag CLI option in commands that operate on tasks
Do not omit the context parameter when calling core functions from commands
Do not call readJSON or writeJSON without passing projectRoot and tag

Files:

  • scripts/modules/config-manager.js
  • scripts/modules/supported-models.json
  • scripts/modules/ai-services-unified.js
scripts/modules/supported-models.json

📄 CodeRabbit inference engine (.cursor/rules/ai_providers.mdc)

Add a new key for the provider and an array of model objects under it in scripts/modules/supported-models.json, including id, name, allowed_roles, and optionally swe_score, cost_per_1m_tokens, and max_tokens.

Files:

  • scripts/modules/supported-models.json
scripts/modules/ai-services-unified.js

📄 CodeRabbit inference engine (.cursor/rules/ai_providers.mdc)

Integrate the new provider module with scripts/modules/ai-services-unified.js by importing it and adding an entry to the PROVIDER_FUNCTIONS map.

scripts/modules/ai-services-unified.js: Centralize all LLM calls through generateTextService or generateObjectService.
Do not import or call anything from the old ai-services.js, ai-client-factory.js, or ai-client-utils.js files.
Do not fetch AI-specific parameters (model ID, max tokens, temp) using config-manager.js getters for the AI call. Pass the role instead.
Do not implement fallback or retry logic outside ai-services-unified.js.
Do not handle API key resolution outside the service layer (it uses utils.js internally).

The telemetryData object returned by ai-services-unified.js must include the fields: timestamp, userId, commandName, modelUsed, providerName, inputTokens, outputTokens, totalTokens, totalCost, and currency.

Files:

  • scripts/modules/ai-services-unified.js
scripts/modules/ai-services*.js

📄 CodeRabbit inference engine (.cursor/rules/new_features.mdc)

Ensure AI calls correctly handle and propagate telemetryData as described in 'telemetry.mdc'.

Files:

  • scripts/modules/ai-services-unified.js
🧠 Learnings (15)
📓 Common learnings
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-11T12:30:23.843Z
Learning: Import Task Master's development workflow commands and guidelines; treat the contents of ./.taskmaster/CLAUDE.md as if included in the main CLAUDE.md
📚 Learning: 2025-07-18T17:06:04.909Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/ai_providers.mdc:0-0
Timestamp: 2025-07-18T17:06:04.909Z
Learning: Applies to src/ai-providers/*.js : Provider modules must import the provider's create<ProviderName> function from ai-sdk/<provider-name>, and import generateText, streamText, generateObject from the core ai package, as well as the log utility from ../../scripts/modules/utils.js.

Applied to files:

  • src/ai-providers/index.js
  • scripts/modules/ai-services-unified.js
  • src/ai-providers/grok-cli.js
  • src/ai-providers/custom-sdk/grok-cli/index.js
📚 Learning: 2025-07-18T17:06:04.909Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/ai_providers.mdc:0-0
Timestamp: 2025-07-18T17:06:04.909Z
Learning: Applies to src/ai-providers/*.js : Create a new provider module file in src/ai-providers/ named <provider-name>.js when adding a new AI provider.

Applied to files:

  • src/ai-providers/index.js
  • scripts/modules/ai-services-unified.js
  • src/ai-providers/grok-cli.js
  • src/ai-providers/custom-sdk/grok-cli/index.js
📚 Learning: 2025-07-18T17:06:04.909Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/ai_providers.mdc:0-0
Timestamp: 2025-07-18T17:06:04.909Z
Learning: Applies to src/ai-providers/*.js : Provider modules must export three functions: generate<ProviderName>Text, stream<ProviderName>Text, and generate<ProviderName>Object.

Applied to files:

  • src/ai-providers/index.js
  • scripts/modules/ai-services-unified.js
📚 Learning: 2025-07-18T17:07:39.336Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/architecture.mdc:0-0
Timestamp: 2025-07-18T17:07:39.336Z
Learning: Applies to src/ai-providers/*.js : Provider-specific wrappers for Vercel AI SDK functions must be implemented in src/ai-providers/*.js, each file corresponding to a provider.

Applied to files:

  • src/ai-providers/index.js
  • scripts/modules/ai-services-unified.js
  • src/ai-providers/grok-cli.js
  • src/ai-providers/custom-sdk/grok-cli/index.js
📚 Learning: 2025-07-18T17:06:04.909Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/ai_providers.mdc:0-0
Timestamp: 2025-07-18T17:06:04.909Z
Learning: Applies to scripts/modules/ai-services-unified.js : Integrate the new provider module with scripts/modules/ai-services-unified.js by importing it and adding an entry to the PROVIDER_FUNCTIONS map.

Applied to files:

  • src/ai-providers/index.js
  • scripts/modules/ai-services-unified.js
📚 Learning: 2025-07-18T17:06:04.909Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/ai_providers.mdc:0-0
Timestamp: 2025-07-18T17:06:04.909Z
Learning: Applies to src/ai-providers/*.js : Implement generate<ProviderName>Text, stream<ProviderName>Text, and generate<ProviderName>Object functions in provider modules with basic validation and try/catch error handling.

Applied to files:

  • src/ai-providers/index.js
  • scripts/modules/ai-services-unified.js
  • src/ai-providers/grok-cli.js
📚 Learning: 2025-07-18T17:06:04.909Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/ai_providers.mdc:0-0
Timestamp: 2025-07-18T17:06:04.909Z
Learning: Applies to scripts/modules/config-manager.js : Update scripts/modules/config-manager.js to add the new provider to MODEL_MAP, ensure it is included in VALID_PROVIDERS, and update API key handling logic.

Applied to files:

  • scripts/modules/config-manager.js
  • src/constants/providers.js
📚 Learning: 2025-07-18T17:18:17.759Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/utilities.mdc:0-0
Timestamp: 2025-07-18T17:18:17.759Z
Learning: Applies to scripts/modules/config-manager.js : Import and use specific getters from `scripts/modules/config-manager.js` to access configuration values needed for application logic; pass the `explicitRoot` parameter to getters if calling from MCP direct functions.

Applied to files:

  • scripts/modules/config-manager.js
📚 Learning: 2025-07-18T17:06:04.909Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/ai_providers.mdc:0-0
Timestamp: 2025-07-18T17:06:04.909Z
Learning: Applies to scripts/modules/supported-models.json : Add a new key for the provider and an array of model objects under it in scripts/modules/supported-models.json, including id, name, allowed_roles, and optionally swe_score, cost_per_1m_tokens, and max_tokens.

Applied to files:

  • scripts/modules/supported-models.json
📚 Learning: 2025-07-21T14:14:48.694Z
Learnt from: rtmcrc
PR: eyaltoledano/claude-task-master#933
File: scripts/modules/supported-models.json:238-238
Timestamp: 2025-07-21T14:14:48.694Z
Learning: Model version updates in scripts/modules/supported-models.json may be included in feature PRs if they provide practical improvements like reduced error rates, even if not directly related to the main feature being implemented.

Applied to files:

  • scripts/modules/supported-models.json
📚 Learning: 2025-08-08T11:34:45.482Z
Learnt from: Crunchyman-ralph
PR: eyaltoledano/claude-task-master#1105
File: .changeset/vast-weeks-fetch.md:5-5
Timestamp: 2025-08-08T11:34:45.482Z
Learning: In this repo, the supported models list is auto-generated by CI into docs/models.md from scripts/modules/supported-models.json via .github/workflows/update-models-md.yml and docs/scripts/models-json-to-markdown.js. Don’t request manual edits to the Markdown; ensure the JSON is correct instead.

Applied to files:

  • scripts/modules/supported-models.json
📚 Learning: 2025-08-08T11:34:45.482Z
Learnt from: Crunchyman-ralph
PR: eyaltoledano/claude-task-master#1105
File: .changeset/vast-weeks-fetch.md:5-5
Timestamp: 2025-08-08T11:34:45.482Z
Learning: In this repo, supported-models.md is auto-generated by CI from supported-models.json; do not request manual edits to that file—ensure JSON entries are correct instead.

Applied to files:

  • scripts/modules/supported-models.json
📚 Learning: 2025-08-08T11:34:45.482Z
Learnt from: Crunchyman-ralph
PR: eyaltoledano/claude-task-master#1105
File: .changeset/vast-weeks-fetch.md:5-5
Timestamp: 2025-08-08T11:34:45.482Z
Learning: This repo’s supported models list is CI-generated into docs/models.md from scripts/modules/supported-models.json (workflow: .github/workflows/update-models-md.yml using docs/scripts/models-json-to-markdown.js). Don’t request manual edits to the Markdown; ensure the JSON is correct instead. README links to docs/models.md.

Applied to files:

  • scripts/modules/supported-models.json
📚 Learning: 2025-07-18T17:06:04.909Z
Learnt from: CR
PR: eyaltoledano/claude-task-master#0
File: .cursor/rules/ai_providers.mdc:0-0
Timestamp: 2025-07-18T17:06:04.909Z
Learning: Applies to tests/unit/ai-providers/*.test.js : Create unit tests for the new provider in tests/unit/ai-providers/<provider-name>.test.js, mocking ai-sdk/<provider-name> and core ai module functions, and testing all exported functions for correct behavior and error handling.

Applied to files:

  • src/ai-providers/grok-cli.js
🧬 Code graph analysis (5)
scripts/modules/config-manager.js (1)
src/constants/providers.js (2)
  • CUSTOM_PROVIDERS (18-28)
  • CUSTOM_PROVIDERS (18-28)
scripts/modules/ai-services-unified.js (1)
src/ai-providers/grok-cli.js (1)
  • GrokCliProvider (10-77)
src/ai-providers/grok-cli.js (3)
src/ai-providers/base-provider.js (1)
  • BaseAIProvider (16-352)
scripts/modules/commands.js (1)
  • commandName (805-805)
src/ai-providers/custom-sdk/grok-cli/index.js (1)
  • createGrokCli (20-65)
src/ai-providers/custom-sdk/grok-cli/index.js (1)
src/ai-providers/custom-sdk/grok-cli/language-model.js (1)
  • GrokCliLanguageModel (26-390)
src/ai-providers/custom-sdk/grok-cli/language-model.js (3)
src/ai-providers/custom-sdk/grok-cli/errors.js (3)
  • createTimeoutError (72-87)
  • createAPICallError (23-48)
  • createAuthenticationError (56-62)
src/ai-providers/custom-sdk/grok-cli/message-converter.js (4)
  • prompt (100-113)
  • createPromptFromMessages (96-116)
  • escapeShellArg (123-130)
  • convertFromGrokCliResponse (44-89)
src/ai-providers/custom-sdk/grok-cli/json-extractor.js (1)
  • extractJson (10-59)
🔇 Additional comments (19)
scripts/modules/supported-models.json (1)

109-158: Grok CLI models block looks consistent with repo schema.

IDs, roles, and max_tokens set; costs default to 0 and will resolve currency to USD in telemetry. No blocking issues.

src/ai-providers/index.js (1)

19-19: Public export added correctly.

Re-exporting GrokCliProvider aligns with existing pattern.

scripts/modules/ai-services-unified.js (2)

46-54: Import added is correct and follows existing provider import pattern.


73-75: Provider registered correctly in PROVIDERS map.

src/constants/providers.js (1)

26-28: GROK_CLI added to CUSTOM_PROVIDERS is correct.

It will flow into CUSTOM_PROVIDERS_ARRAY and ALL_PROVIDERS as intended.

src/ai-providers/grok-cli.js (1)

57-76: Client creation logic LGTM; relies on per-command settings.

Works once commandName is forwarded by ai-services-unified. No further issues.

src/ai-providers/custom-sdk/grok-cli/index.js (1)

20-65: Factory/provider pattern is sound; guards and NoSuchModelError usage are appropriate.

scripts/modules/config-manager.js (6)

382-397: Expose Grok CLI getters — LGTM.


486-490: hasCodebaseAnalysis correctly includes GROK_CLI.


710-721: isApiKeySet: GROK_CLI correctly treated as keyless.


1021-1027: providersWithoutApiKeys export includes GROK_CLI — LGTM.


1038-1041: Public API exports for Grok CLI getters — LGTM.


25-47: Resolved — grok default model exists and is supported.
DEFAULTS.grokCli.defaultModel ("grok-4-latest") is present in scripts/modules/supported-models.json (search match: 1:grok-4-latest).

src/ai-providers/custom-sdk/grok-cli/message-converter.js (3)

14-37: Message shaping — LGTM.

Handles string and multi-part content sanely; trims output.


44-89: JSONL parsing with safe fallback — LGTM.

Last assistant message selection and usage mapping look correct.


118-130: Avoid shell-escaping when using spawn with args.

escapeShellArg is fine as a utility, but passing its output to spawn (without shell: true) will inject literal quotes. Ensure callers pass raw strings to spawn.

src/ai-providers/custom-sdk/grok-cli/errors.js (1)

95-103: Confirm npm package/install command for Grok CLI.
src/ai-providers/custom-sdk/grok-cli/errors.js (lines 95–103) currently recommends npm install -g @vibe-kit/grok-cli. Multiple grok-cli packages exist on npm; verify @vibe-kit/grok-cli is the intended package (or update the message to the correct package name or add a repo URL).

src/ai-providers/custom-sdk/grok-cli/language-model.js (2)

280-303: rawCall.rawSettings currently exposes args; ensure no secrets.

With env-based key handling above, args are safe. Keep it that way and avoid ever serializing env.


327-389: ReadableStream usage — LGTM post import; consider stream chunking as optional improvement.

Works given the simulated strategy; import fix above suffices.

Comment on lines +60 to +65
claudeCode: {},
grokCli: {
timeout: 120000,
workingDirectory: null,
defaultModel: 'grok-4-latest'
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Defaults added for Grok CLI look good; consider schema validation.

Please mirror Claude Code’s zod-based validation for grokCli (timeout, workingDirectory, defaultModel, commandSpecific) to prevent bad config from leaking into runtime.

Example skeleton:

+function validateGrokCliSettings(settings) {
+  const Base = z.object({
+    timeout: z.number().int().positive().max(10 * 60 * 1000).optional(),
+    workingDirectory: z.string().nullable().optional(),
+    defaultModel: z.string().optional(),
+  });
+  const CommandSpecific = z.record(z.enum(AI_COMMAND_NAMES), Base).optional();
+  const Schema = Base.extend({ commandSpecific: CommandSpecific });
+  try { return Schema.parse(settings); } catch { return {}; }
+}

And apply during merge:

- grokCli: { ...defaults.grokCli, ...parsedConfig?.grokCli }
+ grokCli: validateGrokCliSettings({ ...defaults.grokCli, ...parsedConfig?.grokCli })
📝 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.

Suggested change
claudeCode: {},
grokCli: {
timeout: 120000,
workingDirectory: null,
defaultModel: 'grok-4-latest'
}
function validateGrokCliSettings(settings) {
const Base = z.object({
timeout: z.number().int().positive().max(10 * 60 * 1000).optional(),
workingDirectory: z.string().nullable().optional(),
defaultModel: z.string().optional(),
});
const CommandSpecific = z.record(z.enum(AI_COMMAND_NAMES), Base).optional();
const Schema = Base.extend({ commandSpecific: CommandSpecific });
try { return Schema.parse(settings); } catch { return {}; }
}
claudeCode: {},
grokCli: validateGrokCliSettings({ ...defaults.grokCli, ...parsedConfig?.grokCli })

Comment on lines +110 to +118
export function isAuthenticationError(error) {
if (error instanceof LoadAPIKeyError) return true;
if (
error instanceof APICallError &&
/** @type {GrokCliErrorMetadata} */ (error.data)?.exitCode === 401
)
return true;
return false;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Auth detection relies on exitCode 401 which most CLIs won’t emit.

Consider also checking stderr for “unauthorized”/“401” and treating exitCode !== 0 with those markers as auth failures.

 export function isAuthenticationError(error) {
   if (error instanceof LoadAPIKeyError) return true;
-  if (
-    error instanceof APICallError &&
-    /** @type {GrokCliErrorMetadata} */ (error.data)?.exitCode === 401
-  )
+  if (error instanceof APICallError) {
+    const data = /** @type {GrokCliErrorMetadata} */ (error.data);
+    if (data?.exitCode === 401) return true;
+    const msg = (data?.stderr || '').toLowerCase();
+    if (msg.includes('unauthorized') || msg.includes('401')) return true;
+  }
   return false;
 }
📝 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.

Suggested change
export function isAuthenticationError(error) {
if (error instanceof LoadAPIKeyError) return true;
if (
error instanceof APICallError &&
/** @type {GrokCliErrorMetadata} */ (error.data)?.exitCode === 401
)
return true;
return false;
}
export function isAuthenticationError(error) {
if (error instanceof LoadAPIKeyError) return true;
if (error instanceof APICallError) {
const data = /** @type {GrokCliErrorMetadata} */ (error.data);
if (data?.exitCode === 401) return true;
const msg = (data?.stderr || '').toLowerCase();
if (msg.includes('unauthorized') || msg.includes('401')) return true;
}
return false;
}
🤖 Prompt for AI Agents
In src/ai-providers/custom-sdk/grok-cli/errors.js around lines 110-118, the
current auth detection only checks for exitCode === 401; update the logic to
also inspect the CLI stderr (e.g. error.data?.stderr) for case-insensitive
markers like "unauthorized" or "401" and treat any non-zero exitCode combined
with those markers as an authentication error; ensure you null-check error.data
and stderr, normalize to lowercase, and return true when the stderr contains
those substrings.

Comment on lines +70 to +71
export const grokCli = createGrokCli();

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Optional: drop unused default instance export if not referenced.

If grokCli isn’t imported anywhere, consider removing to reduce surface area.

🤖 Prompt for AI Agents
In src/ai-providers/custom-sdk/grok-cli/index.js around lines 70–71, the file
exports a default instance "grokCli" that appears unused; remove the export
(delete the "export const grokCli = createGrokCli();" line) to reduce surface
area, or if the instance is needed elsewhere, replace/remove usages accordingly
(search the repo for imports and either wire those imports to this file or keep
the export). Ensure module exports remain consistent after removing the line.

Comment on lines +26 to +33
const objectMatch = jsonText.match(/{[\s\S]*}/);
const arrayMatch = jsonText.match(/\[[\s\S]*\]/);

if (objectMatch) {
jsonText = objectMatch[0];
} else if (arrayMatch) {
jsonText = arrayMatch[0];
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Avoid greedy JSON capture; pick the earliest object/array match.

Greedy /{[\s\S]*}/ can swallow too much and return invalid JSON when multiple braces exist. Use non-greedy matches and select the earliest occurrence.

-  const objectMatch = jsonText.match(/{[\s\S]*}/);
-  const arrayMatch = jsonText.match(/\[[\s\S]*\]/);
-
-  if (objectMatch) {
-    jsonText = objectMatch[0];
-  } else if (arrayMatch) {
-    jsonText = arrayMatch[0];
-  }
+  const objectMatch = jsonText.match(/{[\s\S]*?}/);
+  const arrayMatch = jsonText.match(/\[[\s\S]*?\]/);
+  let earliest = null;
+  if (objectMatch) earliest = { idx: jsonText.indexOf(objectMatch[0]), val: objectMatch[0] };
+  if (arrayMatch) {
+    const idx = jsonText.indexOf(arrayMatch[0]);
+    earliest = !earliest || idx < earliest.idx ? { idx, val: arrayMatch[0] } : earliest;
+  }
+  if (earliest) {
+    jsonText = earliest.val;
+  }
📝 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.

Suggested change
const objectMatch = jsonText.match(/{[\s\S]*}/);
const arrayMatch = jsonText.match(/\[[\s\S]*\]/);
if (objectMatch) {
jsonText = objectMatch[0];
} else if (arrayMatch) {
jsonText = arrayMatch[0];
}
const objectMatch = jsonText.match(/{[\s\S]*?}/);
const arrayMatch = jsonText.match(/\[[\s\S]*?\]/);
let earliest = null;
if (objectMatch) earliest = { idx: jsonText.indexOf(objectMatch[0]), val: objectMatch[0] };
if (arrayMatch) {
const idx = jsonText.indexOf(arrayMatch[0]);
earliest = !earliest || idx < earliest.idx ? { idx, val: arrayMatch[0] } : earliest;
}
if (earliest) {
jsonText = earliest.val;
}
🤖 Prompt for AI Agents
In src/ai-providers/custom-sdk/grok-cli/json-extractor.js around lines 26 to 33,
the current greedy regexes can capture too much and return invalid JSON when
multiple braces/brackets exist; change to non-greedy patterns (e.g. /{[\s\S]*?}/
and /\[[\s\S]*?\]/) and pick the earliest match by checking match indices (use
the first occurring match between object and array) so the extractor returns the
smallest/first valid JSON snippet instead of a greedy one.

Comment on lines +45 to +48
const converted = jsonText
.replace(/([{,]\s*)([a-zA-Z_$][a-zA-Z0-9_$]*)\s*:/g, '$1"$2":')
// Replace single quotes with double quotes
.replace(/'/g, '"');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Handle trailing commas during JS-to-JSON conversion.

Single-quote replacement is not enough; trailing commas commonly break parsing. Strip them before parsing.

-      const converted = jsonText
+      const converted = jsonText
         .replace(/([{,]\s*)([a-zA-Z_$][a-zA-Z0-9_$]*)\s*:/g, '$1"$2":')
         // Replace single quotes with double quotes
-        .replace(/'/g, '"');
+        .replace(/'/g, '"')
+        // Remove trailing commas before } or ]
+        .replace(/,\s*([}\]])/g, '$1');
📝 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.

Suggested change
const converted = jsonText
.replace(/([{,]\s*)([a-zA-Z_$][a-zA-Z0-9_$]*)\s*:/g, '$1"$2":')
// Replace single quotes with double quotes
.replace(/'/g, '"');
const converted = jsonText
.replace(/([{,]\s*)([a-zA-Z_$][a-zA-Z0-9_$]*)\s*:/g, '$1"$2":')
// Replace single quotes with double quotes
.replace(/'/g, '"')
// Remove trailing commas before } or ]
.replace(/,\s*([}\]])/g, '$1');
🤖 Prompt for AI Agents
In src/ai-providers/custom-sdk/grok-cli/json-extractor.js around lines 45 to 48,
the current JS-to-JSON conversion only adds quotes and replaces single quotes
but doesn't remove trailing commas which break JSON.parse; update the conversion
pipeline to strip trailing commas from objects and arrays (e.g., remove commas
before closing } and ] using a regex pass after quote fixes) so the resulting
string is valid JSON before parsing, and ensure this pass runs before JSON.parse
is called.

Comment on lines +109 to +167
async executeGrokCli(args, options = {}) {
const timeout = options.timeout || this.settings.timeout || 120000; // 2 minutes default

return new Promise((resolve, reject) => {
const child = spawn('grok', args, {
stdio: 'pipe',
cwd: this.settings.workingDirectory || process.cwd()
});

let stdout = '';
let stderr = '';
let timeoutId;

// Set up timeout
if (timeout > 0) {
timeoutId = setTimeout(() => {
child.kill('SIGTERM');
reject(createTimeoutError({
message: `Grok CLI command timed out after ${timeout}ms`,
timeoutMs: timeout,
promptExcerpt: args.join(' ').substring(0, 200)
}));
}, timeout);
}

child.stdout.on('data', (data) => {
stdout += data.toString();
});

child.stderr.on('data', (data) => {
stderr += data.toString();
});

child.on('error', (error) => {
if (timeoutId) clearTimeout(timeoutId);

if (error.code === 'ENOENT') {
reject(createInstallationError({}));
} else {
reject(createAPICallError({
message: `Failed to execute Grok CLI: ${error.message}`,
code: error.code,
stderr: error.message,
isRetryable: false
}));
}
});

child.on('exit', (exitCode) => {
if (timeoutId) clearTimeout(timeoutId);

resolve({
stdout: stdout.trim(),
stderr: stderr.trim(),
exitCode: exitCode || 0
});
});
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Timeout/settlement and exit handling are brittle; normalize exit status and prevent double-settle.

Guard against multiple resolve/reject, include signal, and don’t coerce null exit code to 0.

-  async executeGrokCli(args, options = {}) {
-    const timeout = options.timeout || this.settings.timeout || 120000; // 2 minutes default
+  async executeGrokCli(args, options = {}) {
+    const timeout = options.timeout || this.settings.timeout || 120000;
     return new Promise((resolve, reject) => {
-      const child = spawn('grok', args, {
-        stdio: 'pipe',
-        cwd: this.settings.workingDirectory || process.cwd()
-      });
+      const child = spawn('grok', args, {
+        stdio: 'pipe',
+        cwd: this.settings.workingDirectory || process.cwd(),
+        env: { ...process.env, ...(options.env || {}) }
+      });
       let stdout = '';
       let stderr = '';
-      let timeoutId;
+      let timeoutId;
+      let settled = false;
       if (timeout > 0) {
         timeoutId = setTimeout(() => {
-          child.kill('SIGTERM');
-          reject(createTimeoutError({
+          child.kill('SIGTERM');
+          const to = setTimeout(() => child.kill('SIGKILL'), 5000);
+          if (settled) return;
+          settled = true;
+          clearTimeout(to);
+          reject(createTimeoutError({
             message: `Grok CLI command timed out after ${timeout}ms`,
             timeoutMs: timeout,
-            promptExcerpt: args.join(' ').substring(0, 200)
+            promptExcerpt: '[prompt elided]'
           }));
         }, timeout);
       }
       child.stdout.on('data', (data) => { stdout += data.toString(); });
       child.stderr.on('data', (data) => { stderr += data.toString(); });
       child.on('error', (error) => {
-        if (timeoutId) clearTimeout(timeoutId);
+        if (timeoutId) clearTimeout(timeoutId);
+        if (settled) return;
+        settled = true;
         if (error.code === 'ENOENT') {
           reject(createInstallationError({}));
         } else {
           reject(createAPICallError({
             message: `Failed to execute Grok CLI: ${error.message}`,
             code: error.code,
             stderr: error.message,
             isRetryable: false
           }));
         }
       });
-      child.on('exit', (exitCode) => {
-        if (timeoutId) clearTimeout(timeoutId);
-        resolve({ stdout: stdout.trim(), stderr: stderr.trim(), exitCode: exitCode || 0 });
-      });
+      child.on('exit', (code, signal) => {
+        if (timeoutId) clearTimeout(timeoutId);
+        if (settled) return;
+        settled = true;
+        const exitCode = typeof code === 'number' ? code : 1;
+        resolve({ stdout: stdout.trim(), stderr: stderr.trim(), exitCode, signal });
+      });
     });
   }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/ai-providers/custom-sdk/grok-cli/language-model.js around lines 109 to
167, the child process handling can double-resolve/reject and incorrectly
coerces a null exitCode to 0; add a single settled flag to ensure only one
resolve/reject happens, always clear the timeout when settling, include the
process signal (if any) in the resolved/rejected payload, and do not force null
exitCode to 0 (keep null or undefined). Also ensure 'error' and 'exit' (or
'close') handlers check the settled flag before calling resolve/reject so you
never double-settle.

Comment on lines +223 to +247
// Build command arguments
const args = [
'--prompt', escapeShellArg(prompt)
];

// Add model if specified
if (this.modelId && this.modelId !== 'default') {
args.push('--model', this.modelId);
}

// Add API key if available
if (apiKey) {
args.push('--api-key', apiKey);
}

// Add base URL if provided in settings
if (this.settings.baseURL) {
args.push('--base-url', this.settings.baseURL);
}

// Add working directory if specified
if (this.settings.workingDirectory) {
args.push('--directory', this.settings.workingDirectory);
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Do not shell-escape prompt; pass raw arg to spawn.

escapeShellArg adds literal quotes when spawn is not using a shell.

-    const args = [
-      '--prompt', escapeShellArg(prompt)
-    ];
+    const args = ['--prompt', prompt];
📝 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.

Suggested change
// Build command arguments
const args = [
'--prompt', escapeShellArg(prompt)
];
// Add model if specified
if (this.modelId && this.modelId !== 'default') {
args.push('--model', this.modelId);
}
// Add API key if available
if (apiKey) {
args.push('--api-key', apiKey);
}
// Add base URL if provided in settings
if (this.settings.baseURL) {
args.push('--base-url', this.settings.baseURL);
}
// Add working directory if specified
if (this.settings.workingDirectory) {
args.push('--directory', this.settings.workingDirectory);
}
// Build command arguments
const args = ['--prompt', prompt];
// Add model if specified
if (this.modelId && this.modelId !== 'default') {
args.push('--model', this.modelId);
}
// Add API key if available
if (apiKey) {
args.push('--api-key', apiKey);
}
// Add base URL if provided in settings
if (this.settings.baseURL) {
args.push('--base-url', this.settings.baseURL);
}
// Add working directory if specified
if (this.settings.workingDirectory) {
args.push('--directory', this.settings.workingDirectory);
}
🤖 Prompt for AI Agents
In src/ai-providers/custom-sdk/grok-cli/language-model.js around lines 223 to
247, the code is calling escapeShellArg(prompt) which inserts literal quotes
when spawn is invoked without a shell; remove the shell-escaping and pass the
raw prompt string as the argument instead (i.e., use the prompt value directly
in the args array), ensuring any necessary sanitization is done elsewhere and
that spawn is invoked without shell:true so no further escaping is applied.

Comment on lines +233 to +237
// Add API key if available
if (apiKey) {
args.push('--api-key', apiKey);
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Avoid leaking API keys via process args; use env.

Passing --api-key exposes secrets via ps and error surfaces. Inject via env and remove from args.

-    if (apiKey) {
-      args.push('--api-key', apiKey);
-    }
+    const execEnv = apiKey ? { GROK_API_KEY: apiKey } : undefined;

And pass to executor:

-    const result = await this.executeGrokCli(args, {
-      timeout: this.settings.timeout
-    });
+    const result = await this.executeGrokCli(args, {
+      timeout: this.settings.timeout,
+      env: execEnv
+    });

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/ai-providers/custom-sdk/grok-cli/language-model.js around lines 233-237,
the code appends the API key to the CLI args which can leak secrets via process
listings; remove the '--api-key' and apiKey from args, instead set a well-named
environment variable (e.g. GROK_API_KEY) on the child process env passed to the
executor, ensure you merge it with the existing env (do not overwrite other
vars), and update the executor call to read the API key from process.env (or use
the env you pass) rather than from args.

Comment on lines +253 to +269
if (result.exitCode !== 0) {
// Handle authentication errors
if (result.exitCode === 401 || result.stderr.includes('unauthorized') || result.stderr.includes('authentication')) {
throw createAuthenticationError({
message: `Grok CLI authentication failed: ${result.stderr}`
});
}

throw createAPICallError({
message: `Grok CLI failed with exit code ${result.exitCode}: ${result.stderr || 'Unknown error'}`,
exitCode: result.exitCode,
stderr: result.stderr,
stdout: result.stdout,
promptExcerpt: prompt.substring(0, 200),
isRetryable: result.exitCode >= 500
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Sanitize error context; don’t include args (which may carry secrets).

Use a prompt excerpt only.

-    if (result.exitCode !== 0) {
+    if (result.exitCode !== 0) {
       if (result.exitCode === 401 || result.stderr.includes('unauthorized') || result.stderr.includes('authentication')) {
         throw createAuthenticationError({
           message: `Grok CLI authentication failed: ${result.stderr}`
         });
       }
       throw createAPICallError({
         message: `Grok CLI failed with exit code ${result.exitCode}: ${result.stderr || 'Unknown error'}`,
         exitCode: result.exitCode,
         stderr: result.stderr,
         stdout: result.stdout,
-        promptExcerpt: prompt.substring(0, 200),
+        promptExcerpt: prompt.substring(0, 200),
         isRetryable: result.exitCode >= 500
       });
     }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/ai-providers/custom-sdk/grok-cli/language-model.js around lines 253 to
269, the error objects include full command args and raw stdout/stderr which may
leak secrets; update the error construction to exclude any args and sensitive
runtime details and only attach a sanitized promptExcerpt (e.g.,
prompt.substring(0,200)) plus non-sensitive metadata (exitCode and isRetryable).
Specifically, remove args from the thrown error payloads and stop passing raw
stdout/stderr into createAPICallError/createAuthenticationError; instead include
only a brief promptExcerpt and exitCode (and isRetryable) so no command
arguments or full outputs are logged or surfaced.

Comment on lines +5 to +12
/**
* @typedef {Object} GrokCliSettings
* @property {string} [apiKey] - API key for Grok CLI
* @property {string} [baseURL] - Base URL for Grok API
* @property {string} [model] - Default model to use
* @property {number} [timeout] - Timeout in milliseconds
* @property {string} [workingDirectory] - Working directory for CLI commands
*/
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

Align settings typedef with actual usage (defaultModel).

Provider passes defaultModel into defaultSettings; include it in GrokCliSettings for clarity.

  /**
   * @typedef {Object} GrokCliSettings
   * @property {string} [apiKey] - API key for Grok CLI
   * @property {string} [baseURL] - Base URL for Grok API
-  * @property {string} [model] - Default model to use
+  * @property {string} [defaultModel] - Default model to use when modelId is omitted
   * @property {number} [timeout] - Timeout in milliseconds
   * @property {string} [workingDirectory] - Working directory for CLI commands
   */
📝 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.

Suggested change
/**
* @typedef {Object} GrokCliSettings
* @property {string} [apiKey] - API key for Grok CLI
* @property {string} [baseURL] - Base URL for Grok API
* @property {string} [model] - Default model to use
* @property {number} [timeout] - Timeout in milliseconds
* @property {string} [workingDirectory] - Working directory for CLI commands
*/
/**
* @typedef {Object} GrokCliSettings
* @property {string} [apiKey] - API key for Grok CLI
* @property {string} [baseURL] - Base URL for Grok API
* @property {string} [defaultModel] - Default model to use when modelId is omitted
* @property {number} [timeout] - Timeout in milliseconds
* @property {string} [workingDirectory] - Working directory for CLI commands
*/
🤖 Prompt for AI Agents
In src/ai-providers/custom-sdk/grok-cli/types.js around lines 5 to 12, the JSDoc
typedef GrokCliSettings is missing the defaultModel property that the provider
actually uses; add an optional string property named defaultModel to the typedef
(e.g., @property {string} [defaultModel] - Default model to use when not
specified) so the type definition aligns with defaultSettings and usage.

Crunchyman-ralph pushed a commit that referenced this pull request Sep 18, 2025
Co-authored-by: Max Tuzzolino <maxtuzz@Maxs-MacBook-Pro.local>
Crunchyman-ralph pushed a commit that referenced this pull request Sep 18, 2025
Co-authored-by: Max Tuzzolino <maxtuzz@Maxs-MacBook-Pro.local>
Crunchyman-ralph pushed a commit that referenced this pull request Sep 18, 2025
Co-authored-by: Max Tuzzolino <maxtuzz@Maxs-MacBook-Pro.local>
This was referenced Sep 19, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants