This guide configures a repository checkout of choco-pi as a custom Zed ACP agent. It does not install or fetch a published package.
- Node.js 24 LTS or newer. Bun is not supported. The current checkout is source-executed TypeScript and produces no build output.
- Zed. The currently installed and verified editor is Zed 1.18.0; that is a baseline, not a hard minimum. Older Zed releases receive best-effort support.
- A local clone of this repository whose path will remain stable.
piinstalled and available onPATH, with a model provider configured. SetPI_ACP_PI_COMMANDto an executable path ifpiis elsewhere.
From the repository root, install dependencies and the choco-pi profile as the root README describes:
pnpm install --frozen-lockfile
npm run install:profileThe install:profile step is required. It adds the
choco-pi-editor-context package from this checkout to Pi's global profile;
without it, Tasks can publish context but Pi sessions outside this checkout will
not consume it.
The installer reports every linked or updated path. If it finds conflicting
profile files, review them and rerun npm run install:profile -- --backup only if
you want it to preserve and replace those files.
Back up your Zed configuration before applying changes. Then preview the exact result:
node .pi/packages/choco-pi-acp/bin/choco-pi-acp.ts zed setup --dry-runThe preview reads the existing JSON-with-comments files and prints the full intended settings and Tasks documents without writing anything. Review the absolute Node and checkout paths in that output. Apply only after the preview is correct:
node .pi/packages/choco-pi-acp/bin/choco-pi-acp.ts zed setup --applyThere is no interactive confirmation prompt: passing --apply is the explicit
confirmation that permits writes. Existing changed files are copied to a
single .bak file before replacement, but keep your own backup as well.
On macOS, setup reads and writes:
~/.config/zed/settings.json~/.config/zed/tasks.json
Linux uses $XDG_CONFIG_HOME/zed/ when XDG_CONFIG_HOME is an absolute path,
and otherwise uses ~/.config/zed/. Windows default-path detection is not
implemented; use --zed-config-dir with an explicit directory there.
To target a different Zed configuration directory, add
--zed-config-dir <dir>. Relative values are resolved from the setup command's
working directory. The command prints the resolved configuration directory and
the exact settings.json and tasks.json paths. Dry runs and doctor do not
create the directory; setup --apply creates it when needed. An invalid path
fails instead of falling back to the platform default.
Setup makes these changes and no others:
- It adds
agent_servers.choco-pitosettings.json. The custom server runs the current absolute Node executable with the checkout's absolute.pi/packages/choco-pi-acp/bin/choco-pi-acp.tspath and setsPI_ACP_ENABLE_EMBEDDED_CONTEXTto"true". - It adds six definitions to
tasks.json: Sync Focused Context, Sync Focused Context (No Selection), Sync Saved File Context, List Live Sessions, Select Context Target, and Open Terminal Thread.
Unrelated settings and Tasks are retained. Rewriting tasks.json normalizes
it to plain JSON, so Task-file comments and formatting are not retained. An
existing, different choco-pi agent or one of the six Task labels is treated
as a conflict. Setup refuses to write unless you deliberately add --replace:
node .pi/packages/choco-pi-acp/bin/choco-pi-acp.ts zed setup --apply --replace--replace is limited to the choco-pi agent and those six labels.
Zed's --user-data-dir <DIR> option isolates a throwaway Zed profile under
<DIR>/config. Point setup at that configuration directory before starting the
profile:
PROFILE_DIR="$(mktemp -d)"
node .pi/packages/choco-pi-acp/bin/choco-pi-acp.ts zed setup \
--dry-run --zed-config-dir "$PROFILE_DIR/config"
node .pi/packages/choco-pi-acp/bin/choco-pi-acp.ts zed setup \
--apply --zed-config-dir "$PROFILE_DIR/config"
open -n -a /Applications/Zed.app --args --user-data-dir "$PROFILE_DIR" <project>Start the isolated profile through LaunchServices as shown above. On macOS the
zed CLI wrapper does not apply --user-data-dir: zed --user-data-dir <DIR>
hands the request to the already-running editor, so the launched instance uses
the real user profile instead. This was observed during the Phase 5 runtime E2E
run; lsof on that process showed
~/Library/Application Support/Zed/db/0-stable/db.sqlite open and the user's
installed extensions running, while the throwaway directory stayed empty except
for the config/ directory written by setup. The open -n -a form was verified
to open <DIR>/db/0-stable/db.sqlite instead. See
the runtime E2E evidence for the full record.
Inspect and remove the same isolated configuration explicitly:
node .pi/packages/choco-pi-acp/bin/choco-pi-acp.ts zed doctor \
--zed-config-dir "$PROFILE_DIR/config"
node .pi/packages/choco-pi-acp/bin/choco-pi-acp.ts zed remove \
--apply --zed-config-dir "$PROFILE_DIR/config"Restart Zed or reload its settings, open the Agent Panel, and choose the
choco-pi custom agent. Zed launches:
node .pi/packages/choco-pi-acp/bin/choco-pi-acp.tsThe settings generated by setup use absolute paths rather than the relative
form above. The adapter speaks ACP over standard input/output and starts Pi
with pi --mode rpc in the selected project directory. Pi remains responsible for
provider authentication, commands, tools, extensions, and persisted session
history.
A new thread normally opens with a startup block that reports the Pi version,
context, skills, prompts, and extensions, mirroring what pi prints in a
terminal. That block is suppressed when Pi's quietStartup setting is true
or "header" in ~/.pi/agent/settings.json or <project>/.pi/settings.json;
the shipped default is quietStartup: false, so the block appears unless you
turned it off. With either suppressing value the adapter still emits a "New version available"
notice when the installed Pi is outdated. If a fresh thread shows no startup
block, check that setting before treating it as a defect.
Each open ACP thread keeps its own live Pi child process, so several threads in
one Zed window remain independently usable and independently targetable by
editor-context sync. The adapter retains the eight most recently used threads by
default and closes the least recently used beyond that bound; set
PI_ACP_MAX_LIVE_SESSIONS to an integer from 1 through 32 in the agent
environment to change it. Closing a thread's Pi child does not delete its Pi
session: reopening the thread restores it from Pi's session history.
ACP v1 does not notify the adapter when a Zed thread pane closes. An otherwise
idle Pi child is therefore reaped by a per-session timer instead: ten minutes by
default, configured with PI_ACP_SESSION_IDLE_MS and clamped to 60,000 through
7,200,000 milliseconds (one through 120 minutes). Active and queued turns are
never reaped. If the timer expires with a dialog pending, the dialog is cancelled
through the normal exactly-once settlement path before shutdown. A later prompt
for that thread transparently restores the persisted Pi session in a new child.
ACP transport disconnect still shuts every child down immediately.
session/list reports every main Pi session recorded for the current project,
newest first, with the title Pi stored, the session's absolute working
directory, and its last activity time. Project scoping compares real filesystem
paths, so a worktree opened through a symlink (for example /tmp/project
against the realpath /private/tmp/project on macOS) still matches. Subagent
sidechain transcripts are excluded; ordinary continuations and branches of a
main session are kept.
Zed does not add external-agent threads to Thread History automatically. Per Zed's External Agents documentation, you open the Threads Sidebar, open Thread History, and use Import Threads; Zed then connects to the agent over ACP and adds the sessions it does not already have, as archived entries you open to restore. Because the adapter always reports an absolute working directory for each session, none of them are skipped by Zed's "sessions without an associated working directory" rule.
Open Zed's Task picker to run any installed Choco Pi Task:
Focus a text editor before opening the picker for the three context-sync Tasks.
Zed hides Tasks whose $ZED_FILE, $ZED_ROW, $ZED_COLUMN, or
$ZED_SELECTED_TEXT variables are unavailable, so opening the picker from the
Agent Panel makes the focused-context Tasks appear to be missing. For Sync
Focused Context, create the selection before opening the picker. The no-selection
variant needs an editor focus but does not require a selection.
Zed's $ZED_ROW and $ZED_COLUMN values are one-based, so the installed Tasks
pass them through unchanged as the editor-context line and column.
- Choco Pi: Sync Focused Context publishes the current file, one-based Zed row and column unchanged, language, symbol, worktree, and optional selected text. It does not save the buffer.
- Choco Pi: Sync Focused Context (No Selection) publishes the same path, cursor, language, symbol, and worktree metadata but never reads or publishes selection text.
- Choco Pi: Sync Saved File Context saves the current buffer and publishes its file, language, symbol, and worktree without cursor or selection text.
- Choco Pi: List Live Sessions lists live Pi sessions for the current canonical worktree and prints a ready-to-run targeting command.
- Choco Pi: Select Context Target performs the same listing and tells you to run the chosen command in Zed's terminal.
- Choco Pi: Open Terminal Thread runs
piin the worktree as the fallback for TUI-only workflows.
Setup does not change your keymap. To make focused sync a one-key action, merge
the binding from editors/zed/keymap.example.json into
~/.config/zed/keymap.json:
{
"context": "Workspace && !Terminal",
"bindings": {
"ctrl-alt-c": ["task::Spawn", { "task_name": "Choco Pi: Sync Focused Context" }]
}
}This is an explicit snapshot, not automatic ambient tracking. Run the binding again after focus, cursor, symbol, or selection changes. The extension consumes the newest accepted snapshot immediately before the next agent turn; snapshots expire after 30 seconds.
The generated agent definition sets:
"PI_ACP_ENABLE_EMBEDDED_CONTEXT": "true"Only the exact string true enables the capability. When enabled, attached
ACP resource and resource-link blocks are deduplicated and appended to the Pi
prompt in a clearly delimited untrusted editor context section. Resource
metadata and text are retained; binary resource data is omitted with its
decoded size. The complete embedded section is capped at 64 KiB of UTF-8 and
contains a truncation marker when necessary. When the flag is absent or has
another value, the adapter does not advertise embedded context and discards
resource blocks sent despite that disabled capability.
The focused-context Task carries selection text in the
CHOCO_PI_ZED_SELECTION environment variable, never in a command argument. It
rejects selection text over 16 KiB; the complete editor-context document also
has a 64 KiB limit.
Use Choco Pi: Sync Focused Context (No Selection) when the snapshot should
retain path, cursor, language, symbol, and worktree metadata without selection
text. That generated Task passes --no-selection-text and does not place the
selection in its Task environment. The maintained equivalent is included in
editors/zed/tasks.example.json.
The publish command also accepts either opt-out directly:
node .pi/packages/choco-pi-editor-context/src/cli.ts publish \
--cwd "$PWD" --no-selection-text
CHOCO_PI_EDITOR_CONTEXT_NO_SELECTION=1 \
node .pi/packages/choco-pi-editor-context/src/cli.ts publish --cwd "$PWD"Only the exact environment value 1 enables the opt-out. Disabling selection
text wins over --selection-env or --selection-file: the CLI does not read
the supplied selection source and omits selection from the context document.
This precedence is intentionally privacy-preserving. To apply the setting to
the regular focused-context Task without changing Task JSON, launch Zed from
an environment containing CHOCO_PI_EDITOR_CONTEXT_NO_SELECTION=1; the
dedicated no-selection Task remains the portable per-sync choice.
To disable all explicit ACP resource content, including attached selection
resources, remove PI_ACP_ENABLE_EMBEDDED_CONTEXT from the agent environment
or set it to any value other than true.
Context publication matches live Pi sessions by canonical worktree. With one match, publication selects it automatically. With more than one match, it fails instead of guessing: choosing the wrong session could send file or selection context to the wrong conversation.
Because every open thread keeps its own live Pi child, more than one match is the normal case as soon as you have two threads open in the same worktree. Run Choco Pi: List Live Sessions and select a target explicitly.
Canonical worktree matching resolves symbolic links on both sides, so a project
opened as /tmp/project matches a Pi session whose working directory is the
realpath /private/tmp/project. A worktree that cannot be resolved on the
filesystem is rejected rather than compared lexically.
publish runs the consuming session's own validation, against that session's
working directory, before it writes anything. A context that the session would
reject now fails at the Task and CLI layer with bounded diagnostic codes and a
nonzero exit status, and no context file is written. Earlier revisions reported
Editor context published. with exit 0 while the session rejected the payload.
Run Choco Pi: List Live Sessions, then copy one printed select command
into Zed's terminal. Its source form is:
node .pi/packages/choco-pi-editor-context/src/cli.ts select \
--session-id <session-id> \
--owner-id <owner-id> \
--cwd <absolute-worktree-path>The stored target is scoped to the canonical worktree and is revalidated before every publish. Clear it with:
node .pi/packages/choco-pi-editor-context/src/cli.ts select \
--clear \
--cwd <absolute-worktree-path>Explicit --session-id plus --owner-id on a publish command takes
precedence over the stored target. Both identifiers are required together.
For a local Zed project, Zed launches Node, the adapter, Pi, and Tasks on the local machine. Paths and credentials therefore belong to that machine.
The designed Zed SSH flow runs the checkout, Tasks, adapter, and Pi on the
remote host while the UI remains local. Install Node 24+, Pi, repository
dependencies, and the choco-pi profile on the remote host; run setup there so
the generated absolute paths are remote-native. Pi credentials stay on the
host running Pi and are not copied from Zed. Tool locations travel through ACP
rather than a local zed command.
Remote operation is documented but was not verified end-to-end in this
initiative because no SSH target was available. Treat it as manual setup, and
validate with zed doctor, context diagnostics, and a non-sensitive test file
before using selected text.
Setup owns only the choco-pi key. Existing pi, pi-acp, or registry agent
entries remain untouched and may appear alongside it. Validate a new
choco-pi thread first, then remove the old entry manually if you no longer
need it. --replace does not migrate or remove differently named entries.
The adapter continues to use Pi's session files as the conversation source of truth. Removing a Zed agent definition does not delete Pi sessions. Keep the checkout at the path captured by setup, or rerun setup after moving it.
Preview removal before changing files:
node .pi/packages/choco-pi-acp/bin/choco-pi-acp.ts zed remove --dry-runThen explicitly apply it:
node .pi/packages/choco-pi-acp/bin/choco-pi-acp.ts zed remove --applyRemoval deletes only agent_servers.choco-pi and the six Choco Pi Task
labels, including both focused-context variants. It preserves other agents,
settings, and Tasks and backs up each
existing changed file to .bak. It does not remove the manual keymap binding;
delete that entry from keymap.json yourself. It also does not uninstall the
global choco-pi profile, dependencies, checkout, Pi sessions, or adapter
session mappings. No profile-uninstall command is shipped.
- Treat file and selection content as sensitive. It is transmitted only after an explicit ACP attachment or Task sync action. The integration does not log selection text, and rejection diagnostics contain bounded codes rather than the text.
- Editor-context files live under
~/.pi/agent/choco-pi/session-bridge/editor-context/by default. Directories are created with owner-only0700permissions and atomic temporary files with0600permissions. A consumed file is removed whether validation accepts or rejects it; bounded cleanup removes expired owner-verified leftovers without deleting another live owner's file. - Context is accepted only for the matching live session, owner, generation, canonical worktree, and unexpired capture. Symlinks and foreign-owned context files are rejected where the platform exposes ownership.
- ACP is a transport and presentation protocol, not a sandbox. Pi and its tools
run with the permissions of the Node/Pi process. Review project-local Pi
configuration before trusting it; the adapter never adds
--approve.
Check whether the generated settings and Tasks match the current checkout:
node .pi/packages/choco-pi-acp/bin/choco-pi-acp.ts zed doctordoctor accepts no mutation flags, accepts --zed-config-dir <dir> for an
alternate profile, and returns nonzero for missing or conflicting setup. To
inspect live sessions matching an absolute worktree, run:
node .pi/packages/choco-pi-editor-context/src/cli.ts diagnose \
--cwd <absolute-worktree-path>diagnose currently has the same behavior as list: it prints at most 20
matching live sessions and returns nonzero when there are none. If sync reports
LIVE_TARGET_AMBIGUOUS, select a target as described above. If it reports
LIVE_TARGET_NOT_FOUND, start a Pi-backed choco-pi thread in that worktree
first. A stale saved target is cleared automatically before matching again.
If Pi rejects project-local configuration as untrusted, review the project and complete Pi's one-time trust prompt from a terminal:
node .pi/packages/choco-pi-acp/bin/choco-pi-acp.ts \
--terminal-trust <absolute-project-path>The path must be an existing absolute directory. Exit Pi after recording the
choice. For provider login or API-key setup, run the source entry with
--terminal-login instead. If the adapter cannot locate Pi, install
@earendil-works/pi-coding-agent or set PI_ACP_PI_COMMAND to its executable
path.