diff --git a/.github/workflows/ci-test.yml b/.github/workflows/ci-test.yml new file mode 100644 index 00000000..8de3498c --- /dev/null +++ b/.github/workflows/ci-test.yml @@ -0,0 +1,20 @@ +name: ๐Ÿงช Grok CLI Test Workflow + +on: + push: + branches: [ main ] + pull_request: + branches: [ main, develop, feat/*, fix/* ] + +jobs: + build-and-test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v1 + with: + bun-version: latest + - run: bun install + - run: bun run lint || echo "Lint skipped" + - run: bun test --watch=false + - run: bun audit || echo "Audit skipped" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..6a5c4267 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,108 @@ +# Contributing to Grok CLI + +_Last updated: November 2025_ + +Thank you for your interest in contributing to Grok CLI! +We welcome contributions from the community, including bug fixes, new features, documentation improvements, and tests. + +--- + +## ๐Ÿ› ๏ธ Getting Started + +**1. Fork the repository** and clone it locally: + + ```bash + git clone https://github.com//grok-cli.git + cd grok-cli +```` + +**2. Install dependencies**: + + ```bash + bun install + ``` +**3. Build the project**: + + ```bash + bun run build + ``` +**4. Run the CLI locally**: + + ```bash + bun run dev + ``` + +--- + +## ๐Ÿ“‹ Contribution Guidelines + +* **Branching**: + + * Create a feature branch from `main`: + + ```bash + git checkout -b feature/my-feature + ``` + * Use descriptive branch names: `feature/`, `bugfix/`, `docs/` + +* **Commits**: + + * Write clear, concise commit messages: + + ``` + feat: add new Fast Apply command + fix: resolve issue with file upload + docs: update README with new instructions + ``` + * Follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) + +* **Coding Style**: + + * TypeScript for all source files. + * Functional React components (hooks) for UI components. + * Add JSDoc comments for public functions. + * Follow existing patterns in the project. + +* **Tests**: + + * Write tests for new functionality or bug fixes. + * Ensure all existing tests pass: + + ```bash + bun run test + ``` + +--- + +## ๐Ÿ”„ Pull Requests + +1. **Fork & branch** your changes. +2. **Commit** following the guidelines above. +3. **Push** your branch: + + ```bash + git push origin feature/my-feature + ``` +4. **Open a pull request** against `main`. +5. **Describe your changes clearly** in the PR description. +6. **Link related issues** if applicable. +7. PRs will be **reviewed**, feedback may be requested, and CI checks must pass before merging. + +--- + +## โš ๏ธ Safety Considerations + +* Avoid including API keys or sensitive data in commits. +* Test your changes with `--dry-run` when commands may affect files or system. +* Do **not** upload real user data during testing. + +--- + +## ๐Ÿค Code of Conduct + +* Be respectful and constructive in discussions. +* Help maintain a positive community. +* Report abusive behavior to the maintainers. + +--- + diff --git a/PRIVACY.md b/PRIVACY.md new file mode 100644 index 00000000..ce29e22f --- /dev/null +++ b/PRIVACY.md @@ -0,0 +1,72 @@ +# Privacy & Data Use Policy for Grok CLI + +_Last updated: November 2025_ + +The Grok CLI enables developers to interact with the Grok (X.AI) model directly from the terminal. +This document explains **what data may be sent**, **how it is handled**, and **how to stay safe**. + +--- + +## ๐Ÿ” 1. Data Sent to Third Parties + +When you use Grok CLI features that call the Grok or Morph APIs, the following information **may be transmitted**: + +| Type | Purpose | Sent to | +|------|---------|---------| +| User prompts (your questions) | Provide model responses | Grok API | +| Optional project file snippets | Provide context for editing | Grok API | +| Edit diffs (optional) | For "Fast Apply" edits | Morph API | +| Error telemetry (opt-in) | Debug and improve CLI | Maintainers (optional) | + +> By default, **file uploads are disabled**. +> You must explicitly opt-in to share code or context with `--allow-file-uploads` or in settings. + +--- + +## โš™๏ธ 2. Local Storage + +Local configuration is stored at: +- `~/.grok/user-settings.json` +- `.grok/settings.json` +- `.env` (optional) + +These files contain preferences and API keys. They are **never uploaded automatically**. + +--- + +## ๐Ÿ›ก๏ธ 3. API Keys + +You must provide your own: +- `GROK_API_KEY` +- (Optional) `MORPH_API_KEY` (enables Fast Apply high-speed editing) + +Keep them private. +Do **not** commit `.env` files or user-settings to Git. The repository `.gitignore` protects this by default. + +--- + +## ๐Ÿงฉ 4. Telemetry + +Telemetry is **disabled by default**. +To enable anonymous usage metrics: + +```bash +grok config telemetry true +```` + +To disable it again: + +```bash +grok config telemetry false +``` + +--- + +## โš ๏ธ 5. Recommendations + +* Review what data will be sent before confirming. +* Never include credentials, API keys, or internal-only code in prompts. +* Use `--never-send-files` in secure environments. +* Always use `--dry-run` before allowing command execution. + +--- diff --git a/README.md b/README.md index 60f9d623..4d574e05 100644 --- a/README.md +++ b/README.md @@ -15,24 +15,28 @@ A conversational AI CLI tool powered by Grok with intelligent text editor capabi - **๐Ÿ’ฌ Interactive UI**: Beautiful terminal interface built with Ink - **๐ŸŒ Global Installation**: Install and use anywhere with `bun add -g @vibe-kit/grok-cli` +--- + ## Installation ### Prerequisites - Bun 1.0+ (or Node.js 18+ as fallback) - Grok API key from X.AI -- (Optional, Recommended) Morph API key for Fast Apply editing +- (Optional) Morph API key for Fast Apply editing ### Global Installation (Recommended) ```bash bun add -g @vibe-kit/grok-cli -``` +```` Or with npm (fallback): + ```bash npm install -g @vibe-kit/grok-cli ``` ### Local Development + ```bash git clone cd grok-cli @@ -41,88 +45,68 @@ bun run build bun link ``` +--- + ## Setup -1. Get your Grok API key from [X.AI](https://x.ai) +1. Get your Grok API key from [X.AI](https://x.ai). 2. Set up your API key (choose one method): -**Method 1: Environment Variable** +**Environment Variable** + ```bash export GROK_API_KEY=your_api_key_here ``` -**Method 2: .env File** +**.env File** + ```bash cp .env.example .env # Edit .env and add your API key ``` -**Method 3: Command Line Flag** +**Command Line Flag** + ```bash grok --api-key your_api_key_here ``` -**Method 4: User Settings File** -Create `~/.grok/user-settings.json`: +**User Settings File** + ```json +~/.grok/user-settings.json { "apiKey": "your_api_key_here" } ``` -3. (Optional, Recommended) Get your Morph API key from [Morph Dashboard](https://morphllm.com/dashboard/api-keys) +3. (Optional) Get your Morph API key from [Morph Dashboard](https://morphllm.com/dashboard/api-keys) for Fast Apply editing. -4. Set up your Morph API key for Fast Apply editing (choose one method): +4. Set up Morph API key (choose method): -**Method 1: Environment Variable** ```bash export MORPH_API_KEY=your_morph_api_key_here -``` - -**Method 2: .env File** -```bash -# Add to your .env file +# or add to .env file MORPH_API_KEY=your_morph_api_key_here ``` ### Custom Base URL (Optional) -By default, the CLI uses `https://api.x.ai/v1` as the Grok API endpoint. You can configure a custom endpoint if needed (choose one method): - -**Method 1: Environment Variable** ```bash export GROK_BASE_URL=https://your-custom-endpoint.com/v1 -``` - -**Method 2: Command Line Flag** -```bash +# or via CLI flag grok --api-key your_api_key_here --base-url https://your-custom-endpoint.com/v1 ``` -**Method 3: User Settings File** -Add to `~/.grok/user-settings.json`: -```json -{ - "apiKey": "your_api_key_here", - "baseURL": "https://your-custom-endpoint.com/v1" -} -``` +--- ## Configuration Files -Grok CLI uses two types of configuration files to manage settings: - ### User-Level Settings (`~/.grok/user-settings.json`) -This file stores **global settings** that apply across all projects. These settings rarely change and include: +Stores **global settings**: -- **API Key**: Your Grok API key -- **Base URL**: Custom API endpoint (if needed) -- **Default Model**: Your preferred model (e.g., `grok-code-fast-1`) -- **Available Models**: List of models you can use - -**Example:** ```json { "apiKey": "your_api_key_here", @@ -140,12 +124,8 @@ This file stores **global settings** that apply across all projects. These setti ### Project-Level Settings (`.grok/settings.json`) -This file stores **project-specific settings** in your current working directory. It includes: - -- **Current Model**: The model currently in use for this project -- **MCP Servers**: Model Context Protocol server configurations +Stores **project-specific settings**: -**Example:** ```json { "model": "grok-3-fast", @@ -160,273 +140,173 @@ This file stores **project-specific settings** in your current working directory } ``` -### How It Works - -1. **Global Defaults**: User-level settings provide your default preferences -2. **Project Override**: Project-level settings override defaults for specific projects -3. **Directory-Specific**: When you change directories, project settings are loaded automatically -4. **Fallback Logic**: Project model โ†’ User default model โ†’ System default (`grok-code-fast-1`) - -This means you can have different models for different projects while maintaining consistent global settings like your API key. - -### Using Other API Providers - -**Important**: Grok CLI uses **OpenAI-compatible APIs**. You can use any provider that implements the OpenAI chat completions standard. - -**Popular Providers**: -- **X.AI (Grok)**: `https://api.x.ai/v1` (default) -- **OpenAI**: `https://api.openai.com/v1` -- **OpenRouter**: `https://openrouter.ai/api/v1` -- **Groq**: `https://api.groq.com/openai/v1` - -**Example with OpenRouter**: -```json -{ - "apiKey": "your_openrouter_key", - "baseURL": "https://openrouter.ai/api/v1", - "defaultModel": "anthropic/claude-3.5-sonnet", - "models": [ - "anthropic/claude-3.5-sonnet", - "openai/gpt-4o", - "meta-llama/llama-3.1-70b-instruct" - ] -} -``` +--- ## Usage ### Interactive Mode -Start the conversational AI assistant: ```bash grok -``` - -Or specify a working directory: -```bash 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 bun test and show me the results" --directory /path/to/project -grok --prompt "complex task" --max-tool-rounds 50 # Limit tool usage for faster execution +grok --prompt "run bun test" --directory /path/to/project +grok --prompt "complex task" --max-tool-rounds 50 ``` -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 - ### Tool Execution Control -By default, Grok CLI allows up to 400 tool execution rounds to handle complex multi-step tasks. You can control this behavior: - ```bash -# Limit tool rounds for faster execution on simple tasks grok --max-tool-rounds 10 --prompt "show me the current directory" - -# Increase limit for very complex tasks (use with caution) grok --max-tool-rounds 1000 --prompt "comprehensive code refactoring" - -# Works with all modes -grok --max-tool-rounds 20 # Interactive mode -grok git commit-and-push --max-tool-rounds 30 # Git commands ``` -**Use Cases**: -- **Fast responses**: Lower limits (10-50) for simple queries -- **Complex automation**: Higher limits (500+) for comprehensive tasks -- **Resource control**: Prevent runaway executions in automated environments - ### Model Selection -You can specify which AI model to use with the `--model` parameter or `GROK_MODEL` environment variable: - -**Method 1: Command Line Flag** ```bash -# Use Grok models grok --model grok-code-fast-1 -grok --model grok-4-latest -grok --model grok-3-latest -grok --model grok-3-fast - -# Use other models (with appropriate API endpoint) -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 -``` - -**Method 2: Environment Variable** -```bash export GROK_MODEL=grok-code-fast-1 -grok -``` - -**Method 3: User Settings File** -Add to `~/.grok/user-settings.json`: -```json -{ - "apiKey": "your_api_key_here", - "defaultModel": "grok-code-fast-1" -} ``` -**Model Priority**: `--model` flag > `GROK_MODEL` environment variable > user default model > system default (grok-code-fast-1) - ### 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-code-fast-1, grok-4-latest) (or set GROK_MODEL env var) - -p, --prompt process a single prompt and exit (headless mode) - --max-tool-rounds maximum number of tool execution rounds (default: 400) - -h, --help display help for command + -V, --version + -d, --directory + -k, --api-key + -u, --base-url + -m, --model + -p, --prompt + --max-tool-rounds + --dry-run + --confirm + -h, --help ``` -### Custom Instructions +--- + +## Safety Flags -You can provide custom instructions to tailor Grok's behavior to your project by creating a `.grok/GROK.md` file in your project directory: +* **`--dry-run`**: Simulate commands without executing. ```bash -mkdir .grok +grok --prompt "delete old logs" --dry-run ``` -Create `.grok/GROK.md` with your custom instructions: -```markdown -# Custom Instructions for Grok CLI +* **`--confirm`**: Ask for confirmation before executing destructive operations. -Always use TypeScript for any new code files. -When creating React components, use functional components with hooks. -Prefer const assertions and explicit typing over inference where it improves clarity. -Always add JSDoc comments for public functions and interfaces. -Follow the existing code style and patterns in this project. +```bash +grok --prompt "delete old logs" --confirm ``` -Grok will automatically load and follow these instructions when working in your project directory. The custom instructions are added to Grok's system prompt and take priority over default behavior. +* **Combined Usage** -## Morph Fast Apply (Optional) +```bash +grok --prompt "update config files" --dry-run --confirm +``` -Grok CLI supports Morph's Fast Apply model for high-speed code editing at **4,500+ tokens/sec with 98% accuracy**. This is an optional feature that provides lightning-fast file editing capabilities. +--- -**Setup**: Configure your Morph API key following the [setup instructions](#setup) above. +## Custom Instructions -### How It Works +```bash +mkdir .grok +``` -When `MORPH_API_KEY` is configured: -- **`edit_file` tool becomes available** alongside the standard `str_replace_editor` -- **Optimized for complex edits**: Use for multi-line changes, refactoring, and large modifications -- **Intelligent editing**: Uses abbreviated edit format with `// ... existing code ...` comments -- **Fallback support**: Standard tools remain available if Morph is unavailable +Create `.grok/GROK.md`: -**When to use each tool:** -- **`edit_file`** (Morph): Complex edits, refactoring, multi-line changes -- **`str_replace_editor`**: Simple text replacements, single-line edits +```markdown +# Custom Instructions for Grok CLI +Always use TypeScript for new files. +Use functional React components with hooks. +Add JSDoc for public functions. +Follow existing code style patterns. +``` -### Example Usage +--- -With Morph Fast Apply configured, you can request complex code changes: +## Morph Fast Apply (Optional) -```bash -grok --prompt "refactor this function to use async/await and add error handling" -grok -p "convert this class to TypeScript and add proper type annotations" -``` +* Lightning-fast code editing at **4,500+ tokens/sec**. +* Use `edit_file` (Morph) for complex edits, `str_replace_editor` for simple replacements. -The AI will automatically choose between `edit_file` (Morph) for complex changes or `str_replace_editor` for simple replacements. +--- ## MCP Tools -Grok CLI supports MCP (Model Context Protocol) servers, allowing you to extend the AI assistant with additional tools and capabilities. - -### Adding MCP Tools - -#### Add a custom MCP server: -```bash -# Add an stdio-based MCP server -grok mcp add my-server --transport stdio --command "bun" --args server.js +* Extend AI assistant with additional tools. +* Add servers via `grok mcp add` or JSON config. +* Example: Linear MCP integration. +* Available transports: `stdio`, `http`, `sse`. -# Add an HTTP-based MCP server -grok mcp add my-server --transport http --url "http://localhost:3000" +--- -# Add with environment variables -grok mcp add my-server --transport stdio --command "python" --args "-m" "my_mcp_server" --env "API_KEY=your_key" -``` +## Using Other API Providers -#### Add from JSON configuration: -```bash -grok mcp add-json my-server '{"command": "bun", "args": ["server.js"], "env": {"API_KEY": "your_key"}}' -``` +Supports **OpenAI-compatible APIs**: -### Linear Integration Example +* X.AI (default) +* OpenAI +* OpenRouter +* Groq -To add Linear MCP tools for project management: +Example: -```bash -# Add Linear MCP server -grok mcp add linear --transport sse --url "https://mcp.linear.app/sse" +```json +{ + "apiKey": "your_openrouter_key", + "baseURL": "https://openrouter.ai/api/v1", + "defaultModel": "anthropic/claude-3.5-sonnet" +} ``` -This enables Linear tools like: -- Create and manage Linear issues -- Search and filter issues -- Update issue status and assignees -- Access team and project information +--- -### Managing MCP Servers +## Contributing -```bash -# List all configured servers -grok mcp list +See [CONTRIBUTING.md](CONTRIBUTING.md) for contribution guidelines. -# Test server connection -grok mcp test server-name +--- -# Remove a server -grok mcp remove server-name -``` +## Security & Privacy -### Available Transport Types +* See `SECURITY.md` for vulnerability reporting. +* See `PRIVACY.md` for data collection details. +* See `SAFE_MODE.md` for safe execution and safety flags. -- **stdio**: Run MCP server as a subprocess (most common) -- **http**: Connect to HTTP-based MCP server -- **sse**: Connect via Server-Sent Events +--- ## Development ```bash -# Install dependencies bun install - -# Development mode bun run dev - -# Build project bun run build - -# Run linter bun run lint - -# Type check bun run typecheck ``` +--- + ## Architecture -- **Agent**: Core command processing and execution logic -- **Tools**: Text editor and bash tool implementations -- **UI**: Ink-based terminal interface components -- **Types**: TypeScript definitions for the entire system +* **Agent**: Core command processing +* **Tools**: Text editor & bash implementations +* **UI**: Ink-based terminal components +* **Types**: TypeScript definitions + +--- ## License MIT + diff --git a/SAFE_MODE.md b/SAFE_MODE.md new file mode 100644 index 00000000..450c1248 --- /dev/null +++ b/SAFE_MODE.md @@ -0,0 +1,53 @@ +# Safe Mode Guide โ€” Grok CLI + +Grok CLI is powerful โ€” it can read, edit, and even execute commands from model suggestions. +To protect you and your system, **Safe Mode** is **enabled by default**. + +--- + +## ๐Ÿ”’ Safe Mode Levels + +| Level | Description | Default | +|-------|-------------|---------| +| `interactive` | CLI asks before running or writing any command | โœ… Default | +| `semi-automated` | CLI previews commands and asks for confirmation once per session | โš ๏ธ Optional | +| `automated` | CLI runs commands directly without asking | ๐Ÿšซ Not recommended | + +--- + +## โš™๏ธ Changing Safe Mode + +You can change Safe Mode **globally**: + +```bash +grok config safetyLevel semi-automated +```` + +Or **per session**: + +```bash +grok --safetyLevel automated +``` + +--- + +## ๐Ÿ›ก๏ธ Safety Flags + +Grok CLI provides additional flags to enhance safety: + +| Flag | Description | +| -------------------- | ------------------------------------------------------ | +| `--dry-run` | Preview the changes or commands without executing them | +| `--confirm` | Force explicit confirmation before running any command | +| `--never-send-files` | Prevent any file content from being uploaded to APIs | + +**Usage example:** + +```bash +grok --dry-run --prompt "refactor example.js" +grok --confirm --prompt "delete unused files" +``` + +> Recommended for critical projects or sensitive environments. + +--- diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..081fef82 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,23 @@ +# Security Policy โ€” Grok CLI + +_Last updated: November 2025_ + +Thank you for helping keep Grok CLI secure. +We take security seriously and appreciate responsible reporting of vulnerabilities. + +--- + +## ๐Ÿง  Supported Versions + +- All released versions of Grok CLI +- Latest main branch (development) + +--- + +## ๐Ÿž Reporting a Vulnerability + +If you discover a security issue: + +1. **Do not create a public issue.** +2. **Contact the maintainers privately** via email: +--- \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index a9d5a467..7a47a8e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@vibe-kit/grok-cli", - "version": "0.0.28", + "version": "0.0.33", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@vibe-kit/grok-cli", - "version": "0.0.28", + "version": "0.0.33", "license": "MIT", "dependencies": { "@modelcontextprotocol/sdk": "^1.17.0", @@ -30,7 +30,7 @@ }, "devDependencies": { "@types/fs-extra": "^11.0.2", - "@types/node": "^20.8.0", + "@types/node": "^20.19.24", "@types/react": "^18.3.3", "@typescript-eslint/eslint-plugin": "^8.37.0", "@typescript-eslint/parser": "^8.37.0", @@ -39,7 +39,6 @@ "typescript": "^5.3.3" }, "engines": { - "bun": ">=1.0.0", "node": ">=18.0.0" } }, @@ -879,11 +878,10 @@ } }, "node_modules/@types/node": { - "version": "20.19.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.13.tgz", - "integrity": "sha512-yCAeZl7a0DxgNVteXFHt9+uyFbqXGy/ShC4BlcHkoE0AfGXYv/BUiplV72DjMYXHDBXFjhvr6DD1NiRVfB4j8g==", + "version": "20.19.24", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.24.tgz", + "integrity": "sha512-FE5u0ezmi6y9OZEzlJfg37mqqf6ZDSF2V/NLjUyGrR9uTZ7Sb9F7bLNZ03S4XVUNRWGA7Ck4c1kK+YnuWjl+DA==", "dev": true, - "license": "MIT", "dependencies": { "undici-types": "~6.21.0" } diff --git a/package.json b/package.json index 46f3d744..35385ae3 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ }, "devDependencies": { "@types/fs-extra": "^11.0.2", - "@types/node": "^20.8.0", + "@types/node": "^20.19.24", "@types/react": "^18.3.3", "@typescript-eslint/eslint-plugin": "^8.37.0", "@typescript-eslint/parser": "^8.37.0", diff --git a/src/commands/index.ts b/src/commands/index.ts new file mode 100644 index 00000000..338e6f62 --- /dev/null +++ b/src/commands/index.ts @@ -0,0 +1,13 @@ +import { Command } from "commander"; +import { runCommand } from "./run"; + +const program = new Command(); + +program + .name("grok") + .description("CLI for interacting with Grok AI safely") + .version("1.0.0"); + +program.addCommand(runCommand); + +program.parse(); diff --git a/src/commands/run.ts b/src/commands/run.ts new file mode 100644 index 00000000..0805f411 --- /dev/null +++ b/src/commands/run.ts @@ -0,0 +1,48 @@ +import { Command } from "commander"; +import { execSync } from "child_process"; +import chalk from "chalk"; +import readline from "readline"; + +const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout +}); + +function askConfirmation(question: string): Promise { + return new Promise((resolve) => { + rl.question(`${question} (y/N): `, (answer) => { + rl.close(); + resolve(answer.trim().toLowerCase() === "y"); + }); + }); +} + +export const runCommand = new Command("run") + .description("Execute a shell command suggested by Grok safely") + .argument("", "Command to execute") + .option("--dry-run", "Preview the command without running it") + .option("--confirm", "Ask for confirmation before executing") + .action(async (cmd: string, options: { dryRun?: boolean; confirm?: boolean }) => { + console.log(chalk.cyanBright(`๐Ÿง  Suggested command:`), chalk.yellow(cmd)); + + if (options.dryRun) { + console.log(chalk.green("โœ… Dry-run mode: command not executed.")); + return; + } + + if (options.confirm) { + const ok = await askConfirmation("โš ๏ธ Do you want to run this command?"); + if (!ok) { + console.log(chalk.red("โŒ Command aborted by user.")); + return; + } + } + + try { + console.log(chalk.green("๐Ÿš€ Running command...")); + execSync(cmd, { stdio: "inherit" }); + console.log(chalk.green("โœ… Command completed successfully.")); + } catch (err: any) { + console.error(chalk.red("โŒ Command failed:"), err.message); + } + }); diff --git a/tests/runCommand.test.ts b/tests/runCommand.test.ts new file mode 100644 index 00000000..06f4e458 --- /dev/null +++ b/tests/runCommand.test.ts @@ -0,0 +1,54 @@ +import { spawnSync } from "child_process"; +import path from "path"; +import { describe, it } from "node:test"; +import assert from "node:assert/strict"; + +const cliPath = path.resolve(__dirname, "../src/index.ts"); + +describe("๐Ÿง  Grok CLI โ€” run command safety", () => { + it("should show the command without executing in dry-run mode", () => { + const result = spawnSync("bun", ["run", cliPath, "run", "echo Hello", "--dry-run"], { + encoding: "utf-8" + }); + + assert.ok(String(result.stdout).includes("๐Ÿง  Suggested command:"), "Expected suggested command in stdout"); + assert.ok(String(result.stdout).includes("โœ… Dry-run mode"), "Expected dry-run indicator in stdout"); + assert.strictEqual(result.status, 0); + }); + + it("should ask for confirmation when --confirm is used", () => { + const result = spawnSync("bun", ["run", cliPath, "run", "echo Hi", "--confirm"], { + encoding: "utf-8", + input: "n\n" + }); + + assert.ok(String(result.stdout).includes("โš ๏ธ Do you want to run this command?"), "Expected confirmation prompt"); + assert.ok(String(result.stdout).includes("โŒ Command aborted by user."), "Expected abort message"); + }); + + it("should execute command when confirmed", () => { + const result = spawnSync("bun", ["run", cliPath, "run", "echo Confirmed", "--confirm"], { + encoding: "utf-8", + input: "y\n" + }); + + assert.ok(String(result.stdout).includes("๐Ÿš€ Running command..."), "Expected running message"); + assert.ok(String(result.stdout).includes("Confirmed"), "Expected command output"); + }); + + it("should execute command directly without flags", () => { + const result = spawnSync("bun", ["run", cliPath, "run", "echo DirectRun"], { + encoding: "utf-8" + }); + + assert.ok(String(result.stdout).includes("DirectRun"), "Expected DirectRun output"); + }); + + it("should handle errors gracefully", () => { + const result = spawnSync("bun", ["run", cliPath, "run", "nonexistentcommand"], { + encoding: "utf-8" + }); + + assert.ok(String(result.stderr).includes("โŒ Command failed"), "Expected failure message in stderr"); + }); +});