Launch Copilot CLI in your current local checkout with tools for working on one or more GitHub Codespaces over SSH.
The launcher:
- Selects, starts, and connects to Codespaces.
- Deploys the helper binary and establishes multiplexed SSH connections.
- Installs a user-scoped Copilot extension and starts its local extension host.
- Launches Copilot without changing directories.
Copilot remains in the checkout where you ran gh copilot-codespace. Local instructions, agents, skills, and other project files are the source of session context. Nothing is fetched from a Codespace into a generated mirror, and no project agents, hooks, or remote server configuration are generated locally.
All built-in local tools remain enabled. The extension adds the first-party Codespaces tools, but repository implementation must happen in the Codespace working copy:
- Use
remote_view,remote_edit,remote_create,remote_apply_patch,remote_grep, andremote_globfor repository files. - Use
remote_bash,remote_write_bash,remote_read_bash,remote_list_bash, andremote_stop_bashfor builds, tests, linters, dependency operations, repository scripts, Git commands, and interactive diagnostics. - Use
list_codespacesfirst and pass the reportedworkdiras explicitcwdon each remote command call. - Reserve local tools for local context, Copilot session artifacts, and explicit local-only work.
The local and remote checkouts are separate and are not synchronized.
The Go binary has four modes:
- Launcher (default) — connects Codespaces, prepares the extension session, and replaces itself with Copilot.
- Exec agent (
gh-copilot-codespace exec) — deployed to a Codespace for structured remote process execution. - Extension host (
gh-copilot-codespace extension-host) — exposes the 21 first-party remote and lifecycle tools through the Copilot extension API. - Sandbox daemon (
gh-copilot-codespace daemon) — runs inside each connected Codespace and handles remote operations over a long-lived JSON protocol stream.
The stable user-scoped extension at ~/.copilot/extensions/copilot-codespace/extension.mjs activates only when the launcher supplies a valid per-session manifest and token. It forwards the extension host's tools, appended systemMessage, and inline @remote-explorer agent to joinSession.
The systemMessage always explains that local files provide context while repository work belongs on the Codespace, and it supplies remote-tool routing guidance. When at least one Codespace is connected, the extension also registers the inline @remote-explorer agent for remote codebase exploration. These are delivered through the extension API rather than generated project files.
The extension host wraps each Codespace SSH executor with a daemon client by default. Calls share one multiplexed SSH stream, avoiding a new SSH process and shell setup for every operation. If daemon deployment or connection fails, the host falls back to direct SSH. Set COPILOT_CODESPACE_NO_DAEMON=1 to force that fallback.
Daemon-backed remote tools probe the connection on demand. If a connected Codespace was stopped, the next tool call automatically wakes it, refreshes stale SSH multiplexing, and reconnects the daemon. Operations are never replayed after an in-flight connection loss because their remote outcome may be unknown; the error reports whether recovery succeeded so the next call can continue on the restored connection.
ghCLI authenticated withcodespacescope- Permission to list, create, and connect GitHub Codespaces
- Copilot CLI installed, or available through
gh copilot
# As a gh extension (recommended)
gh extension install ekroon/gh-copilot-codespace
# With mise
mise use -g github:ekroon/gh-copilot-codespace
# Or build from source
go build -o gh-copilot-codespace ./cmd/gh-copilot-codespace# Interactive picker (select zero, one, or many)
gh copilot-codespace
# Connect to a specific Codespace
gh copilot-codespace -c my-codespace-name
# Connect to multiple Codespaces
gh copilot-codespace -c codespace-1,codespace-2
# Start without a Codespace, then create or connect from the agent
gh copilot-codespace --no-codespace
# Limit the agent to the Codespaces selected at startup
gh copilot-codespace --selected-only
# Forward Copilot CLI arguments directly
gh copilot-codespace -c my-codespace-name --resume <copilot-session>
gh copilot-codespace --model claude-sonnet-4.5Arguments not consumed by this launcher are forwarded directly to Copilot. In particular, --resume is Copilot's conversation-resume option; this project does not create or manage launcher workspaces.
If neither -c/--codespace nor --no-codespace is supplied, the interactive picker supports multiple selections. Press Enter without selecting a Codespace to launch with none connected. The agent can then use list_available_codespaces, get_codespace_options, create_codespace, or connect_codespace.
The extension registers:
remote_view,remote_edit,remote_createremote_apply_patchremote_bash,remote_grep,remote_globremote_write_bash,remote_read_bash,remote_stop_bash,remote_list_bashremote_cd,remote_cwdremote_copylist_codespaces,list_available_codespacesget_codespace_options,create_codespace,connect_codespace,delete_codespaceopen_shell
@remote-explorer can use the command-capable exploration set: remote_grep, remote_glob, remote_view, remote_bash, remote_write_bash, remote_read_bash, remote_stop_bash, remote_list_bash, remote_cwd, and list_codespaces. It starts from list_codespaces.workdir, passes cwd explicitly on every remote_bash/remote_grep/remote_glob call, and does not use remote_cd, remote_edit, remote_create, remote_copy, remote_apply_patch, lifecycle discovery/mutation tools, or open_shell.
remote_viewmirrors the localviewsurface with line ranges,forceReadLargeFiles, directory listings, image results, and binary metadata.remote_createcreates parent directories and refuses to overwrite an existing file.remote_grepmirrors localrgoptions includingpath/paths,output_mode,-i,-A,-B,-C,-n,head_limit, andmultiline.remote_globmirrors localglobpath selection withpath/paths; results default to 1,000 matches, accept an optionallimitcapped at 10,000, and report truncation in structured metadata.remote_apply_patchaccepts the canonicalapply_patchpayload and applies it atomically on the Codespace.remote_bashuses a lightweight daemon-managed process for normal sync commands. Commands that exceedinitial_waitkeep running once under the returnedshellId; usemode=asyncwhen stdin or a PTY is required. Useremote_write_bash,remote_read_bash,remote_list_bash, andremote_stop_bashto manage those longer-lived sessions.
remote_copy performs an explicit one-time transfer between the current local checkout and a connected Codespace:
remote_copy(source="src/app.go", destination="cs://github/src/app.go")
remote_copy(source="cs://github/src/generated.go", destination="src/generated.go")
The github segment is the alias shown by list_codespaces. The tool copies files only, refuses to overwrite by default, and never establishes synchronization between checkouts.
All repository remote_* tools accept an optional codespace alias. The alias is optional when exactly one Codespace is connected.
remote_bash(codespace="api", cwd="/workspaces/api", command="go test ./...")
remote_view(codespace="web", path="/workspaces/web/src/app.ts")
Use list_codespaces to see connected aliases, repositories, branches, and working directories. In default sessions, lifecycle tools can create, connect, and delete Codespaces during the session, but @remote-explorer does not use them.
--selected-only limits the agent to the Codespaces selected at startup. At least one Codespace must be supplied with -c/--codespace or chosen in the interactive picker.
| Tool group | Behavior |
|---|---|
Repository remote_* tools |
Operate on the connected startup Codespaces. |
list_codespaces |
Lists the connected startup Codespaces. |
list_available_codespaces, get_codespace_options |
Not registered. |
create_codespace, connect_codespace, delete_codespace |
Not registered. The agent cannot create, attach, disconnect, or delete Codespaces through the extension lifecycle tools. |
--no-codespace --selected-only and an empty interactive selection are rejected because a selected-only session has no lifecycle tools for adding a Codespace later.
Provisioners run setup after the launcher connects a Codespace and after extension lifecycle tools create or connect one. Built-in provisioners upload terminal information and start git fetch origin in the background. Fetch output is written to ~/.local/state/gh-copilot-codespace/git-fetch.log unless XDG_STATE_HOME is set. Branch-specific provisioning only blocks for a fetch when the requested branch is not already available locally.
For Ghostty, the built-in terminal provisioner uploads xterm-ghostty. Custom bash provisioners run on the Codespace.
Add custom provisioners in ~/.config/copilot-codespace/provisioners.json:
{
"builtins": {
"terminfo": true,
"git-fetch": true
},
"provisioners": [
{
"name": "ghostty-shell-setup",
"bash": "echo 'export BAT_THEME=GitHub' >> ~/.bashrc",
"match": { "terminal": "xterm-ghostty" }
},
{
"name": "github-setup",
"bash": "cd /workspaces/github && bin/setup",
"match": { "repository": "github/github" }
}
]
}| Field | Description |
|---|---|
builtins.terminfo |
Enable or disable terminal information upload (enabled by default) |
builtins.git-fetch |
Enable or disable background git fetch origin (enabled by default) |
name |
Provisioner name shown in logs |
bash |
Command run on the Codespace |
match.terminal |
Run only for the detected local terminal |
match.repository |
Run only for the specified repository |
Provisioners without match run on every Codespace. Errors are logged but do not block connection.
go build ./cmd/gh-copilot-codespace
go vet ./...
go test -race ./...Integration tests require a real Codespace and gh authentication:
./scripts/setup-signoff.sh
./scripts/integration-test.sh
gh signoff integrationEvery push to main runs vet, tests, and cross-platform builds. A successful build creates a dev-{sha} pre-release.
Pushing a semantic-version tag triggers a release for gh extension users. To promote a development release to latest for mise users, run the Promote to Latest workflow after integration signoff.
| Variable | Description |
|---|---|
CODESPACE_REGISTRY |
Connected Codespace metadata supplied to the extension host |
CODESPACE_LOCAL_WORKDIR |
Current local checkout used as the allowed local root for remote_copy |
CODESPACE_LIFECYCLE_CONFIG |
Per-session lifecycle access policy |
COPILOT_CODESPACE_TIMINGS |
Set to enable connection-stage timings on stderr for launcher and lifecycle connect flows |
COPILOT_CODESPACE_EXTENSION_TOKEN |
Token activating the user-scoped extension for this launch |
COPILOT_CODESPACE_EXTENSION_MANIFEST |
Path to the per-session extension manifest |
COPILOT_CODESPACE_NO_DAEMON |
Set to 1 to use direct SSH instead of the sandbox daemon |