add support for headless mode (#31)

This commit is contained in:
Ismail Pelaseyed authored and GitHub committed 2025-07-23 06:30:08 +02:00
1 parent 5c3ba942ea
commit f020b1d4d8
3 files changed
+139 -16

No files matched your search

+32
View File
@@ -88,6 +88,8 @@ Add to `~/.grok/user-settings.json`:
## Usage
### Interactive Mode
Start the conversational AI assistant:
```bash
grok
@@ -98,6 +100,21 @@ Or specify a working directory:
grok -d /path/to/project
```
### Headless Mode
Process a single prompt and exit (useful for scripting and automation):
```bash
grok --prompt "show me the package.json file"
grok -p "create a new file called example.js with a hello world function"
grok --prompt "run npm test and show me the results" --directory /path/to/project
```
This mode is particularly useful for:
- **CI/CD pipelines**: Automate code analysis and file operations
- **Scripting**: Integrate AI assistance into shell scripts
- **Terminal benchmarks**: Perfect for tools like Terminal Bench that need non-interactive execution
- **Batch processing**: Process multiple prompts programmatically
### Model Selection
You can specify which AI model to use with the `--model` parameter:
@@ -113,6 +130,21 @@ grok --model gemini-2.5-pro --base-url https://api-endpoint.com/v1
grok --model claude-sonnet-4-20250514 --base-url https://api-endpoint.com/v1
```
### Command Line Options
```bash
grok [options]
Options:
-V, --version output the version number
-d, --directory <dir> set working directory
-k, --api-key <key> Grok API key (or set GROK_API_KEY env var)
-u, --base-url <url> Grok API base URL (or set GROK_BASE_URL env var)
-m, --model <model> AI model to use (e.g., grok-4-latest, grok-3-latest)
-p, --prompt <prompt> process a single prompt and exit (headless mode)
-h, --help display help for command
```
### Custom Instructions
You can provide custom instructions to tailor Grok's behavior to your project by creating a `.grok/GROK.md` file in your project directory:
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@vibe-kit/grok-cli",
"version": "0.0.4",
"version": "0.0.5",
"description": "An open-source AI agent that brings the power of Grok directly into your terminal.",
"main": "dist/index.js",
"bin": {
+106 -15
View File
@@ -9,6 +9,7 @@ import ChatInterface from "./ui/components/chat-interface";
import * as fs from "fs";
import * as path from "path";
import * as os from "os";
import { ConfirmationService } from "./utils/confirmation-service";
// Load environment variables
dotenv.config();
@@ -17,22 +18,22 @@ dotenv.config();
function loadApiKey(): string | undefined {
// First check environment variables
let apiKey = process.env.GROK_API_KEY;
if (!apiKey) {
// Try to load from user settings file
try {
const homeDir = os.homedir();
const settingsFile = path.join(homeDir, '.grok', 'user-settings.json');
const settingsFile = path.join(homeDir, ".grok", "user-settings.json");
if (fs.existsSync(settingsFile)) {
const settings = JSON.parse(fs.readFileSync(settingsFile, 'utf8'));
const settings = JSON.parse(fs.readFileSync(settingsFile, "utf8"));
apiKey = settings.apiKey;
}
} catch (error) {
// Ignore errors, apiKey will remain undefined
}
}
return apiKey;
}
@@ -40,25 +41,92 @@ function loadApiKey(): string | undefined {
function loadBaseURL(): string | undefined {
// First check environment variables
let baseURL = process.env.GROK_BASE_URL;
if (!baseURL) {
// Try to load from user settings file
try {
const homeDir = os.homedir();
const settingsFile = path.join(homeDir, '.grok', 'user-settings.json');
const settingsFile = path.join(homeDir, ".grok", "user-settings.json");
if (fs.existsSync(settingsFile)) {
const settings = JSON.parse(fs.readFileSync(settingsFile, 'utf8'));
const settings = JSON.parse(fs.readFileSync(settingsFile, "utf8"));
baseURL = settings.baseURL;
}
} catch (error) {
// Ignore errors, baseURL will remain undefined
}
}
return baseURL;
}
// Headless mode processing function
async function processPromptHeadless(
prompt: string,
apiKey: string,
baseURL?: string,
model?: string
): Promise<void> {
try {
const agent = new GrokAgent(apiKey, baseURL, model);
// Configure confirmation service for headless mode (auto-approve all operations)
const confirmationService = ConfirmationService.getInstance();
confirmationService.setSessionFlag("allOperations", true);
console.log("🤖 Processing prompt...\n");
// Process the user message
const chatEntries = await agent.processUserMessage(prompt);
// Output the results
for (const entry of chatEntries) {
switch (entry.type) {
case "user":
console.log(`> ${entry.content}\n`);
break;
case "assistant":
if (entry.content.trim()) {
console.log(entry.content.trim());
console.log();
}
break;
case "tool_result":
const toolName = entry.toolCall?.function?.name || "unknown";
const getFilePath = (toolCall: any) => {
if (toolCall?.function?.arguments) {
try {
const args = JSON.parse(toolCall.function.arguments);
if (toolCall.function.name === "search") {
return args.query;
}
return args.path || args.file_path || args.command || "";
} catch {
return "";
}
}
return "";
};
const filePath = getFilePath(entry.toolCall);
const toolDisplay = filePath ? `${toolName}(${filePath})` : toolName;
if (entry.toolResult?.success) {
console.log(`✅ ${toolDisplay}: ${entry.content.split("\n")[0]}`);
} else {
console.log(`❌ ${toolDisplay}: ${entry.content}`);
}
break;
}
}
} catch (error: any) {
console.error("❌ Error processing prompt:", error.message);
process.exit(1);
}
}
program
.name("grok")
.description(
@@ -67,9 +135,19 @@ program
.version("1.0.0")
.option("-d, --directory <dir>", "set working directory", process.cwd())
.option("-k, --api-key <key>", "Grok API key (or set GROK_API_KEY env var)")
.option("-u, --base-url <url>", "Grok API base URL (or set GROK_BASE_URL env var)")
.option("-m, --model <model>", "AI model to use (e.g., gemini-2.5-pro, grok-4-latest)")
.action((options) => {
.option(
"-u, --base-url <url>",
"Grok API base URL (or set GROK_BASE_URL env var)"
)
.option(
"-m, --model <model>",
"AI model to use (e.g., gemini-2.5-pro, grok-4-latest)"
)
.option(
"-p, --prompt <prompt>",
"process a single prompt and exit (headless mode)"
)
.action(async (options) => {
if (options.directory) {
try {
process.chdir(options.directory);
@@ -87,10 +165,23 @@ program
const apiKey = options.apiKey || loadApiKey();
const baseURL = options.baseUrl || loadBaseURL();
const model = options.model;
const agent = apiKey ? new GrokAgent(apiKey, baseURL, model) : undefined;
if (!apiKey) {
console.error(
"❌ Error: API key required. Set GROK_API_KEY environment variable, use --api-key flag, or save to ~/.grok/user-settings.json"
);
process.exit(1);
}
// Headless mode: process prompt and exit
if (options.prompt) {
await processPromptHeadless(options.prompt, apiKey, baseURL, model);
return;
}
// Interactive mode: launch UI
const agent = new GrokAgent(apiKey, baseURL, model);
console.log("🤖 Starting Grok CLI Conversational Assistant...\n");
render(React.createElement(ChatInterface, { agent }));
} catch (error: any) {
console.error("❌ Error initializing Grok CLI:", error.message);