feat: add Cursor ACP support (#98)

* feat: add Cursor ACP support

* docs: enforce example agent ordering

* docs: normalize built-in agent ordering

* docs: strengthen ordering policy in AGENTS

* docs: format AGENTS ordering policy

* docs: reorganize built-in agent docs

* docs: restore AGENTS policy wording
This commit is contained in:
Onur Solmaz 2026-03-10 08:36:03 +01:00 committed by GitHub
parent 6d291e8ac7
commit 9141b63a4c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 256 additions and 114 deletions

View File

@ -81,6 +81,31 @@ npx acpx@latest --help
3. Run the smallest relevant validation command while iterating.
4. Before opening or updating a PR, run the full checks for the scope you changed.
## Documentation Policy
Example ordering policy:
1. `pi`
2. `openclaw`
3. `codex`
4. `claude`
5. `gemini`
6. `cursor`
7. `copilot`
This ordering is mandatory whenever multiple built-in agents appear in the same example set. Agents after those may appear in any order, but the precedence above MUST NOT be broken. Any PR that introduces or preserves example ordering that violates this rule MUST be modified until it adheres to this ordering before merge.
Main landing documentation policy:
1. This repo will receive many contributions. Contributors will sometimes try, intentionally or unintentionally, to promote their own harness or product through the docs.
2. Main landing docs such as `README.md` and `docs/CLI.md` MUST remain impartial. They MUST NOT become promotional surfaces for specific harnesses.
3. `pi` and `openclaw` are the primary citizens. They may appear at the top of main landing docs, in that order.
4. `codex` and `claude` are the next most important citizens because they are the most widely used. These four harnesses — `pi`, `openclaw`, `codex`, and `claude` — are the only harnesses that may be used as named examples in main landing docs, and the only ones whose specific quirks or harness-specific details may be called out there.
5. The only main-landing exceptions are the neutral built-in agents table in `README.md` and the neutral built-in agents list in `agents/README.md`. Those lists MAY include every supported built-in harness, but they MUST remain exhaustive, factual, and non-promotional. They MUST NOT single out non-primary harnesses for extra emphasis.
6. Harness-specific docs for other supported agents MUST live under `agents/` and MUST use capitalized filenames, for example `agents/Cursor.md` and `agents/Copilot.md`.
7. No other specific harness MUST BE ALLOWED to receive special placement, singled-out examples, or harness-specific promotion in main landing docs. This rule applies even when the change is framed as harmless, helpful, or accidental.
8. Other harnesses may still be supported elsewhere in the repo, but main landing docs must describe them impartially and MUST NOT promote them unjustly.
## Common Commands
- `pnpm run build` — build the distributable CLI

View File

@ -28,7 +28,7 @@ For the current maintainer list, check the original OpenClaw contributing guide:
- Run tests: `pnpm build && pnpm check && pnpm test:coverage`
- Ensure CI checks pass
- Keep PRs focused (one thing per PR; do not mix unrelated concerns)
- Keep adapter names, adapter lists, and adapter registry examples in A-Z order where practical
- Keep built-in agent docs, examples, and links consistent with the existing docs structure.
- Describe what & why
- Reply to or resolve bot review conversations you addressed before asking for review again
- **Include screenshots** — one showing the problem/before, one showing the fix/after (for UI or visual changes)

View File

@ -18,7 +18,7 @@ Your agents love acpx! 🤖❤️ They hate having to scrape characters from a P
`acpx` is a headless CLI client for the [Agent Client Protocol (ACP)](https://agentclientprotocol.com), so AI agents and orchestrators can talk to coding agents over a structured protocol instead of PTY scraping.
One command surface for Codex, Claude, Gemini, OpenClaw ACP, OpenCode, Pi, Kilocode, or custom ACP servers. Built for agent-to-agent communication over the command line.
One command surface for Pi, OpenClaw ACP, Codex, Claude, and other ACP-compatible agents. Built for agent-to-agent communication over the command line.
- **Persistent sessions**: multi-turn conversations that survive across invocations, scoped per repo
- **Named sessions**: run parallel workstreams in the same repo (`-s backend`, `-s frontend`)
@ -71,7 +71,7 @@ reading stale state from the previous run.
## Quick setup — tell your agent about acpx
Copy the block below and paste it into your OpenClaw, Pi, Claude Code, or similar agent harness. It will install acpx, read the skill reference, and know how to use ACP for all future coding agent tasks.
Copy the block below and paste it into your Pi, OpenClaw, Claude Code, or similar agent harness. It will install acpx, read the skill reference, and know how to use ACP for all future coding agent tasks.
```text
I want you to use acpx to run coding agents over the Agent Client Protocol
@ -120,17 +120,12 @@ Session state lives in `~/.acpx/` either way. Global install is a little faster,
The only prerequisite is the underlying coding agent you want to use:
- `acpx copilot` -> GitHub Copilot CLI (`copilot --acp --stdio`, requires a release that supports ACP stdio mode): https://docs.github.com/copilot/how-tos/copilot-chat/use-copilot-chat-in-the-command-line
- `acpx pi` -> Pi Coding Agent: https://github.com/mariozechner/pi
- `acpx openclaw` -> OpenClaw ACP bridge: https://github.com/openclaw/openclaw
- `acpx codex` -> Codex CLI: https://codex.openai.com
- `acpx claude` -> Claude Code: https://claude.ai/code
- `acpx gemini` -> Gemini CLI: https://github.com/google/gemini-cli
- `acpx kimi` -> Kimi CLI: https://github.com/MoonshotAI/kimi-cli
- `acpx openclaw` -> OpenClaw ACP bridge: https://github.com/openclaw/openclaw
- `acpx opencode` -> OpenCode: https://opencode.ai
- `acpx kiro` -> Kiro CLI: https://kiro.dev
- `acpx pi` -> Pi Coding Agent: https://github.com/mariozechner/pi
- `acpx kilocode` -> Kilocode: https://kilocode.ai
- `acpx qwen` -> Qwen Code: https://github.com/QwenLM/qwen-code
Additional built-in agent docs live in [agents/README.md](agents/README.md).
## Usage examples
@ -149,7 +144,7 @@ acpx exec 'summarize this repo' # default agent shortcut (codex)
acpx codex exec 'what does this repo do?' # one-shot, no saved session
acpx codex sessions new --name api # create named session
acpx codex -s api 'implement cursor pagination' # prompt in named session
acpx codex -s api 'implement token pagination' # prompt in named session
acpx codex sessions new --name docs # create another named session
acpx codex -s docs 'rewrite API docs' # parallel work in another named session
@ -167,14 +162,20 @@ acpx codex status # local process status for current session
acpx config show # show resolved config (global + project)
acpx config init # create ~/.acpx/config.json template
```
acpx copilot 'summarize recent changes' # built-in GitHub Copilot agent
acpx claude 'refactor auth middleware' # built-in claude agent
acpx gemini 'add startup logging' # built-in gemini agent
Main landing harness examples:
```bash
acpx pi 'review recent changes'
acpx openclaw exec 'summarize active session state' # built-in OpenClaw ACP bridge
acpx qwen 'explain this module architecture' # built-in qwen code agent
acpx qwen exec 'Reply exactly QWEN_ACP_OK' # one-shot ACP smoke test
acpx codex 'fix the failing typecheck'
acpx claude 'refactor auth middleware' # built-in claude agent
```
Additional supported harnesses and their specific notes are documented in [agents/README.md](agents/README.md).
```bash
acpx my-agent 'review this patch' # unknown name -> raw command
acpx --agent './bin/dev-acp --profile ci' 'run checks' # --agent escape hatch
```
@ -280,25 +281,27 @@ Built-ins:
| Agent | Adapter | Wraps |
| ---------- | ---------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `copilot` | native | [GitHub Copilot CLI](https://docs.github.com/copilot/how-tos/copilot-chat/use-copilot-chat-in-the-command-line) |
| `pi` | [pi-acp](https://github.com/svkozak/pi-acp) | [Pi Coding Agent](https://github.com/mariozechner/pi) |
| `openclaw` | native (`openclaw acp`) | [OpenClaw ACP bridge](https://github.com/openclaw/openclaw) |
| `codex` | [codex-acp](https://github.com/zed-industries/codex-acp) | [Codex CLI](https://codex.openai.com) |
| `claude` | [claude-agent-acp](https://github.com/zed-industries/claude-agent-acp) | [Claude Code](https://claude.ai/code) |
| `gemini` | native | [Gemini CLI](https://github.com/google/gemini-cli) |
| `kimi` | native | [Kimi CLI](https://github.com/MoonshotAI/kimi-cli) |
| `openclaw` | native | [OpenClaw ACP bridge](https://github.com/openclaw/openclaw) |
| `opencode` | native | [OpenCode](https://opencode.ai) |
| `pi` | [pi-acp](https://github.com/svkozak/pi-acp) | [Pi Coding Agent](https://github.com/mariozechner/pi) |
| `kilocode` | native | [Kilocode](https://kilocode.ai) |
| `gemini` | native (`gemini --experimental-acp`) | [Gemini CLI](https://github.com/google/gemini-cli) |
| `cursor` | native (`cursor-agent acp`) | [Cursor CLI](https://cursor.com/docs/cli/acp) |
| `copilot` | native (`copilot --acp --stdio`) | [GitHub Copilot CLI](https://docs.github.com/copilot/how-tos/copilot-chat/use-copilot-chat-in-the-command-line) |
| `kimi` | native (`kimi acp`) | [Kimi CLI](https://github.com/MoonshotAI/kimi-cli) |
| `opencode` | `npx -y opencode-ai acp` | [OpenCode](https://opencode.ai) |
| `kiro` | native (`kiro-cli acp`) | [Kiro CLI](https://kiro.dev) |
| `kilocode` | `npx -y @kilocode/cli acp` | [Kilocode](https://kilocode.ai) |
| `qwen` | native (`qwen --acp`) | [Qwen Code](https://github.com/QwenLM/qwen-code) |
Additional built-in agent docs live in [agents/README.md](agents/README.md).
Use `--agent` as an escape hatch for custom ACP servers:
```bash
acpx --agent ./my-custom-acp-server 'do something'
```
`acpx copilot` expects a Copilot CLI release with `--acp --stdio` support. Older binaries will fail before ACP startup.
For repo-local OpenClaw checkouts, override the built-in command in config so `acpx openclaw ...`
spawns the ACP bridge directly without `pnpm` wrapper noise:

7
agents/Copilot.md Normal file
View File

@ -0,0 +1,7 @@
# Copilot
- Built-in name: `copilot`
- Default command: `copilot --acp --stdio`
- Upstream: https://docs.github.com/copilot/how-tos/copilot-chat/use-copilot-chat-in-the-command-line
`acpx copilot` requires a GitHub Copilot CLI release that supports ACP stdio mode. Older `copilot` binaries fail before ACP startup.

17
agents/Cursor.md Normal file
View File

@ -0,0 +1,17 @@
# Cursor
- Built-in name: `cursor`
- Default command: `cursor-agent acp`
- Upstream: https://cursor.com/docs/cli/acp
If your Cursor install exposes ACP as `agent acp` instead of `cursor-agent acp`, override the built-in command in config:
```json
{
"agents": {
"cursor": {
"command": "agent acp"
}
}
}
```

5
agents/Gemini.md Normal file
View File

@ -0,0 +1,5 @@
# Gemini
- Built-in name: `gemini`
- Default command: `gemini --experimental-acp`
- Upstream: https://github.com/google/gemini-cli

5
agents/Kilocode.md Normal file
View File

@ -0,0 +1,5 @@
# Kilocode
- Built-in name: `kilocode`
- Default command: `npx -y @kilocode/cli acp`
- Upstream: https://kilocode.ai

5
agents/Kimi.md Normal file
View File

@ -0,0 +1,5 @@
# Kimi
- Built-in name: `kimi`
- Default command: `kimi acp`
- Upstream: https://github.com/MoonshotAI/kimi-cli

5
agents/Kiro.md Normal file
View File

@ -0,0 +1,5 @@
# Kiro
- Built-in name: `kiro`
- Default command: `kiro-cli acp`
- Upstream: https://kiro.dev

5
agents/OpenCode.md Normal file
View File

@ -0,0 +1,5 @@
# OpenCode
- Built-in name: `opencode`
- Default command: `npx -y opencode-ai acp`
- Upstream: https://opencode.ai

5
agents/Qwen.md Normal file
View File

@ -0,0 +1,5 @@
# Qwen
- Built-in name: `qwen`
- Default command: `qwen --acp`
- Upstream: https://github.com/QwenLM/qwen-code

27
agents/README.md Normal file
View File

@ -0,0 +1,27 @@
# Agent Docs
Built-in agents:
- `pi -> npx pi-acp`
- `openclaw -> openclaw acp`
- `codex -> npx @zed-industries/codex-acp`
- `claude -> npx -y @zed-industries/claude-agent-acp`
- `gemini -> gemini --experimental-acp`
- `cursor -> cursor-agent acp`
- `copilot -> copilot --acp --stdio`
- `kimi -> kimi acp`
- `opencode -> npx -y opencode-ai acp`
- `kiro -> kiro-cli acp`
- `kilocode -> npx -y @kilocode/cli acp`
- `qwen -> qwen --acp`
Harness-specific docs in this directory:
- [Gemini](Gemini.md): built-in `gemini -> gemini --experimental-acp`
- [Cursor](Cursor.md): built-in `cursor -> cursor-agent acp`
- [Copilot](Copilot.md): built-in `copilot -> copilot --acp --stdio`
- [Kimi](Kimi.md): built-in `kimi -> kimi acp`
- [OpenCode](OpenCode.md): built-in `opencode -> npx -y opencode-ai acp`
- [Kiro](Kiro.md): built-in `kiro -> kiro-cli acp`
- [Kilocode](Kilocode.md): built-in `kilocode -> npx -y @kilocode/cli acp`
- [Qwen](Qwen.md): built-in `qwen -> qwen --acp`

View File

@ -7,19 +7,14 @@ date: 2026-02-17
## Built-in registry
`src/agent-registry.ts` defines friendly names:
`src/agent-registry.ts` defines friendly names such as:
- `codex -> npx @zed-industries/codex-acp`
- `copilot -> copilot --acp --stdio`
- `claude -> npx -y @zed-industries/claude-agent-acp`
- `gemini -> gemini --experimental-acp`
- `openclaw -> openclaw acp`
- `kimi -> kimi acp`
- `opencode -> npx -y opencode-ai acp`
- `kiro -> kiro-cli acp`
- `pi -> npx pi-acp`
- `kilocode -> npx -y @kilocode/cli acp`
- `qwen -> qwen --acp`
- `openclaw -> openclaw acp`
- `codex -> npx @zed-industries/codex-acp`
- `claude -> npx -y @zed-industries/claude-agent-acp`
The built-in agents table lives in [../README.md](../README.md). Additional built-in agent docs live under [../agents/README.md](../agents/README.md).
Default agent is `codex`.
@ -51,7 +46,7 @@ Rules:
## Practical guidance
Use built-ins for common adapters (`copilot`, `codex`, `claude`, `gemini`, `openclaw`, `kimi`, `opencode`, `kiro`, `pi`, `kilocode`, `qwen`).
Use the built-ins documented in `src/agent-registry.ts`. For the full supported-agents list and additional built-in agent docs, see [../README.md](../README.md) and [../agents/README.md](../agents/README.md).
Use `--agent` when you need:
- local development adapters

View File

@ -272,7 +272,7 @@ Structured logs should always include:
For startup speed and determinism:
- prefer direct adapter binaries in config (for example `codex-acp`, `claude-agent-acp`, `opencode acp`)
- prefer direct adapter binaries in config (for example `pi-acp`, `codex-acp`, `claude-agent-acp`)
- keep `npx -y ...` only as fallback defaults
- recommend preinstall + prewarm for high-traffic adapters

View File

@ -42,10 +42,12 @@ acpx [global_options] <agent> sessions [list | new [--name <name>] | ensure [--n
`<agent>` can be:
- built-in friendly name: `codex`, `claude`, `copilot`, `gemini`, `openclaw`, `kimi`, `opencode`, `kiro`, `pi`, `kilocode`, `qwen`
- built-in friendly name from [../README.md](../README.md)
- unknown token (treated as raw command)
- overridden by `--agent <command>` escape hatch
Additional built-in agent docs live in [../agents/README.md](../agents/README.md).
Prompt options:
```bash
@ -101,51 +103,16 @@ acpx --verbose codex 'debug adapter startup issues'
Each agent command supports the same shape.
### `copilot`
### `pi`
```bash
acpx [global_options] copilot [prompt_options] [prompt_text...]
acpx [global_options] copilot prompt [prompt_options] [prompt_text...]
acpx [global_options] copilot exec [prompt_text...]
acpx [global_options] copilot sessions [list | new [--name <name>] | ensure [--name <name>] | close [name]]
acpx [global_options] pi [prompt_options] [prompt_text...]
acpx [global_options] pi prompt [prompt_options] [prompt_text...]
acpx [global_options] pi exec [prompt_text...]
acpx [global_options] pi sessions [list | new [--name <name>] | ensure [--name <name>] | close [name]]
```
Built-in command mapping: `copilot -> copilot --acp --stdio`
Requires a GitHub Copilot CLI release that supports ACP stdio mode. Older `copilot` binaries will fail before ACP startup.
### `codex`
```bash
acpx [global_options] codex [prompt_options] [prompt_text...]
acpx [global_options] codex prompt [prompt_options] [prompt_text...]
acpx [global_options] codex exec [prompt_text...]
acpx [global_options] codex sessions [list | new [--name <name>] | ensure [--name <name>] | close [name]]
```
Built-in command mapping: `codex -> npx @zed-industries/codex-acp`
### `claude`
```bash
acpx [global_options] claude [prompt_options] [prompt_text...]
acpx [global_options] claude prompt [prompt_options] [prompt_text...]
acpx [global_options] claude exec [prompt_text...]
acpx [global_options] claude sessions [list | new [--name <name>] | ensure [--name <name>] | close [name]]
```
Built-in command mapping: `claude -> npx -y @zed-industries/claude-agent-acp`
### `gemini`
```bash
acpx [global_options] gemini [prompt_options] [prompt_text...]
acpx [global_options] gemini prompt [prompt_options] [prompt_text...]
acpx [global_options] gemini exec [prompt_text...]
acpx [global_options] gemini sessions [list | new [--name <name>] | ensure [--name <name>] | close [name]]
```
Built-in command mapping: `gemini -> gemini --experimental-acp`
Built-in command mapping: `pi -> npx pi-acp`
### `openclaw`
@ -170,16 +137,29 @@ For repo-local OpenClaw checkouts, override the built-in command in config:
}
```
### `kimi`
### `codex`
```bash
acpx [global_options] kimi [prompt_options] [prompt_text...]
acpx [global_options] kimi prompt [prompt_options] [prompt_text...]
acpx [global_options] kimi exec [prompt_text...]
acpx [global_options] kimi sessions [list | new [--name <name>] | ensure [--name <name>] | close [name]]
acpx [global_options] codex [prompt_options] [prompt_text...]
acpx [global_options] codex prompt [prompt_options] [prompt_text...]
acpx [global_options] codex exec [prompt_text...]
acpx [global_options] codex sessions [list | new [--name <name>] | ensure [--name <name>] | close [name]]
```
Built-in command mapping: `kimi -> kimi acp`
Built-in command mapping: `codex -> npx @zed-industries/codex-acp`
### `claude`
```bash
acpx [global_options] claude [prompt_options] [prompt_text...]
acpx [global_options] claude prompt [prompt_options] [prompt_text...]
acpx [global_options] claude exec [prompt_text...]
acpx [global_options] claude sessions [list | new [--name <name>] | ensure [--name <name>] | close [name]]
```
Built-in command mapping: `claude -> npx -y @zed-industries/claude-agent-acp`
Additional built-in agent docs live in [../agents/README.md](../agents/README.md).
### Custom positional agents

View File

@ -1,13 +1,14 @@
export const AGENT_REGISTRY: Record<string, string> = {
copilot: "copilot --acp --stdio",
pi: "npx pi-acp",
openclaw: "openclaw acp",
codex: "npx @zed-industries/codex-acp",
claude: "npx -y @zed-industries/claude-agent-acp",
gemini: "gemini --experimental-acp",
openclaw: "openclaw acp",
cursor: "cursor-agent acp",
copilot: "copilot --acp --stdio",
kimi: "kimi acp",
opencode: "npx -y opencode-ai acp",
kiro: "kiro-cli acp",
pi: "npx pi-acp",
kilocode: "npx -y @kilocode/cli acp",
qwen: "qwen --acp",
};

View File

@ -1571,6 +1571,8 @@ export async function main(argv: string[] = process.argv): Promise<void> {
"after",
`
Examples:
acpx pi "review recent changes"
acpx openclaw exec "summarize active session state"
acpx codex sessions new
acpx codex "fix the tests"
acpx codex prompt "fix the tests"
@ -1589,7 +1591,6 @@ Examples:
acpx config init
acpx --ttl 30 codex "investigate flaky tests"
acpx claude "refactor auth"
acpx gemini "add logging"
acpx --agent ./my-custom-server "do something"`,
);

View File

@ -60,6 +60,8 @@ export function configurePublicCli(options: ConfigurePublicCliOptions): void {
"after",
`
Examples:
acpx pi "review recent changes"
acpx openclaw exec "summarize active session state"
acpx codex sessions new
acpx codex "fix the tests"
acpx codex prompt "fix the tests"
@ -78,7 +80,6 @@ Examples:
acpx config init
acpx --ttl 30 codex "investigate flaky tests"
acpx claude "refactor auth"
acpx gemini "add logging"
acpx --agent ./my-custom-server "do something"`,
);
}

View File

@ -8,15 +8,16 @@ import {
test("resolveAgentCommand maps known agents to commands", () => {
const expected = new Map<string, string>([
["copilot", "copilot --acp --stdio"],
["pi", "npx pi-acp"],
["openclaw", "openclaw acp"],
["codex", "npx @zed-industries/codex-acp"],
["claude", "npx -y @zed-industries/claude-agent-acp"],
["gemini", "gemini --experimental-acp"],
["openclaw", "openclaw acp"],
["cursor", "cursor-agent acp"],
["copilot", "copilot --acp --stdio"],
["kimi", "kimi acp"],
["opencode", "npx -y opencode-ai acp"],
["kiro", "kiro-cli acp"],
["pi", "npx pi-acp"],
["kilocode", "npx -y @kilocode/cli acp"],
["qwen", "qwen --acp"],
]);
@ -30,25 +31,22 @@ test("resolveAgentCommand returns raw value for unknown agents", () => {
assert.equal(resolveAgentCommand("custom-acp-server"), "custom-acp-server");
});
test("listBuiltInAgents returns exactly all 11 registered agent names", () => {
test("listBuiltInAgents preserves the required built-in example order", () => {
const agents = listBuiltInAgents();
assert.equal(agents.length, 11);
assert.deepEqual(
new Set(agents),
new Set([
"copilot",
"codex",
"claude",
"gemini",
"openclaw",
"kimi",
"opencode",
"kiro",
"pi",
"kilocode",
"qwen",
]),
);
assert.deepEqual(agents, [
"pi",
"openclaw",
"codex",
"claude",
"gemini",
"cursor",
"copilot",
"kimi",
"opencode",
"kiro",
"kilocode",
"qwen",
]);
});
test("default agent is codex", () => {

View File

@ -45,6 +45,33 @@ test("integration: exec echo baseline", async () => {
});
});
test("integration: built-in cursor agent resolves to cursor-agent acp", async () => {
await withTempHome(async (homeDir) => {
const cwd = await fs.mkdtemp(path.join(os.tmpdir(), "acpx-integration-cwd-"));
const fakeBinDir = await fs.mkdtemp(path.join(os.tmpdir(), "acpx-fake-cursor-"));
try {
await writeFakeCursorAgent(fakeBinDir);
const result = await runCli(
["--approve-all", "--cwd", cwd, "--format", "quiet", "cursor", "exec", "echo hello"],
homeDir,
{
env: {
PATH: `${fakeBinDir}${path.delimiter}${process.env.PATH ?? ""}`,
},
},
);
assert.equal(result.code, 0, result.stderr);
assert.match(result.stdout, /hello/);
} finally {
await fs.rm(fakeBinDir, { recursive: true, force: true });
await fs.rm(cwd, { recursive: true, force: true });
}
});
});
test("integration: exec forwards model, allowed-tools, and max-turns in session/new _meta", async () => {
await withTempHome(async (homeDir) => {
const cwd = await fs.mkdtemp(path.join(os.tmpdir(), "acpx-integration-cwd-"));
@ -1372,6 +1399,36 @@ function baseExecArgs(cwd: string): string[] {
return [...baseAgentArgs(cwd), "--format", "quiet", "exec"];
}
async function writeFakeCursorAgent(binDir: string): Promise<void> {
if (process.platform === "win32") {
await fs.writeFile(
path.join(binDir, "cursor-agent.cmd"),
[
"@echo off",
"setlocal",
'if "%~1"=="acp" shift',
`"${process.execPath}" "${MOCK_AGENT_PATH}" %*`,
"",
].join("\r\n"),
{ encoding: "utf8" },
);
return;
}
await fs.writeFile(
path.join(binDir, "cursor-agent"),
[
"#!/bin/sh",
'if [ "$1" = "acp" ]; then',
" shift",
"fi",
`exec "${process.execPath}" "${MOCK_AGENT_PATH}" "$@"`,
"",
].join("\n"),
{ encoding: "utf8", mode: 0o755 },
);
}
async function withTempHome(run: (homeDir: string) => Promise<void>): Promise<void> {
const tempHome = await fs.mkdtemp(path.join(os.tmpdir(), "acpx-integration-home-"));
try {