This is the official Java SDK for GitHub Copilot. This repository treats the official .NET and nodejs SDKs for GitHub Copilot as reference implementations. These SDKS are all officially supported as GitHub open source projects. The Java implementation follows the backward compatibility guarantees offered by the reference implementations. As such this implementation may introduce breaking changes, according to the policy declared by the reference implementations. Use at your own risk.
+
diff --git a/.github/workflows/notes.template b/.github/workflows/notes.template
index 0fd7af642..9c148cdf1 100644
--- a/.github/workflows/notes.template
+++ b/.github/workflows/notes.template
@@ -1,6 +1,6 @@
# Installation
-⚠️ **Disclaimer:** This is the official Java SDK for GitHub Copilot. This repository treats the official .NET and nodejs SDKs for GitHub Copilot as reference implementations. These SDKS are all officially supported as GitHub open source projects. The Java implementation follows the backward compatibility guarantees offered by the reference implementations. As such this implementation may introduce breaking changes, according to the policy declared by the reference implementations. Use at your own risk.
+ℹ️ **Public Preview:** This is the official Java SDK for GitHub Copilot. This repository treats the official .NET and Node.js SDKs for GitHub Copilot as reference implementations. These SDKs are all officially supported as GitHub open source projects. The Java implementation follows the backward compatibility guarantees offered by the reference implementations. While in public preview, minor breaking changes may still occur between releases.
⚠️ **Artifact versioning plan:** Releases of this implementation track releases of the reference implementation. For each release of the reference implementation, there may follow a corresponding relase of this implementation with the same number as the reference implementation. Release identifiers of the reference implementation are in the form `vMaj.Min.Micro`. For example v0.1.32. The corresponding maven version for the release will be `Maj.Min.Micro-java.N`, where `Maj`, `Min` and `Micro` are the corresponding numbers for the reference impementation release, and `N` is a monotonically increasing sequence number starting with 0 for each release. See the corrseponding architectural decision record for more information in the `docs/adr` directory of the source code.
diff --git a/.github/workflows/publish-maven.yml b/.github/workflows/publish-maven.yml
index a7bb58f01..242853209 100644
--- a/.github/workflows/publish-maven.yml
+++ b/.github/workflows/publish-maven.yml
@@ -121,8 +121,13 @@ jobs:
sed -i "s|
[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\(-java\.[0-9][0-9]*\)\{0,1\}|
${VERSION}|g" README.md
sed -i "s|copilot-sdk-java:[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\(-java\.[0-9][0-9]*\)\{0,1\}|copilot-sdk-java:${VERSION}|g" README.md
+ # Update snapshot version in README.md
+ DEV_VERSION="${{ steps.versions.outputs.dev_version }}"
+ sed -i "s|
[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\(-java\.[0-9][0-9]*\)\{0,1\}-SNAPSHOT|
${DEV_VERSION}|g" README.md
+
# Update version in jbang-example.java
sed -i "s|copilot-sdk-java:[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\(-java\.[0-9][0-9]*\)\{0,1\}|copilot-sdk-java:${VERSION}|g" jbang-example.java
+ sed -i 's|copilot-sdk-java:${project\.version}|copilot-sdk-java:'"${VERSION}"'|g' jbang-example.java
# Update version in cookbook files (hardcoded for direct GitHub browsing and JBang usage)
find src/site/markdown/cookbook -name "*.md" -type f -exec \
@@ -206,7 +211,7 @@ jobs:
# Build the gh release command
GH_ARGS=("${CURRENT_TAG}")
- GH_ARGS+=("--title" "Copilot Java SDK ${VERSION}")
+ GH_ARGS+=("--title" "GitHub Copilot SDK for Java ${VERSION}")
GH_ARGS+=("--notes" "${RELEASE_NOTES}")
GH_ARGS+=("--generate-notes")
diff --git a/README.md b/README.md
index 539a33895..a33aaedda 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@
## Background
-> ⚠️ **Disclaimer:** This SDK tracks the pre-GA [GitHub Copilot SDKs](https://github.com/github/copilot-sdk) for [.NET](https://github.com/github/copilot-sdk/tree/main/dotnet) and [nodejs](https://github.com/github/copilot-sdk/tree/main/nodejs). This SDK may change in breaking ways. Use at your own risk.
+> ℹ️ **Public Preview:** This SDK tracks the [GitHub Copilot SDKs](https://github.com/github/copilot-sdk) for [.NET](https://github.com/github/copilot-sdk/tree/main/dotnet) and [Node.js](https://github.com/github/copilot-sdk/tree/main/nodejs). While in public preview, minor breaking changes may still occur between releases.
Java SDK for programmatic control of GitHub Copilot CLI, enabling you to build AI-powered applications and agentic workflows.
@@ -53,7 +53,7 @@ Snapshot builds of the next development version are published to Maven Central S
com.github
copilot-sdk-java
- 0.2.1-java.0-SNAPSHOT
+ 0.2.3-java.1-SNAPSHOT
```
diff --git a/instructions/copilot-sdk-java.instructions.md b/instructions/copilot-sdk-java.instructions.md
index bf18a3c5a..7881322fd 100644
--- a/instructions/copilot-sdk-java.instructions.md
+++ b/instructions/copilot-sdk-java.instructions.md
@@ -6,7 +6,7 @@ name: 'GitHub Copilot SDK Java Instructions'
## Core Principles
-- The SDK is in technical preview and may have breaking changes
+- The SDK is in public preview and may have breaking changes
- Requires Java 17 or later
- Requires GitHub Copilot CLI installed and in PATH
- Uses `CompletableFuture` for all async operations
diff --git a/jbang-example.java b/jbang-example.java
index 3d02653c1..dd1f80762 100644
--- a/jbang-example.java
+++ b/jbang-example.java
@@ -1,5 +1,5 @@
!
-//DEPS com.github:copilot-sdk-java:${project.version}
+//DEPS com.github:copilot-sdk-java:0.2.2-java.1
import com.github.copilot.sdk.CopilotClient;
import com.github.copilot.sdk.events.AssistantMessageEvent;
import com.github.copilot.sdk.events.SessionUsageInfoEvent;
diff --git a/pom.xml b/pom.xml
index 8e3e7e1df..b61c36166 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,7 +7,7 @@
com.github
copilot-sdk-java
-
0.2.2-java.1
+
0.2.3-java.1-SNAPSHOT
jar
GitHub Copilot SDK :: Java
@@ -33,7 +33,7 @@
scm:git:https://github.com/github/copilot-sdk-java.git
scm:git:https://github.com/github/copilot-sdk-java.git
https://github.com/github/copilot-sdk-java
-
v0.2.2-java.1
+
HEAD
diff --git a/src/site/markdown/index.md b/src/site/markdown/index.md
index b599484d9..60b96ce9d 100644
--- a/src/site/markdown/index.md
+++ b/src/site/markdown/index.md
@@ -1,6 +1,6 @@
# GitHub Copilot SDK for Java
-> ⚠️ **Disclaimer:** This is the official Java SDK for GitHub Copilot. This repository treats the official .NET and nodejs SDKs for GitHub Copilot as reference implementations. These SDKS are all officially supported as GitHub open source projects. The Java implementation follows the backward compatibility guarantees offered by the reference implementations. As such this implementation may introduce breaking changes, according to the policy declared by the reference implementations. Use at your own risk.
+> ℹ️ **Public Preview:** This is the official Java SDK for GitHub Copilot. This repository treats the official .NET and Node.js SDKs for GitHub Copilot as reference implementations. These SDKs are all officially supported as GitHub open source projects. The Java implementation follows the backward compatibility guarantees offered by the reference implementations. While in public preview, minor breaking changes may still occur between releases.
Welcome to the documentation for the **GitHub Copilot SDK for Java** — a Java SDK for programmatic control of GitHub Copilot CLI, enabling you to build AI-powered applications and agentic workflows.
diff --git a/src/test/java/com/github/copilot/sdk/CapiProxy.java b/src/test/java/com/github/copilot/sdk/CapiProxy.java
index 1a7df2d6c..bcd064d94 100644
--- a/src/test/java/com/github/copilot/sdk/CapiProxy.java
+++ b/src/test/java/com/github/copilot/sdk/CapiProxy.java
@@ -89,7 +89,11 @@ public String start() throws IOException, InterruptedException {
}
// Start the harness server using npx tsx
- var pb = new ProcessBuilder("npx", "tsx", "server.ts");
+ // On Windows, npx is installed as npx.cmd which requires cmd /c to launch
+ boolean isWindows = System.getProperty("os.name").toLowerCase().contains("win");
+ var pb = isWindows
+ ? new ProcessBuilder("cmd", "/c", "npx", "tsx", "server.ts")
+ : new ProcessBuilder("npx", "tsx", "server.ts");
pb.directory(harnessDir.toFile());
pb.redirectErrorStream(false);
diff --git a/src/test/java/com/github/copilot/sdk/CliServerManagerTest.java b/src/test/java/com/github/copilot/sdk/CliServerManagerTest.java
index 32257b0a5..e556839cc 100644
--- a/src/test/java/com/github/copilot/sdk/CliServerManagerTest.java
+++ b/src/test/java/com/github/copilot/sdk/CliServerManagerTest.java
@@ -70,13 +70,18 @@ void connectToServerTcpMode() throws Exception {
}
}
+ private static Process startBlockingProcess() throws IOException {
+ boolean isWindows = System.getProperty("os.name").toLowerCase().contains("windows");
+ return (isWindows ? new ProcessBuilder("cmd", "/c", "more") : new ProcessBuilder("cat")).start();
+ }
+
@Test
void connectToServerStdioMode() throws Exception {
var options = new CopilotClientOptions();
var manager = new CliServerManager(options);
// Create a dummy process for stdio mode
- Process process = new ProcessBuilder("cat").start();
+ Process process = startBlockingProcess();
try {
JsonRpcClient client = manager.connectToServer(process, null, null);
assertNotNull(client);
@@ -126,6 +131,14 @@ void processInfoWithNullPort() {
// resolveCliCommand is private, so we test indirectly through startCliServer
// with specific cliPath values.
+ // On Windows, "/nonexistent/copilot" is not an absolute path (no drive letter),
+ // so resolveCliCommand wraps it with "cmd /c" and ProcessBuilder.start()
+ // succeeds
+ // (launching cmd.exe). Use a Windows-absolute path to ensure IOException.
+ private static final String NONEXISTENT_CLI = System.getProperty("os.name").toLowerCase().contains("win")
+ ? "C:\\nonexistent\\copilot"
+ : "/nonexistent/copilot";
+
@Test
void startCliServerWithJsFile() throws Exception {
// Using a .js file path causes resolveCliCommand to prepend "node"
@@ -147,8 +160,8 @@ void startCliServerWithJsFile() throws Exception {
@Test
void startCliServerWithCliArgs() throws Exception {
// Test that cliArgs are included in the command
- var options = new CopilotClientOptions().setCliPath("/nonexistent/copilot")
- .setCliArgs(new String[]{"--extra-flag"}).setUseStdio(true);
+ var options = new CopilotClientOptions().setCliPath(NONEXISTENT_CLI).setCliArgs(new String[]{"--extra-flag"})
+ .setUseStdio(true);
var manager = new CliServerManager(options);
var ex = assertThrows(IOException.class, () -> manager.startCliServer());
@@ -158,7 +171,7 @@ void startCliServerWithCliArgs() throws Exception {
@Test
void startCliServerWithExplicitPort() throws Exception {
// Test the explicit port branch (useStdio=false, port > 0)
- var options = new CopilotClientOptions().setCliPath("/nonexistent/copilot").setUseStdio(false).setPort(9999);
+ var options = new CopilotClientOptions().setCliPath(NONEXISTENT_CLI).setUseStdio(false).setPort(9999);
var manager = new CliServerManager(options);
var ex = assertThrows(IOException.class, () -> manager.startCliServer());
@@ -168,7 +181,7 @@ void startCliServerWithExplicitPort() throws Exception {
@Test
void startCliServerWithGitHubToken() throws Exception {
// Test the github token branch
- var options = new CopilotClientOptions().setCliPath("/nonexistent/copilot").setGitHubToken("ghp_test123")
+ var options = new CopilotClientOptions().setCliPath(NONEXISTENT_CLI).setGitHubToken("ghp_test123")
.setUseStdio(true);
var manager = new CliServerManager(options);
@@ -179,7 +192,7 @@ void startCliServerWithGitHubToken() throws Exception {
@Test
void startCliServerWithUseLoggedInUserExplicit() throws Exception {
// Test the explicit useLoggedInUser=false branch (adds --no-auto-login)
- var options = new CopilotClientOptions().setCliPath("/nonexistent/copilot").setUseLoggedInUser(false)
+ var options = new CopilotClientOptions().setCliPath(NONEXISTENT_CLI).setUseLoggedInUser(false)
.setUseStdio(true);
var manager = new CliServerManager(options);
@@ -190,7 +203,7 @@ void startCliServerWithUseLoggedInUserExplicit() throws Exception {
@Test
void startCliServerWithGitHubTokenAndNoExplicitUseLoggedInUser() throws Exception {
// When gitHubToken is set and useLoggedInUser is null, defaults to false
- var options = new CopilotClientOptions().setCliPath("/nonexistent/copilot").setGitHubToken("ghp_test123")
+ var options = new CopilotClientOptions().setCliPath(NONEXISTENT_CLI).setGitHubToken("ghp_test123")
.setUseStdio(true);
var manager = new CliServerManager(options);
@@ -220,8 +233,7 @@ void startCliServerWithTelemetryAllOptions() throws Exception {
// so even with a nonexistent CLI path, the telemetry code path is exercised
var telemetry = new TelemetryConfig().setOtlpEndpoint("http://localhost:4318").setFilePath("/tmp/telemetry.log")
.setExporterType("otlp-http").setSourceName("test-app").setCaptureContent(true);
- var options = new CopilotClientOptions().setCliPath("/nonexistent/copilot").setTelemetry(telemetry)
- .setUseStdio(true);
+ var options = new CopilotClientOptions().setCliPath(NONEXISTENT_CLI).setTelemetry(telemetry).setUseStdio(true);
var manager = new CliServerManager(options);
var ex = assertThrows(IOException.class, () -> manager.startCliServer());
@@ -232,8 +244,7 @@ void startCliServerWithTelemetryAllOptions() throws Exception {
void startCliServerWithTelemetryCaptureContentFalse() throws Exception {
// Test the false branch of getCaptureContent()
var telemetry = new TelemetryConfig().setCaptureContent(false);
- var options = new CopilotClientOptions().setCliPath("/nonexistent/copilot").setTelemetry(telemetry)
- .setUseStdio(true);
+ var options = new CopilotClientOptions().setCliPath(NONEXISTENT_CLI).setTelemetry(telemetry).setUseStdio(true);
var manager = new CliServerManager(options);
var ex = assertThrows(IOException.class, () -> manager.startCliServer());
diff --git a/src/test/java/com/github/copilot/sdk/JsonRpcClientTest.java b/src/test/java/com/github/copilot/sdk/JsonRpcClientTest.java
index 8b5c1858c..4fb43f4b6 100644
--- a/src/test/java/com/github/copilot/sdk/JsonRpcClientTest.java
+++ b/src/test/java/com/github/copilot/sdk/JsonRpcClientTest.java
@@ -133,9 +133,14 @@ void testIsConnectedWithSocketClosed() throws Exception {
pair.serverSocket.close();
}
+ private static Process startBlockingProcess() throws IOException {
+ boolean isWindows = System.getProperty("os.name").toLowerCase().contains("windows");
+ return (isWindows ? new ProcessBuilder("cmd", "/c", "more") : new ProcessBuilder("cat")).start();
+ }
+
@Test
void testIsConnectedWithProcess() throws Exception {
- Process proc = new ProcessBuilder("cat").start();
+ Process proc = startBlockingProcess();
try (var client = JsonRpcClient.fromProcess(proc)) {
assertTrue(client.isConnected());
}
@@ -143,7 +148,7 @@ void testIsConnectedWithProcess() throws Exception {
@Test
void testIsConnectedWithProcessDead() throws Exception {
- Process proc = new ProcessBuilder("cat").start();
+ Process proc = startBlockingProcess();
var client = JsonRpcClient.fromProcess(proc);
proc.destroy();
proc.waitFor(5, TimeUnit.SECONDS);
@@ -155,7 +160,7 @@ void testIsConnectedWithProcessDead() throws Exception {
@Test
void testGetProcessReturnsProcess() throws Exception {
- Process proc = new ProcessBuilder("cat").start();
+ Process proc = startBlockingProcess();
try (var client = JsonRpcClient.fromProcess(proc)) {
assertSame(proc, client.getProcess());
}