diff --git a/.cursor/rules/development-workflow.mdc b/.cursor/rules/development-workflow.mdc new file mode 100644 index 0000000..15e7143 --- /dev/null +++ b/.cursor/rules/development-workflow.mdc @@ -0,0 +1,62 @@ +--- +description: Development workflow, scripts, and common tasks +globs: +alwaysApply: true +--- + +# Development Workflow + +## Prerequisites + +- Node.js 18+ (required) +- Bun 1.0+ (preferred, npm as fallback) + +## Common Commands + +```bash +# Install dependencies +bun install # preferred +npm install # fallback + +# Development (runs from source) +bun run dev # uses Bun runtime +bun run dev:node # uses tsx (Node.js + TypeScript) + +# Build (compile TypeScript to dist/) +bun run build + +# Type checking (no emit) +bun run typecheck + +# Linting +bun run lint + +# Run the built CLI +bun run start # Node.js +bun run start:bun # Bun runtime +``` + +## Environment Variables + +| Variable | Required | Description | +|----------|----------|-------------| +| `GROK_API_KEY` | Yes | API key for Grok / OpenAI-compatible provider | +| `GROK_BASE_URL` | No | Custom API endpoint (default: `https://api.x.ai/v1`) | +| `GROK_MODEL` | No | Model override (default: `grok-code-fast-1`) | +| `GROK_MAX_TOKENS` | No | Max tokens per response (default: 1536) | +| `MORPH_API_KEY` | No | Enables Morph Fast Apply editing | + +Copy `.env.example` to `.env` and fill in your values. + +## Before Submitting Changes + +1. Run `bun run typecheck` — CI enforces this on every PR. +2. Run `bun run lint` — fix any ESLint errors. +3. Ensure no secrets or `.env` files are committed. +4. Follow the PR template in `.github/pull_request_template.md`. + +## Project Structure Conventions + +- Source code lives in `src/`, compiled output in `dist/` (gitignored). +- No test framework is configured yet — if adding tests, prefer Vitest for ESM compatibility. +- The CLI entry point is `src/index.ts` which uses Commander.js for argument parsing. diff --git a/.cursor/rules/project-overview.mdc b/.cursor/rules/project-overview.mdc new file mode 100644 index 0000000..cc5da0d --- /dev/null +++ b/.cursor/rules/project-overview.mdc @@ -0,0 +1,51 @@ +--- +description: Project overview and architecture for Grok CLI +globs: +alwaysApply: true +--- + +# Grok CLI — Project Overview + +Grok CLI (`@vibe-kit/grok-cli`) is an open-source conversational AI CLI tool powered by Grok that brings AI assistance directly into the terminal. + +## Tech Stack + +- **Language**: TypeScript (ES2022, ESNext modules) +- **Runtime**: Node.js 18+ (Bun preferred) +- **UI Framework**: React with [Ink](https://github.com/vadimdemedes/ink) for terminal rendering +- **Build**: `tsc` (TypeScript compiler) +- **Package Manager**: Bun (primary), npm (fallback) +- **Linting**: ESLint with `@typescript-eslint` +- **API Client**: OpenAI SDK (used for OpenAI-compatible APIs including Grok) + +## Architecture + +``` +src/ +├── index.ts # CLI entry point (Commander.js) +├── agent/ # GrokAgent — orchestrates client, tools, MCP +├── grok/ # Grok API client and tool definitions +├── mcp/ # Model Context Protocol integration +├── commands/ # CLI subcommands (e.g. mcp) +├── tools/ # Built-in tools (bash, text-editor, search, etc.) +├── ui/ # Ink-based terminal UI +│ ├── app.tsx +│ ├── components/ # Chat interface, diff renderer, inputs, etc. +│ └── utils/ # Colors, markdown rendering, code colorizer +├── hooks/ # React hooks for input handling +├── utils/ # Settings, token counting, custom instructions +└── types/ # Shared TypeScript type definitions +``` + +## Key Patterns + +- **Agent loop**: `GrokAgent` runs an iterative tool-call loop — the LLM responds, tools execute, results feed back until no more tool calls remain or the round limit is hit. +- **Streaming**: `processUserMessageStream` is an async generator that yields chunks for real-time UI updates. +- **Settings hierarchy**: User-level (`~/.grok/user-settings.json`) → Project-level (`.grok/settings.json`) → System defaults. +- **MCP**: External tools are integrated via Model Context Protocol servers (stdio, HTTP, SSE transports). +- **ESM only**: The project uses `"type": "module"` — all imports use `.js` extensions for compiled output. + +## CI/CD + +- **typecheck.yml**: Runs `tsc --noEmit` on push/PR to `main`/`develop`. +- **security.yml**: Runs `npm audit` and TruffleHog secret scanning. diff --git a/.cursor/rules/react-ink-components.mdc b/.cursor/rules/react-ink-components.mdc new file mode 100644 index 0000000..d6d6716 --- /dev/null +++ b/.cursor/rules/react-ink-components.mdc @@ -0,0 +1,45 @@ +--- +description: Guidelines for React components using Ink for terminal UI +globs: "src/ui/**/*.tsx,src/hooks/**/*.ts" +alwaysApply: false +--- + +# React + Ink Terminal UI + +## Framework + +This project uses [Ink](https://github.com/vadimdemedes/ink) v4 to render React components in the terminal. Ink provides ``, ``, and other primitives instead of HTML elements. + +## Component Patterns + +- Use **functional components** with hooks — no class components. +- JSX is compiled with `"jsx": "react"` (classic transform), so `import React from "react"` is required in every `.tsx` file. +- Components are in `src/ui/components/` and follow kebab-case naming (`chat-interface.tsx`, `diff-renderer.tsx`). + +## Key Components + +| Component | Purpose | +|-----------|---------| +| `ChatInterface` | Main chat loop, orchestrates agent interaction | +| `ChatHistory` | Renders conversation entries | +| `ChatInput` | User text input with key bindings | +| `DiffRenderer` | Displays file diffs | +| `ModelSelection` | Model picker UI | +| `LoadingSpinner` | Animated loading indicator | +| `ConfirmationDialog` | User confirmation prompts for tool operations | +| `McpStatus` | MCP server connection status | +| `CommandSuggestions` | Autocomplete suggestions | +| `ApiKeyInput` | API key entry prompt | + +## Ink-Specific Guidelines + +- Use `` for layout (flexbox model) and `` for styled text. +- Use Ink's `useInput` hook for keyboard handling. +- Use `chalk` for color utilities in `src/ui/utils/colors.ts`. +- Markdown rendering for chat output uses `marked` + `marked-terminal`. +- The app is rendered via `render(React.createElement(ChatInterface, { agent, initialMessage }))` in the entry point. + +## Hooks + +- Custom hooks live in `src/hooks/`. +- Follow the `use` prefix convention (`useInput`, `useChat`, etc.). diff --git a/.cursor/rules/tools-and-agent.mdc b/.cursor/rules/tools-and-agent.mdc new file mode 100644 index 0000000..3818f42 --- /dev/null +++ b/.cursor/rules/tools-and-agent.mdc @@ -0,0 +1,69 @@ +--- +description: Guidelines for the agent system and tool implementations +globs: "src/agent/**/*.ts,src/tools/**/*.ts,src/grok/**/*.ts" +alwaysApply: false +--- + +# Agent & Tools System + +## GrokAgent + +`GrokAgent` (in `src/agent/grok-agent.ts`) is the core orchestrator. It extends `EventEmitter` and manages: + +- The Grok API client (`GrokClient`) +- Built-in tools (text editor, bash, search, todo, confirmation) +- Optional Morph Fast Apply editor +- MCP server integration +- Chat history and message accumulation +- Abort/cancellation support + +### Agent Loop + +The agent uses an iterative tool-call pattern: + +1. Send messages to the LLM. +2. If the response contains `tool_calls`, execute each tool. +3. Append tool results to the message history. +4. Repeat until no tool calls remain or `maxToolRounds` is reached. + +Both `processUserMessage` (non-streaming) and `processUserMessageStream` (async generator) implement this loop. + +## Tool Interface + +All tools return a `ToolResult`: + +```typescript +interface ToolResult { + success: boolean; + output?: string; + error?: string; + data?: any; +} +``` + +Tools should never throw — wrap errors in `{ success: false, error: "..." }`. + +## Built-in Tools + +| Tool | File | Purpose | +|------|------|---------| +| `TextEditorTool` | `src/tools/text-editor.ts` | View, create, and str_replace files | +| `MorphEditorTool` | `src/tools/morph-editor.ts` | Fast Apply editing (requires MORPH_API_KEY) | +| `BashTool` | `src/tools/bash.ts` | Execute shell commands | +| `SearchTool` | `src/tools/search.ts` | Unified text/file search (uses ripgrep) | +| `TodoTool` | `src/tools/todo-tool.ts` | Create/update visual todo lists | +| `ConfirmationTool` | `src/tools/confirmation-tool.ts` | User confirmation for operations | + +## Adding a New Tool + +1. Create a new file in `src/tools/` implementing the tool class. +2. Export it from `src/tools/index.ts`. +3. Register the tool schema in `src/grok/tools.ts` (add to `GROK_TOOLS` array). +4. Add the execution case in `GrokAgent.executeTool()`. +5. Update the system prompt in the `GrokAgent` constructor to document the tool. + +## MCP Integration + +- MCP tools are prefixed with `mcp__` and dispatched through `executeMCPTool`. +- MCP config is loaded from `.grok/settings.json` under `mcpServers`. +- Supported transports: stdio, HTTP, SSE (defined in `src/mcp/transports.ts`). diff --git a/.cursor/rules/typescript-conventions.mdc b/.cursor/rules/typescript-conventions.mdc new file mode 100644 index 0000000..ad2199c --- /dev/null +++ b/.cursor/rules/typescript-conventions.mdc @@ -0,0 +1,54 @@ +--- +description: TypeScript coding conventions and style guidelines +globs: "**/*.ts,**/*.tsx" +alwaysApply: false +--- + +# TypeScript Conventions + +## Module System + +- This is an ESM project (`"type": "module"` in package.json). +- Always use ES module `import`/`export` syntax — never `require()`. +- When importing local modules, include the `.js` extension (TypeScript compiles `.ts` → `.js`): + ```typescript + import { GrokAgent } from "./agent/grok-agent.js"; + ``` + +## TypeScript Configuration + +- Target: ES2022, Module: ESNext, JSX: react +- `strict: false` and `noImplicitAny: false` — the codebase does not enforce strict mode. +- `moduleResolution: "Bundler"` is used. +- Source files live in `src/`, compiled output goes to `dist/`. + +## Type Practices + +- Prefer explicit interfaces for public API boundaries (e.g. `ChatEntry`, `ToolResult`, `GrokToolCall`). +- Use `type` imports when importing only types: + ```typescript + import type { ChatCompletionMessageParam } from "openai/resources/chat"; + ``` +- `any` is acceptable where strict typing would add friction, but prefer narrower types when practical. +- Shared types belong in `src/types/index.ts`. + +## Naming Conventions + +- **Files**: kebab-case (`grok-agent.ts`, `chat-interface.tsx`, `settings-manager.ts`). +- **Classes**: PascalCase (`GrokAgent`, `TextEditorTool`, `BashTool`). +- **Interfaces/Types**: PascalCase (`ChatEntry`, `ToolResult`, `StreamingChunk`). +- **Functions/variables**: camelCase (`processUserMessage`, `loadApiKey`). +- **Constants**: camelCase or UPPER_SNAKE_CASE for true constants (`GROK_TOOLS`). + +## Error Handling + +- Catch errors as `error: any` and access `.message` for display. +- Tool execution methods return `ToolResult` objects (`{ success, output?, error? }`) rather than throwing. +- Use `process.exit(1)` for fatal CLI errors. + +## ESLint Rules + +- `@typescript-eslint/no-unused-vars`: error +- `@typescript-eslint/no-explicit-any`: warn + +Run `bun run lint` to check and `bun run typecheck` to verify types. diff --git a/.gitignore b/.gitignore index 5e4daa0..2bb579e 100644 --- a/.gitignore +++ b/.gitignore @@ -99,4 +99,7 @@ temp/ # Coding agents .claude/ -.grok/ \ No newline at end of file +.grok/ + +# Cursor config (tracked — do not ignore) +# .cursor/ \ No newline at end of file