add support for headless mode (#31)
This commit is contained in:
1 parent
5c3ba942ea
commit
f020b1d4d8
3 files changed
+139
-16
No files matched your search
@@ -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
@@ -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
@@ -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);
|
||||
|
||||
Reference in new issue
Block a user