Skip to content

Commit 04ef519

Browse files
committed
feat(grok-build): cli ACP stdio integration
1 parent 964675c commit 04ef519

37 files changed

Lines changed: 4762 additions & 186 deletions

Makefile

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,23 @@
1-
.PHONY: test lint format
1+
.PHONY: test test-acp test-acp-smoke lint format
22

33
TEST_INIT := tests/minimal_init.lua
44
TEST_DIR := tests/
55
PLUGIN_DIR := lua/
66

7+
# Full suite. For classic-only evidence we filter acp lines in the verify script.
78
test:
89
nvim --headless -u tests/minimal_init.lua -c "PlenaryBustedDirectory tests/ {minimal_init = 'tests/minimal_init.lua', sequential = true}"
910

11+
# Fast offline ACP unit tests only (no grok agent stdio, ~few seconds).
12+
test-acp:
13+
nvim --headless -u tests/minimal_init.lua \
14+
-c "PlenaryBustedDirectory tests/parrot/acp {minimal_init = 'tests/minimal_init.lua'}" \
15+
-c "lua require('plenary.busted').run('tests/parrot/provider/acp_provider_spec.lua')" \
16+
-c "qa!"
17+
18+
test-acp-smoke:
19+
nvim --headless -u tests/minimal_init.lua --cmd "set rtp+=." -l tests/acp_smoke.lua
20+
1021
lint:
1122
luacheck ${PLUGIN_DIR}
1223

README.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ A substantial part of the code is based on an early fork of the brilliant work b
2828
+ [Anthropic API](https://www.anthropic.com/api)
2929
+ [Google Gemini API](https://ai.google.dev/gemini-api/docs)
3030
+ [xAI API](https://console.x.ai)
31+
+ [Grok Build CLI](https://docs.x.ai/build/overview) via [ACP](https://agentclientprotocol.com/get-started/introduction) (`grok agent stdio`)
3132
+ Local and offline serving via [ollama](https://github.com/ollama/ollama)
3233
+ Any custom OpenAI-compatible endpoint with configurable functions; also supports [Perplexity.ai API](https://blog.perplexity.ai/blog/introducing-pplx-api), [Mistral API](https://docs.mistral.ai/api/), [Groq API](https://console.groq.com), [DeepSeek API](https://platform.deepseek.com), [GitHub Models](https://github.com/marketplace/models), [NVIDIA API](https://docs.api.nvidia.com), and [EveryAPI](https://everyapi.ai) (multi-provider gateway · 240+ models behind one key)
3334
- Flexible API credential management from various sources:
@@ -615,6 +616,85 @@ providers = {
615616
```
616617
</details>
617618

619+
<details>
620+
<summary>Grok Build (ACP)</summary>
621+
622+
Use the [Grok Build CLI](https://docs.x.ai/build/overview) as a provider through the
623+
[Agent Client Protocol](https://agentclientprotocol.com/get-started/introduction).
624+
This routes chat, inline edits (`PrtRewrite`, etc.), and slash commands through
625+
`grok agent stdio` instead of direct HTTP API calls. Models are listed via
626+
`grok models` and `:PrtModel`.
627+
628+
Install the CLI from https://x.ai/cli, then authenticate with `grok login` or
629+
`XAI_API_KEY`.
630+
631+
```lua
632+
providers = {
633+
grok = {
634+
type = "acp",
635+
name = "grok",
636+
command = { "grok", "agent", "stdio" },
637+
cli_command = { "grok" },
638+
models = { "grok-composer-2.5-fast", "grok-build" },
639+
always_approve = false, -- set true to skip ACP permission prompts
640+
resume_session = true, -- resume the last Grok ACP session for this git repo (default)
641+
},
642+
}
643+
```
644+
645+
#### How sessions fit into the parrot workflow
646+
647+
Parrot and Grok maintain **two different kinds of state**:
648+
649+
| Layer | What it stores | Where |
650+
|-------|----------------|-------|
651+
| **Parrot transcript** | Markdown chat/command buffers you see in Neovim | `chat_dir` (e.g. `~/.local/share/nvim/parrot/chats`) |
652+
| **Grok agent state** | Tool history, goals, slash-command context, permission mode | `~/.grok/sessions` on disk; session IDs in parrot `state.json` |
653+
654+
They are related but not identical. Editing or deleting a parrot `.md` chat file does **not** reset the Grok session, and resuming a Grok session does **not** replay old turns into the buffer.
655+
656+
**Scope:** Session IDs are keyed by **git repository root** (fallback: Neovim `cwd`). Any file under `chat_dir` is an ACP **chat** session (path-based); parrot chat keymaps (`PrtChatRespond`, etc.) still require the full markdown template (`utils.is_chat`). Command sessions use the buffer file's directory / git root at prompt time.
657+
658+
**Chat project binding** (`chat_project_cwd` in `state.json`): set at `:PrtChatNew` / first `BufEnter` on a chat file. Legacy chats created before this feature bind on the **first ACP prompt** after upgrade, using whatever Neovim `cwd` is then — `:cd` to the intended repo before that prompt. Once bound, prompts, slash commands, and mode changes keep that project even if you `:cd` elsewhere mid-chat.
659+
660+
**Kinds:** `chat` and `command` use **separate session IDs** per repo (`PrtChatNew` / chat buffers vs `PrtAsk`, `PrtRewrite`, popups, etc.).
661+
662+
**Lifecycle:**
663+
664+
1. Neovim starts → parrot spawns `grok agent stdio` (one process per model/connection).
665+
2. First prompt for a kind → `initialize``authenticate``session/load` (if `resume_session = true` and a stored ID exists) or `session/new`.
666+
3. Prompts stream via `session/prompt` / `session/update` (`agent_message_chunk`).
667+
4. Neovim exits → parrot terminates ACP processes (`VimLeavePre`).
668+
669+
Set `resume_session = false` to always call `session/new` (fresh Grok context; parrot still keeps markdown transcripts).
670+
671+
**Recommended workflows**
672+
673+
- **Long-running agent work in a repo:** `resume_session = true`, chat with `grok-build`, set a goal via `:PrtAcpSlashCommand goal`, continue across Neovim restarts. Parrot markdown is your readable log; Grok session is the agent's working memory.
674+
- **One-off edits / popups:** Command sessions (`PrtRewrite`, `PrtAsk`) stay isolated from chat. Same repo, different session kind.
675+
- **Switch repos:** `:cd` to the other project before starting a **new** chat or command prompt. Existing chat files keep their original project binding; reopening an old chat resumes that project's Grok session.
676+
- **Reset agent context without losing transcripts:** `resume_session = false` temporarily, or delete the repo's entries under `state.json``acp_sessions`, or start a new Grok session from the CLI and update the stored ID.
677+
- **After `grok` CLI upgrade:** `:PrtReloadCache grok` refreshes models and slash-command cache (keyed by `grok --version`).
678+
679+
**Background agent:** Plugin setup, Tab completion on incomplete slash caches, and `:PrtReloadCache` may spawn `grok agent stdio` in the background (with a 15s warm timeout). Use `make test-acp` / `make test-acp-smoke` for offline verification without a live agent.
680+
681+
**Slash commands:** The full list arrives after `session/new` or `session/load` via `available_commands_update` (not from `initialize` alone). Parrot caches commands in `state.json` for instant Tab completion; incomplete caches refresh in the background. `:PrtAcpSlashCommand` with no args opens a picker; Tab completes `name` + description.
682+
683+
**Transcript vs agent desync:** If you `:PrtChatNew` (new markdown file) but `resume_session = true`, Grok still remembers prior tool runs. For a clean agent slate, use `resume_session = false` or clear the repo's `acp_sessions` entry. Conversely, an old markdown chat file can be reopened while Grok resumes — the buffer shows history Grok may not re-ingest unless you paste or summarize it in a new prompt.
684+
685+
**ACP-specific commands** (registered automatically):
686+
687+
- `:PrtAcpSlashCommand [cmd]` — run Grok slash commands (`/compact`, `/context`, `/goal`, …)
688+
- `:PrtAcpMode [mode]` — session mode picker; for Grok, toggles permission behavior (`always-approve-on` / `always-approve-off`)
689+
- `:PrtReloadCache [provider]` — refresh model and slash-command caches (use after CLI updates)
690+
691+
**Other options:**
692+
693+
- `always_approve = true` — skip ACP permission prompts (same as `/always-approve on`)
694+
- `show_thoughts = true` — include `agent_thought_chunk` streams in the response (when the agent emits them)
695+
696+
</details>
697+
618698
<details>
619699
<summary>Ollama</summary>
620700

0 commit comments

Comments
 (0)