A Rust SDK for programmatic access to the GitHub Copilot CLI.
Note: This SDK is in technical preview and may change in breaking ways.
See github/copilot-sdk for the equivalent SDKs in TypeScript, Python, Go, and .NET. The Rust SDK seeks parity with those SDKs; see Differences From Other SDKs below for the small set of intentional divergences.
Releases: github.com/github/copilot-sdk/releases?q=rust%2F — per-version release notes for the Rust crate.
use std::sync::Arc;
use github_copilot_sdk::{Client, ClientOptions, SessionConfig};
use github_copilot_sdk::handler::ApproveAllHandler;
# async fn example() -> Result<(), github_copilot_sdk::Error> {
let client = Client::start(ClientOptions::default()).await?;
let session = client.create_session(
SessionConfig::default().with_handler(Arc::new(ApproveAllHandler)),
).await?;
let _message_id = session.send("Hello!").await?;
session.disconnect().await?;
client.stop().await.ok();
# Ok(())
# }Your Application
↓
github_copilot_sdk::Client (manages CLI process lifecycle)
↓
github_copilot_sdk::Session (per-session event loop + handler dispatch)
↓ JSON-RPC over stdio or TCP
copilot --server --stdio
The SDK manages the CLI process lifecycle: spawning, health-checking, and graceful shutdown. Communication uses JSON-RPC 2.0 over stdin/stdout with Content-Length framing (the same protocol used by LSP). TCP transport is also supported.
// Start a client (spawns CLI process)
let client = Client::start(options).await?;
// Create a new session
let session = client.create_session(config.with_handler(handler)).await?;
// Resume an existing session
let session = client.resume_session(config.with_handler(handler)).await?;
// Low-level RPC
let result = client.call("method.name", Some(params)).await?;
let response = client.send_request("method.name", Some(params)).await?;
// Health check (echoes message back, returns typed PingResponse)
let pong = client.ping("hello").await?;
// Shutdown
client.stop().await?;ClientOptions:
| Field | Type | Description |
|---|---|---|
program |
CliProgram |
Resolve (default: auto-detect) or Path(PathBuf) (explicit) |
prefix_args |
Vec<OsString> |
Args before --server (e.g. script path for node) |
cwd |
PathBuf |
Working directory for CLI process |
env |
Vec<(OsString, OsString)> |
Environment variables for CLI process |
env_remove |
Vec<OsString> |
Environment variables to remove |
extra_args |
Vec<String> |
Extra CLI flags |
transport |
Transport |
Stdio (default), Tcp { port }, or External { host, port } |
With the default CliProgram::Resolve, Client::start() automatically resolves the binary via github_copilot_sdk::resolve::copilot_binary() — checking COPILOT_CLI_PATH, the embedded CLI, and then the system PATH. Use CliProgram::Path(path) to skip resolution.
Created via Client::create_session or Client::resume_session. Owns an internal event loop that dispatches events to the SessionHandler.
use github_copilot_sdk::MessageOptions;
// Simple send — &str / String convert into MessageOptions automatically.
// Returns the assigned message ID for correlation with later events.
let _id = session.send("Fix the bug in auth.rs").await?;
// Send with mode and attachments
let _id = session
.send(
MessageOptions::new("What's in this image?")
.with_mode("autopilot")
.with_attachments(attachments),
)
.await?;
// Message history
let messages = session.get_messages().await?;
// Abort the current agent turn
session.abort().await?;
// Model management
session.set_model("claude-sonnet-4.5", None).await?;
// Generated typed RPCs cover lower-level session operations.
let model = session.rpc().model().get_current().await?;
let mode = session.rpc().mode().get().await?;
// Workspace files
let files = session.rpc().workspaces().list_files().await?;
let content = session
.rpc()
.workspaces()
.read_file(github_copilot_sdk::generated::api_types::WorkspacesReadFileRequest {
path: "plan.md".to_string(),
})
.await?;
// Plan management
let plan = session.rpc().plan().read().await?;
session
.rpc()
.plan()
.update(github_copilot_sdk::generated::api_types::PlanUpdateRequest {
content: "Updated plan content".to_string(),
})
.await?;
// Fleet (sub-agents)
session
.rpc()
.fleet()
.start(github_copilot_sdk::generated::api_types::FleetStartRequest {
prompt: Some("Implement the auth module".to_string()),
})
.await?;
// Cleanup (preserves on-disk session state for later resume)
session.disconnect().await?;High-level helpers are convenience wrappers over a fully-typed
JSON-RPC namespace generated from the GitHub Copilot CLI schema. Client::rpc()
and Session::rpc() give direct access to every method on the wire,
including ones with no helper today, with strongly-typed request and
response structs.
// Common generated RPCs.
let files = session.rpc().workspaces().list_files().await?.files;
let models = client.rpc().models().list().await?.models;
// Methods with no helper — full schema-typed access.
let agents = session.rpc().agent().list().await?.agents;
let tasks = session.rpc().tasks().list().await?.tasks;
let forked = client
.rpc()
.sessions()
.fork(github_copilot_sdk::generated::api_types::SessionsForkRequest {
session_id: "session-id".to_string(),
from_message_id: None,
})
.await?;New RPCs land in the namespace immediately as the schema regenerates; helpers are added on top only when an ergonomic story is worth the maintenance.
Implement this trait to control how a session responds to CLI events. Two styles are supported:
1. Per-event methods (recommended). Override only the callbacks you care about; every method has a safe default (permission → deny, user input → none, external tool → "no handler", elicitation → cancel, exit plan → default). This is the serenity::EventHandler pattern.
use async_trait::async_trait;
use github_copilot_sdk::handler::{PermissionResult, SessionHandler};
use github_copilot_sdk::types::{PermissionRequestData, RequestId, SessionId};
struct MyHandler;
#[async_trait]
impl SessionHandler for MyHandler {
async fn on_permission_request(
&self,
_sid: SessionId,
_rid: RequestId,
data: PermissionRequestData,
) -> PermissionResult {
if data.extra.get("tool").and_then(|v| v.as_str()) == Some("view") {
PermissionResult::Approved
} else {
PermissionResult::Denied
}
}
async fn on_session_event(&self, sid: SessionId, event: github_copilot_sdk::types::SessionEvent) {
println!("[{sid}] {}", event.event_type);
}
}2. Single on_event method. Override on_event directly and match on HandlerEvent — useful for logging middleware, custom routing, or when you want one exhaustive dispatch point.
use github_copilot_sdk::handler::*;
use async_trait::async_trait;
#[async_trait]
impl SessionHandler for MyRouter {
async fn on_event(&self, event: HandlerEvent) -> HandlerResponse {
match event {
HandlerEvent::SessionEvent { session_id, event } => {
println!("[{session_id}] {}", event.event_type);
HandlerResponse::Ok
}
HandlerEvent::PermissionRequest { .. } => {
HandlerResponse::Permission(PermissionResult::Approved)
}
HandlerEvent::UserInput { question, .. } => {
HandlerResponse::UserInput(Some(UserInputResponse {
answer: prompt_user(&question),
was_freeform: true,
}))
}
_ => HandlerResponse::Ok,
}
}
}The default on_event dispatches to the per-event methods, so overriding on_event short-circuits them entirely — pick one style per handler.
Events are processed serially per session — blocking in a handler method pauses that session's event loop (which is correct, since the CLI is also waiting for the response). Other sessions are unaffected.
Note: Notification-triggered events (
PermissionRequestviapermission.requested,ExternalToolviaexternal_tool.requested) are dispatched on spawned tasks and may run concurrently with the serial event loop. See the trait-level docs onSessionHandlerfor details.
let config = SessionConfig {
model: Some("gpt-5".into()),
system_message: Some(SystemMessageConfig {
content: Some("Always explain your reasoning.".into()),
..Default::default()
}),
request_elicitation: Some(true), // enable elicitation provider
..Default::default()
};
let session = client.create_session(config.with_handler(handler)).await?;Hooks intercept CLI behavior at lifecycle points — tool use, prompt submission, session start/end, and errors. Install a SessionHooks impl with [SessionConfig::with_hooks] — the SDK auto-enables hooks in SessionConfig when one is set.
use std::sync::Arc;
use github_copilot_sdk::hooks::*;
use async_trait::async_trait;
struct MyHooks;
#[async_trait]
impl SessionHooks for MyHooks {
async fn on_hook(&self, event: HookEvent) -> HookOutput {
match event {
HookEvent::PreToolUse { input, ctx } => {
if input.tool_name == "dangerous_tool" {
HookOutput::PreToolUse(PreToolUseOutput {
permission_decision: Some("deny".to_string()),
permission_decision_reason: Some("blocked by policy".to_string()),
..Default::default()
})
} else {
HookOutput::None // pass through
}
}
HookEvent::SessionStart { input, .. } => {
HookOutput::SessionStart(SessionStartOutput {
additional_context: Some("Extra system context".to_string()),
..Default::default()
})
}
_ => HookOutput::None,
}
}
}
let session = client
.create_session(
config
.with_handler(handler)
.with_hooks(Arc::new(MyHooks)),
)
.await?;Hook events: PreToolUse, PostToolUse, UserPromptSubmitted, SessionStart, SessionEnd, ErrorOccurred. Each carries typed input/output structs. Return HookOutput::None for events you don't handle.
Transforms customize system message sections during session creation. The SDK injects action: "transform" entries for each section ID your transform handles.
use github_copilot_sdk::transforms::*;
use async_trait::async_trait;
struct MyTransform;
#[async_trait]
impl SystemMessageTransform for MyTransform {
fn section_ids(&self) -> Vec<String> {
vec!["instructions".to_string()]
}
async fn transform_section(
&self,
_section_id: &str,
content: &str,
_ctx: TransformContext,
) -> Option<String> {
Some(format!("{content}\n\nAlways be concise."))
}
}
let session = client
.create_session(
config
.with_handler(handler)
.with_transform(Arc::new(MyTransform)),
)
.await?;Define client-side tools as named types with ToolHandler, then route them with ToolHandlerRouter. Enable the derive feature for schema_for::<T>() — it generates JSON Schema from Rust types via schemars.
use std::sync::Arc;
use github_copilot_sdk::handler::ApproveAllHandler;
use github_copilot_sdk::tool::{
schema_for, tool_parameters, JsonSchema, ToolHandler, ToolHandlerRouter,
};
use github_copilot_sdk::{Error, SessionConfig, Tool, ToolInvocation, ToolResult};
use serde::Deserialize;
use async_trait::async_trait;
#[derive(Deserialize, JsonSchema)]
struct GetWeatherParams {
/// City name
city: String,
/// Temperature unit
unit: Option<String>,
}
struct GetWeatherTool;
#[async_trait]
impl ToolHandler for GetWeatherTool {
fn tool(&self) -> Tool {
Tool {
name: "get_weather".to_string(),
namespaced_name: None,
description: "Get weather for a city".to_string(),
parameters: tool_parameters(schema_for::<GetWeatherParams>()),
instructions: None,
}
}
async fn call(&self, inv: ToolInvocation) -> Result<ToolResult, Error> {
let params: GetWeatherParams = serde_json::from_value(inv.arguments)?;
Ok(ToolResult::Text(format!("Weather in {}: sunny", params.city)))
}
}
// Build a router that dispatches tool calls by name
let router = ToolHandlerRouter::new(
vec![Box::new(GetWeatherTool)],
Arc::new(ApproveAllHandler),
);
let config = SessionConfig {
tools: Some(router.tools()),
..Default::default()
}
.with_handler(Arc::new(router));
let session = client.create_session(config).await?;Tools are named types (not closures) — visible in stack traces and navigable via "go to definition". The router implements SessionHandler, forwarding unrecognized tools and non-tool events to the inner handler.
For trivial tools that don't need a named type, define_tool collapses the definition to a single expression:
use github_copilot_sdk::tool::{define_tool, JsonSchema, ToolHandlerRouter};
use github_copilot_sdk::ToolResult;
use serde::Deserialize;
#[derive(Deserialize, JsonSchema)]
struct GetWeatherParams { city: String }
let router = ToolHandlerRouter::new(
vec![define_tool(
"get_weather",
"Get weather for a city",
|_inv, params: GetWeatherParams| async move {
Ok(ToolResult::Text(format!("Sunny in {}", params.city)))
},
)],
Arc::new(ApproveAllHandler),
);The closure receives the full ToolInvocation alongside the deserialized parameters, so handlers that need inv.session_id or inv.tool_call_id for telemetry, streaming updates, or scoped lookups can use them directly. Use _inv when you don't need the metadata.
Reach for the ToolHandler trait directly when you need shared state across multiple methods or want a named type that shows up by name in stack traces.
Set a permission policy directly on SessionConfig with the chainable builders. They wrap whatever handler you've installed (defaulting to DenyAllHandler if none) so only permission requests are intercepted; every other event flows through unchanged.
let session = client
.create_session(
SessionConfig::default()
.with_handler(Arc::new(my_handler))
.approve_all_permissions(),
// or .deny_all_permissions()
// or .approve_permissions_if(|data| {
// data.extra.get("tool").and_then(|v| v.as_str()) != Some("shell")
// })
)
.await?;Call the policy method after
with_handler—with_handleroverwrites the handler field, soapprove_all_permissions().with_handler(...)discards the wrap.
For composing a policy onto a handler outside the builder chain (e.g. when wrapping a ToolHandlerRouter you've built elsewhere), the permission module exposes the same primitives as free functions:
use github_copilot_sdk::permission;
let router = ToolHandlerRouter::new(tools, Arc::new(MyHandler));
let handler = permission::approve_all(Arc::new(router));
// or permission::deny_all(...) / permission::approve_if(..., predicate)
let session = client.create_session(config.with_handler(handler)).await?;The SDK negotiates capabilities with the CLI after session creation. Enable elicitation to let the agent present structured UI dialogs (forms, URL prompts) to the user.
let config = SessionConfig {
request_elicitation: Some(true),
..Default::default()
};The handler receives HandlerEvent::ElicitationRequest with a message, optional JSON Schema for form fields, and an optional mode. Known modes include Form and Url, but the mode may be absent or an unknown future value. Return HandlerResponse::Elicitation(result).
Some sessions ask the user free-form questions (or multiple-choice prompts) outside the elicitation flow. Implement SessionHandler::on_user_input and the SDK will forward userInput.request callbacks:
async fn on_user_input(
&self,
_session_id: SessionId,
question: String,
choices: Option<Vec<String>>,
_allow_freeform: Option<bool>,
) -> Option<UserInputResponse> {
// Render `question` + `choices` to your UI, then:
Some(UserInputResponse {
answer: "Yes".to_string(),
was_freeform: false,
})
}Return None to signal "no answer available" (the CLI falls back to its own prompt). Enable via SessionConfig::request_user_input (defaults to Some(true)).
Register named commands so users can invoke them as /name args from the TUI:
use github_copilot_sdk::types::{CommandContext, CommandDefinition, CommandHandler};
use async_trait::async_trait;
struct DeployCommand;
#[async_trait]
impl CommandHandler for DeployCommand {
async fn on_command(&self, ctx: CommandContext) -> Result<(), github_copilot_sdk::Error> {
println!("deploy {}", ctx.args);
Ok(())
}
}
let mut config = SessionConfig::default();
config.commands = Some(vec![
CommandDefinition::new("deploy", Arc::new(DeployCommand))
.with_description("Deploy the application"),
]);Only name and description are sent over the wire; the handler stays in your process. Returning Err(_) surfaces the message back through the TUI.
Set streaming: true to receive incremental delta events alongside finalized messages:
let mut config = SessionConfig::default();
config.streaming = Some(true);
let mut events = session.subscribe();
while let Ok(event) = events.recv().await {
match event.event_type.as_str() {
"assistant.message_delta" | "assistant.reasoning_delta" => {
if let Some(d) = event.data.get("delta").and_then(|v| v.as_str()) {
print!("{d}");
}
}
"assistant.message" => println!(), // final
_ => {}
}
}When streaming is off (the default), only the final assistant.message and assistant.reasoning events fire. Delta events arrive in order; concatenating their delta text payloads reproduces the final message.
Enable the SDK's session-store integration so conversations persist across CLI restarts and grow beyond the model's context window via automatic compaction:
use github_copilot_sdk::types::InfiniteSessionConfig;
let mut infinite = InfiniteSessionConfig::default();
infinite.workspace_path = Some("/path/to/workspace".into());
let mut config = SessionConfig::default();
config.infinite_sessions = Some(infinite);The CLI emits session.compaction_start / session.compaction_complete events around each compaction. The session id remains stable across compactions; resume with Client::resume_session to pick up a prior conversation. Workspace state lives under ~/.copilot/session-state/{sessionId} by default — override with workspace_path to relocate.
Route model traffic through your own inference endpoint instead of GitHub's hosted models:
use github_copilot_sdk::types::ProviderConfig;
let mut provider = ProviderConfig::default();
provider.provider_type = Some("openai".to_string());
provider.base_url = "https://my-proxy.example.com/v1".to_string();
provider.bearer_token = Some(std::env::var("OPENAI_API_KEY")?);
let mut config = SessionConfig::default();
config.provider = Some(provider);Provider types include "openai", "azure", and "anthropic". Set wire_api to "completions" or "responses" (OpenAI/Azure only). Custom headers go in provider.headers. The SDK forwards the configuration to the CLI verbatim — the CLI handles the upstream call, including authentication.
Forward OpenTelemetry signals from the spawned CLI process to your collector:
use github_copilot_sdk::{ClientOptions, OtelExporterType, TelemetryConfig};
let mut telem = TelemetryConfig::default();
telem.exporter_type = Some(OtelExporterType::OtlpHttp);
telem.otlp_endpoint = Some("http://localhost:4318".to_string());
telem.source_name = Some("my-app".to_string());
let mut opts = ClientOptions::default();
opts.telemetry = Some(telem);
let client = Client::start(opts).await?;The SDK injects the appropriate environment variables (COPILOT_OTEL_EXPORTER_TYPE, OTEL_EXPORTER_OTLP_ENDPOINT, ...) into the spawned CLI process. The SDK takes no OpenTelemetry dependency; the CLI itself owns the exporter pipeline. Caller-supplied ClientOptions::env entries override telemetry-injected values.
For fire-and-forget messaging where you need to block until the agent finishes:
use std::time::Duration;
use github_copilot_sdk::MessageOptions;
// Sends a message and blocks until session.idle or session.error
session
.send_and_wait(
MessageOptions::new("Fix the bug").with_wait_timeout(Duration::from_secs(120)),
)
.await?;Default timeout is 60 seconds. Only one send_and_wait can be active per session — concurrent calls return an error.
SessionId — a newtype wrapper around String that prevents accidentally passing workspace IDs or request IDs where session IDs are expected. Transparent serialization (#[serde(transparent)]), zero-cost Deref<Target=str>, and ergonomic comparisons with &str and String.
use github_copilot_sdk::SessionId;
let id = SessionId::new("sess-abc123");
assert_eq!(id, "sess-abc123"); // compare with &str
let raw: String = id.into_inner(); // unwrap when neededThe SDK uses a typed error enum:
pub enum Error {
Protocol(ProtocolError), // JSON-RPC framing, CLI startup, version mismatch
Rpc { code: i32, message: String }, // CLI returned an error response
Session(SessionError), // Session not found, agent error, timeout, conflicts
Io(std::io::Error), // Transport I/O error
Json(serde_json::Error), // Serialization error
BinaryNotFound { name, hint }, // CLI binary not found
}
// Check if the transport is broken (caller should discard the client)
if err.is_transport_failure() {
client = Client::start(options).await?;
}The Rust SDK aligns closely with the Node, Python, Go, and .NET SDKs but diverges in a few places where Rust idiom or the type system gives a clearly better shape, and exposes a small additional surface where the language affords ergonomics the dynamically-typed SDKs don't.
SessionFsProviderregistration is direct, not factory-closure. Where Node/Python/Go/.NET accept a closure that the runtime calls on each session-create to build a fresh provider, the Rust SDK takesArc<dyn SessionFsProvider>directly via [SessionConfig::with_session_fs_provider]. The factory pattern doesn't cleanly express in Rust at the session-config call site — there is noSessionvalue to thread in, and the SDK already prefers traits over boxed closures for handler-shaped APIs (SessionHandler,SessionHooks,ToolHandler).
use std::sync::Arc;
use github_copilot_sdk::session_fs::{SessionFsConfig, SessionFsConventions};
let mut options = ClientOptions::default();
options.session_fs = Some(SessionFsConfig::new(
"/workspace",
"/workspace/.copilot",
SessionFsConventions::Posix,
));
let client = Client::start(options).await?;
let session = client
.create_session(
SessionConfig::default()
.with_handler(Arc::new(ApproveAllHandler))
.with_session_fs_provider(Arc::new(MyProvider::new())),
)
.await?;See examples/session_fs.rs for a complete
in-memory provider implementation.
A handful of conveniences exist only on the Rust SDK as of 0.1.0. These are surface areas where Rust idiom (newtypes, enums, trait objects) gives a clearly nicer shape than Node/Python/Go/.NET currently expose. Rust gets to be Rust here — cross-SDK parity for these is a post-release conversation, not a release blocker. None of these are deprecated and none of them are scheduled for removal.
- Typed newtypes —
SessionIdandRequestIdare#[serde(transparent)]newtypes aroundString, so the type system distinguishes a session identifier from an arbitraryStringat compile time. Node/Python/Go use bare strings. - Permission policy builders —
permission::approve_all,permission::deny_all, andpermission::approve_if(handler, predicate)incrate::permissionprovide composable, no-handler-needed permission shortcuts that wrap an existingSessionHandler. Other SDKs require a full handler implementation for these patterns. Client::from_streams— connect to a CLI server over arbitrary caller-suppliedAsyncRead/AsyncWrite. Useful for testing, in-process embedding, or custom transports. Other SDKs are spawn-only or fixed-stdio.enum Transport { Stdio, Tcp, External }— explicit, exhaustive transport selector onClientOptions::transport. Node/Python/Go rely on conditional config field combinations instead.- Split
prefix_args/extra_argsonClientOptions— separate arg vectors for "prepend before subcommand" vs "append after the built-in flags", giving precise control over CLI invocation order without string-splicing.
| File | Description |
|---|---|
lib.rs |
Client, ClientOptions, CliProgram, Transport, Error |
session.rs |
Session struct, event loop, send/send_and_wait, Client::create_session/resume_session |
subscription.rs |
EventSubscription / LifecycleSubscription (Stream-able observer handles for subscribe() / subscribe_lifecycle()) |
handler.rs |
SessionHandler trait, HandlerEvent/HandlerResponse enums, ApproveAllHandler |
hooks.rs |
SessionHooks trait, HookEvent/HookOutput enums, typed hook inputs/outputs |
transforms.rs |
SystemMessageTransform trait, section-level system message customization |
tool.rs |
ToolHandler trait, ToolHandlerRouter, schema_for::<T>() (with derive feature) |
types.rs |
CLI protocol types (SessionId, SessionEvent, SessionConfig, Tool, etc.) |
resolve.rs |
Binary resolution (copilot_binary, node_binary, extended_path) |
embeddedcli.rs |
Embedded CLI extraction (embedded-cli feature) |
router.rs |
Internal per-session event demux |
jsonrpc.rs |
Internal Content-Length framed JSON-RPC transport |
By default, copilot_binary() searches COPILOT_CLI_PATH, the system PATH, and common install locations. To ship with a specific CLI version embedded in the binary, set COPILOT_CLI_VERSION at build time:
COPILOT_CLI_VERSION=1.0.15 cargo build-
Build time: The SDK's
build.rsdetectsCOPILOT_CLI_VERSION, downloads the platform-appropriate archive from thegithub/copilot-cliGitHub Releases (copilot-{platform}.tar.gzon macOS/Linux,.zipon Windows), verifies the archive's SHA-256 against the release'sSHA256SUMS.txt, extracts thecopilotbinary, compresses it with zstd, and embeds viainclude_bytes!(). No extra steps or tools needed — just the env var. -
Runtime: On the first call to
github_copilot_sdk::resolve::copilot_binary(), the embedded binary is lazily extracted to~/.cache/github-copilot-sdk-{version}/copilot(orcopilot.exeon Windows), SHA-256 verified, and cached. Subsequent calls return the cached path. -
Dev builds: Without the env var,
build.rsdoes nothing. The binary is resolved from PATH as usual — zero friction.
copilot_binary() checks these sources in order:
COPILOT_CLI_PATHenvironment variable- Embedded CLI (build-time, via
COPILOT_CLI_VERSION) - System PATH + common install locations
Supported: darwin-arm64, darwin-x64, linux-x64, linux-arm64, win32-x64, win32-arm64. The target platform is auto-detected from CARGO_CFG_TARGET_OS and CARGO_CFG_TARGET_ARCH (cross-compilation works).
No features are enabled by default — the bare SDK resolves the CLI from COPILOT_CLI_PATH or the system PATH without pulling in additional feature-gated dependencies.
| Feature | Default | Description |
|---|---|---|
embedded-cli |
— | Build-time CLI embedding via COPILOT_CLI_VERSION (adds sha2, zstd). Enable when you need to ship a self-contained binary with a pinned CLI version. |
derive |
— | schema_for::<T>() for generating JSON Schema from Rust types (adds schemars). Enable when defining tool parameters. |
# These examples use registry syntax for illustration; until the crate is
# published, use a path or git dependency instead.
# Minimal — resolve CLI from PATH
github-copilot-sdk = "0.1"
# Ship a pinned CLI version in your binary
github-copilot-sdk = { version = "0.1", features = ["embedded-cli"] }
# Derive JSON Schema for tool parameters
github-copilot-sdk = { version = "0.1", features = ["derive"] }
# Both
github-copilot-sdk = { version = "0.1", features = ["embedded-cli", "derive"] }