Skip to content

feat(setup): add a setup agent step that uses OpenCode - #876

Closed
atornsii wants to merge 7 commits into
mainfrom
atorns/setup-agent-step
Closed

atornsii wants to merge 7 commits into
mainfrom
atorns/setup-agent-step

Conversation

@atornsii

Copy link
Copy Markdown

This draft adds the CLI side of the setup agent. Setup can now use an OpenCode session to add the SDK initialization code. The agent step replaces the init step in the wizard, and a new ldcli setup agent command runs only that step.

The design is in the scope doc for the ldcli setup agent. This PR covers the CLI work items that do not need a hosted service.

What changed

  • ldcli setup --agent=auto|on|off. With auto (the default), setup uses the agent if OpenCode 1.19.0 or later is installed, and the templates if not. With on, setup stops with an error if the agent cannot run. With off, setup always uses the templates.
  • ldcli setup agent --path . --sdk-id node-server --flag-key my-new-flag runs only the agent step. --sdk-key, --client-side-id, and --mobile-key only fill in the closing export command. The agent never receives them.
  • New package internal/setup/agent. It starts opencode serve in the project directory, creates a session, sends the task, reads the event stream, answers permission requests, and stops OpenCode when the run ends.
  • The wizard has a new stepAgent step. It shows finished tool calls and a y or n prompt for each permission request. After the run, it lists the changed files and the export command for the credential, and then it continues to the existing verify step.

Security

  • OpenCode listens on 127.0.0.1 only, with a random password for each run (OPENCODE_SERVER_PASSWORD).
  • The CLI removes every LD_ variable from the OpenCode environment, so OpenCode cannot read the access token.
  • The permission rules in OPENCODE_CONFIG_CONTENT allow reads and edits in the project. They require approval for every shell command, and they deny access to other directories, web access, and sub-agents.
  • The prompt tells the agent to read the credential from LAUNCHDARKLY_SDK_KEY, LAUNCHDARKLY_CLIENT_SIDE_ID, or LAUNCHDARKLY_MOBILE_KEY. No key goes into a prompt.
  • If the agent fails after it edited a file, setup stops and does not apply a template on top of the partial edit.
  • If the user quits while OpenCode starts, a tracker closes the run when setup exits.

Not in this PR

  • AgentControl evaluation and telemetry. ConfigSource is the seam for them. DefaultConfigSource supplies built-in instructions and no model, so OpenCode uses the model that the user configured. The Go AI SDK needs an OpenCode handler first.
  • The SDK relay and managed inference in gonfalon, and the CLI loopback proxy that uses them.
  • --capture-content, the outcome metric, the diff with revert, and the privacy notice.
  • The Go 1.26 upgrade, which the Go AI SDK needs.

Testing

  • Unit tests cover the event decoder, the stream reader, the prompt, the environment filter, the permission configuration, and the version check.
  • A fake OpenCode HTTP server tests the run lifecycle: the task, the events, the permission replies, and a stream that ends early.
  • Wizard tests with a fake runner cover the agent step, the permission keys, the fallback in auto mode, the error in on mode, and the close on quit.
  • go test -race ./cmd/setup/ ./internal/setup/... passes. go test ./... passes.
  • I did not run the agent against a real OpenCode install, because OpenCode is not installed on my machine. The API shapes come from the OpenCode v1.19.3 SDK types. The process startup in server.go has no automated test.

🤖 Generated with Claude Code

Setup can now use an OpenCode session to add the SDK initialization
code. The agent step replaces the init step in the wizard. The new
`ldcli setup agent` command runs only that step.

The `--agent` flag controls the step. With auto, the default, setup
uses the agent if OpenCode is installed, and the templates if not.
With on, setup stops if the agent cannot run. With off, setup always
uses the templates.

The CLI starts `opencode serve` on the loopback interface with a
random password for each run. It removes every LD_ variable from the
OpenCode environment, so OpenCode never gets the access token. The
agent can edit files in the project, but it must ask before it runs a
shell command. The wizard shows each request as a y or n prompt.

The code that the agent writes reads the SDK credential from an
environment variable. The CLI shows the export command after the run.

The model and the instructions come from a ConfigSource. A built-in
source supplies them until the Go AI SDK has an OpenCode handler.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
atornsii and others added 6 commits October 19, 2026 16:36
OpenCode can ask for several tool calls at once. The wizard kept only
one pending request, so each new request replaced the one before it.
The replaced requests never got an answer, and OpenCode waited forever.

The wizard now keeps a queue. It shows the oldest request and how many
more are waiting, and each y or n answers one request.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The wait-for-app screen printed the SDK key, the client-side ID, and the
mobile key, although each SDK reads only one of them. It now shows only
the credential that the chosen SDK uses. The prompt also names only that
environment variable, so the agent does not have to choose.

The wizard now says "the agent" instead of "OpenCode" in its progress
text and permission questions. Messages that tell the user to install or
upgrade OpenCode keep the name, because the user must act on them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The agent can ask the user to choose with OpenCode's question tool, for
example where the SDK key lives. The wizard did not show these
questions, so the run waited for an answer that never came.

The wizard now shows each question with numbered options. A number picks
an option, and s skips the request, so the agent goes on without
answers. `ldcli setup agent` asks on the terminal the same way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The agent now runs shell commands in the project without asking. It asks
the user only with the question tool, when it needs information such as
where to store a key. Approving every command made a run slow, and most
commands only read the project.

Some limits stay. Rules deny git commit, git push, git reset, sudo, and
rm -rf on absolute paths. Files outside the project, web access, and
sub-agents stay denied.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A closed terminal sends SIGHUP, which ended ldcli at once and left the
OpenCode process running. The wizard now quits on SIGHUP the same way
that Bubble Tea quits on SIGINT and SIGTERM, so it stops OpenCode.
`ldcli setup agent` stops OpenCode on SIGINT, SIGTERM, and SIGHUP.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The agent runs shell commands without approvals, and OpenCode's rules
limit only its file tools. In a test run, the agent searched the whole
disk with find, read files in another repo, and read the ldcli access
token with `ldcli config get`.

On macOS, ldcli now starts OpenCode with sandbox-exec and a Seatbelt
profile for each run. The sandbox applies to OpenCode and to every
command that it runs. It denies the home folder, except the project,
OpenCode's own folders, package caches, and toolchains. Other repos,
~/.config/ldcli, and ~/.ssh stay unreadable. System folders and the
network stay open. The profile uses resolved paths, because Seatbelt
matches the real path behind a symlink.

OpenCode's rules also deny `ldcli` and `npx skills` now. The CLI does
the LaunchDarkly work, and skills must not be installed into the repo.

Linux and Windows run without a sandbox for now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant