diff --git a/README.md b/README.md
index 54214f9..fcdc4f3 100644
--- a/README.md
+++ b/README.md
@@ -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
set working directory
+ -k, --api-key Grok API key (or set GROK_API_KEY env var)
+ -u, --base-url Grok API base URL (or set GROK_BASE_URL env var)
+ -m, --model AI model to use (e.g., grok-4-latest, grok-3-latest)
+ -p, --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:
diff --git a/package.json b/package.json
index 1088370..018b867 100644
--- a/package.json
+++ b/package.json
@@ -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": {
diff --git a/src/index.ts b/src/index.ts
index 2dc821e..7d00b14 100755
--- a/src/index.ts
+++ b/src/index.ts
@@ -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 {
+ 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 ", "set working directory", process.cwd())
.option("-k, --api-key ", "Grok API key (or set GROK_API_KEY env var)")
- .option("-u, --base-url ", "Grok API base URL (or set GROK_BASE_URL env var)")
- .option("-m, --model ", "AI model to use (e.g., gemini-2.5-pro, grok-4-latest)")
- .action((options) => {
+ .option(
+ "-u, --base-url ",
+ "Grok API base URL (or set GROK_BASE_URL env var)"
+ )
+ .option(
+ "-m, --model ",
+ "AI model to use (e.g., gemini-2.5-pro, grok-4-latest)"
+ )
+ .option(
+ "-p, --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);