Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

### Overview

Grok CLI (`@vibe-kit/grok-cli`) is a single-package TypeScript CLI tool — no databases, Docker, or background services. See `README.md` for full documentation and usage.
grok-kev is a personal fork of Grok CLI — a single-package TypeScript CLI tool — no databases, Docker, or background services. See `README.md` for full documentation and usage.

### Quick reference

Expand All @@ -14,15 +14,15 @@ Grok CLI (`@vibe-kit/grok-cli`) is a single-package TypeScript CLI tool — no d
| Install deps | `bun install` (installs Husky; pre-commit runs Biome on staged files) |
| Typecheck | `bun run typecheck` |
| Build | `bun run build` |
| Run built CLI | `node dist/index.js` |
| Headless mode | `node dist/index.js --prompt "..." --max-tool-rounds N` |
| CLI help | `node dist/index.js --help` |
| Run CLI | `bun bin/grok.js` (auto-rebuilds stale `dist/`) |
| Headless mode | `bun bin/grok.js --prompt "..." --max-tool-rounds N` |
| CLI help | `bun bin/grok.js --help` |


### Known issues

- **ESLint config is broken**: The repo has `.eslintrc.js` (legacy format) but uses ESLint 9 (`^9.31.0`) + `@typescript-eslint` v8, which require flat config (`eslint.config.js`). Additionally, `.eslintrc.js` uses `module.exports` (CJS) but `package.json` has `"type": "module"` (ESM). Running `bun run lint` will fail. Use `bun run typecheck` as the primary code quality check (this is also what CI enforces).
- **Dev mode (`bun run dev` / `bun run dev:node`) fails at runtime**: `src/utils/model-config.ts` imports TypeScript interfaces (`UserSettings`, `ProjectSettings`) as value imports from `settings-manager.ts`. These type-only exports are erased at runtime by Bun and tsx, causing `SyntaxError: export '...' not found`. The fix is to use `import type` syntax, but this is a pre-existing repo issue. **Workaround**: build first (`bun run build`), then run the compiled version (`node dist/index.js`).
- **Dev mode (`bun run dev` / `bun run dev:node`) fails at runtime**: `src/utils/model-config.ts` imports TypeScript interfaces (`UserSettings`, `ProjectSettings`) as value imports from `settings-manager.ts`. These type-only exports are erased at runtime by Bun and tsx, causing `SyntaxError: export '...' not found`. The fix is to use `import type` syntax, but this is a pre-existing repo issue. **Workaround**: run `bun bin/grok.js` (or `grok` / `grok-kev`), which rebuilds `dist/` when source is newer.

### Environment

Expand Down
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added
- Dedicated grep tool powered by npm ripgrep WASM (#263)
- `/btw` command for side questions (#264)
- `/resume` command to switch to a saved workspace session from the TUI
- Splash screen shows upstream updates; `ctrl+u` / `/update` installs them

### Changed
- Renamed the `grok-dev` CLI command to `grok-kev`
- Branded this checkout as `grok-kev`
- `grok` / `grok-kev` rebuild a source checkout automatically when `dist/` is stale
- Switched Telegram voice/audio transcription from whisper.cpp to Grok STT (`/v1/stt`); removed `whisper-cli`, `ffmpeg`, and model-download requirements (#266, #265)
- Install script warns when auto-resolving to a pre-release version (#269)
- Release workflow publishes Sigstore build-provenance attestations (#271)
Expand Down
9 changes: 8 additions & 1 deletion LICENSE
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,11 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
SOFTWARE.

---

This repository includes a port of pi-prompt-suggester
(https://github.com/guwidoe/pi-prompt-suggester),
Copyright (c) 2026 Guido Witt-Dörring, licensed under the MIT License.
See NOTICE.md.
35 changes: 35 additions & 0 deletions NOTICE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Third-party notices

## pi-prompt-suggester

The next-prompt suggester plugin in `src/suggester/` is a Grok CLI port of
[pi-prompt-suggester](https://github.com/guwidoe/pi-prompt-suggester)
by **Guido Witt-Dörring**.

Copyright (c) 2026 Guido Witt-Dörring

Licensed under the MIT License. A copy of that license is included below.
The original design, two-stage seed + suggestion pipeline, steering
classifications, and several prompt templates were adapted from that
project. This port reimplements the product for Grok CLI / OpenTUI; it
is not a drop-in of the Pi extension.

### MIT License (pi-prompt-suggester)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN ANY ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
71 changes: 64 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# grok-cli: an open-source coding agent for the Grok API
# grok-kev

Personal fork of [superagent-ai/grok-cli](https://github.com/superagent-ai/grok-cli).
The local command is `grok-kev`. Stock `grok` / `grok-dev` stay untouched.

[![CI](https://github.com/superagent-ai/grok-cli/actions/workflows/typecheck.yml/badge.svg)](https://github.com/superagent-ai/grok-cli/actions/workflows/typecheck.yml)
[![npm](https://img.shields.io/npm/v/grok-dev.svg)](https://www.npmjs.com/package/grok-dev)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](./LICENSE)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.9-3178C6?logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
[![Bun](https://img.shields.io/badge/Bun-1.x-000000?logo=bun&logoColor=white)](https://bun.sh/)
Expand Down Expand Up @@ -44,7 +45,7 @@ grok uninstall --keep-config
**Interactive (default)** — launches the OpenTUI coding agent:

```bash
grok
grok-kev
```

### Supported terminals
Expand Down Expand Up @@ -86,7 +87,9 @@ grok --session latest
grok -s <session-id>
```

Works in interactive mode too—same flag.
Works in interactive mode too—same flag. Inside the TUI, `/resume` opens a
searchable picker of this workspace's sessions. `/resume latest` and
`/resume <session-id>` switch immediately.

**Structured headless output:**

Expand Down Expand Up @@ -142,6 +145,44 @@ Use `/schedule` in the TUI to browse saved schedules. One-time schedules start
immediately in the background; recurring schedules keep running as long as the
daemon is active.

**Next-prompt suggestions** — bundled plugin, off until you install it:

```text
/install suggester
```

After each interactive turn, the TUI may ghost a likely next user prompt
into the empty editor. It also keeps a background project-intent seed and
learns if you accept or rewrite suggestions. `Esc` clears an untouched fill.
It never auto-sends. `/suggester reseed` refreshes the seed;
`/suggester instruction set ...` adds a preference; `/suggester ghost` goes
back to dim placeholder + `Space`. `/uninstall suggester` removes it.
Headless `--prompt` mode is unchanged.

The suggester is a complete Grok CLI port of
[pi-prompt-suggester](https://github.com/guwidoe/pi-prompt-suggester)
by **Guido Witt-Dörring** (MIT). See [`NOTICE.md`](./NOTICE.md).

**GitHub plugins** — `/install owner/repo[@ref]` fetches `plugin.json` plus a
relative `.js` entry from GitHub and loads it into the current TUI session.
HTTPS GitHub only. The repo needs:

```json
{
"id": "weather",
"name": "Weather",
"description": "Look up the weather",
"version": "1.0.0",
"entry": "index.js",
"commands": ["weather"]
}
```

`index.js` must export `createPlugin()` and return a plugin object with `id`
and optional `handleCommand`. Files land in `~/.grok/plugins/`.
`/uninstall weather` removes them. This is an in-process JS loader, not a
sandbox. Only install repos you trust.

**List Grok models and pricing hints:**

```bash
Expand Down Expand Up @@ -181,6 +222,7 @@ You keep using a text model for the session, and Grok saves generated media unde
| **Computer use** | Built-in `**computer`** sub-agent for host desktop automation via `**agent-desktop`**. It prefers semantic accessibility snapshots and stable refs, with screenshots saved under `**.grok/computer/**` when requested. |
| **Custom sub-agents** | Define named agents with `**subAgents`** in `**~/.grok/user-settings.json`** and manage them from the TUI with `**/agents**`. |
| **Remote control** | Pair **Telegram** from the TUI (`/remote-control` → Telegram): DM your bot, `**/pair`**, approve the code in-terminal. Keep the CLI running while you ping it from your phone. |
| **Next-prompt suggestions** | Bundled plugin. `/install suggester`, then the TUI ghosts a likely next prompt after each turn. `/suggester` for status/reseed/instruction. Never auto-sends. |
| **No “mystery meat” UI** | OpenTUI React terminal UI—fast, keyboard-driven, not whatever glitchy thing you’re thinking of. |
| **Skills** | Agent Skills under `**.agents/skills/<name>/SKILL.md`** (project) or `**~/.agents/skills/`** (user). Use `**/skills**` in the TUI to list what’s installed. |
| **MCPs** | Extend with Model Context Protocol servers—configure via `**/mcps`** in the TUI or `**.grok/settings.json`** (`mcpServers`). |
Expand Down Expand Up @@ -221,6 +263,19 @@ grok -k your_key_here
{ "apiKey": "your_key_here" }
```

Optional `**suggester**` — next-prompt ghosts after `/install suggester`.
`enabled` still defaults to `true` once the plugin is installed:

```json
{
"suggester": {
"enabled": true,
"model": "grok-3-mini",
"maxSuggestionChars": 200
}
}
```

Optional `**subAgents**` — custom foreground sub-agents. Each entry needs `**name**`, `**model**`, and `**instruction**`:

```json
Expand Down Expand Up @@ -470,14 +525,16 @@ From a clone:

```bash
bun install
bun run build
bun run start
# or: node dist/index.js
# or: grok-kev / grok after bun link
```

`grok` and `grok-kev` rebuild `dist/` automatically when source files are newer. Skip that with `GROK_SKIP_REBUILD=1`.

Other useful commands:

```bash
bun run build
bun run dev # run from source (Bun)
bun run typecheck
bun run lint
Expand Down
13 changes: 13 additions & 0 deletions bin/ensure-built.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export function isSourceCheckout(root: string): boolean;

export function findLocalTsc(root: string): string | null;

export function shouldRebuild(root: string, options?: { env?: NodeJS.ProcessEnv }): { needed: boolean; reason: string };

export function ensureBuilt(
root: string,
options?: {
env?: NodeJS.ProcessEnv;
runBuild?: (root: string) => { status?: number | null };
},
): { rebuilt: boolean; reason: string; status: number };
106 changes: 106 additions & 0 deletions bin/ensure-built.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { spawnSync } from "child_process";
import fs from "fs";
import path from "path";

const SOURCE_EXTENSIONS = new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".json"]);
const TEST_SUFFIXES = [".test.ts", ".test.tsx", ".spec.ts", ".spec.tsx", ".test.js", ".spec.js"];

export function isSourceCheckout(root) {
return fs.existsSync(path.join(root, "src", "index.ts")) && fs.existsSync(path.join(root, "tsconfig.json"));
}

export function findLocalTsc(root) {
const candidate = path.join(root, "node_modules", "typescript", "bin", "tsc");
return fs.existsSync(candidate) ? candidate : null;
}

export function shouldRebuild(root, options = {}) {
const env = options.env ?? process.env;
if (env.GROK_SKIP_REBUILD) {
return { needed: false, reason: "GROK_SKIP_REBUILD is set" };
}
if (!isSourceCheckout(root)) {
return { needed: false, reason: "not a source checkout" };
}

const distIndex = path.join(root, "dist", "index.js");
if (!fs.existsSync(distIndex)) {
return { needed: true, reason: "dist/index.js is missing" };
}

const distMtime = fs.statSync(distIndex).mtimeMs;
const watched = [
path.join(root, "package.json"),
path.join(root, "tsconfig.json"),
...listSourceFiles(path.join(root, "src")),
];

for (const file of watched) {
if (!fs.existsSync(file)) continue;
if (fs.statSync(file).mtimeMs > distMtime) {
return { needed: true, reason: `${toPosix(path.relative(root, file))} is newer than dist` };
}
}

return { needed: false, reason: "dist is up to date" };
}

export function ensureBuilt(root, options = {}) {
const decision = shouldRebuild(root, options);
if (!decision.needed) {
return { rebuilt: false, reason: decision.reason, status: 0 };
}

const runBuild = options.runBuild ?? defaultRunBuild;
const result = runBuild(root);
const status = typeof result?.status === "number" ? result.status : 1;
if (status === 0) {
return { rebuilt: true, reason: decision.reason, status: 0 };
}

const hasDist = fs.existsSync(path.join(root, "dist", "index.js"));
return {
rebuilt: false,
reason: decision.reason,
status: hasDist ? 0 : status,
};
}

function defaultRunBuild(root) {
const tsc = findLocalTsc(root);
if (!tsc) {
console.error("Cannot rebuild: TypeScript is not installed. Run `bun install` first.");
return { status: 1 };
}

return spawnSync(process.execPath, [tsc, "-p", "tsconfig.json"], {
cwd: root,
stdio: "inherit",
});
}

function listSourceFiles(dir, files = []) {
if (!fs.existsSync(dir)) return files;

for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
if (entry.name === "node_modules" || entry.name === "dist") continue;

const full = path.join(dir, entry.name);
if (entry.isDirectory()) {
listSourceFiles(full, files);
continue;
}
if (isTestFile(entry.name)) continue;
if (SOURCE_EXTENSIONS.has(path.extname(entry.name))) files.push(full);
}

return files;
}

function isTestFile(name) {
return TEST_SUFFIXES.some((suffix) => name.endsWith(suffix));
}

function toPosix(filePath) {
return filePath.split(path.sep).join("/");
}
19 changes: 19 additions & 0 deletions bin/grok.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#!/usr/bin/env bun
import path from "path";
import { fileURLToPath, pathToFileURL } from "url";
import { ensureBuilt } from "./ensure-built.js";

const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
const result = ensureBuilt(root);

if (result.status !== 0) {
process.exit(result.status);
}

if (result.rebuilt) {
process.stderr.write(`Rebuilt CLI (${result.reason}).\n`);
} else if (result.reason.includes("newer than dist")) {
process.stderr.write(`Using last good dist (${result.reason}; rebuild failed).\n`);
}

await import(pathToFileURL(path.join(root, "dist", "index.js")).href);
9 changes: 5 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "grok-dev",
"name": "grok-kev",
"version": "1.1.7",
"description": "An open-source AI coding agent powered by Grok, built with Bun and OpenTUI.",
"description": "Personal grok-kev fork of the Grok CLI coding agent.",
"type": "module",
"main": "dist/index.js",
"exports": {
Expand All @@ -11,13 +11,14 @@
}
},
"bin": {
"grok": "dist/index.js"
"grok": "bin/grok.js",
"grok-kev": "bin/grok.js"
},
"scripts": {
"dev": "bun run src/index.ts",
"build": "tsc",
"build:binary": "bun build --compile --outfile dist/grok-standalone ./src/index.ts",
"start": "bun run dist/index.js",
"start": "bun bin/grok.js",
"typecheck": "tsc --noEmit",
"test": "bunx vitest run",
"test:watch": "bunx vitest",
Expand Down
Loading