Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions dotnet/GitHub.Copilot.SDK.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,7 @@
<Folder Name="/test/">
<Project Path="test/GitHub.Copilot.SDK.Test.csproj" />
</Folder>
<Folder Name="/samples/">
<Project Path="samples/Chat.csproj" />
</Folder>
</Solution>
9 changes: 9 additions & 0 deletions dotnet/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@ SDK for programmatic control of GitHub Copilot CLI.
dotnet add package GitHub.Copilot.SDK
```

## Run the Sample

Try the interactive chat sample (from the repo root):

```bash
cd dotnet/samples
dotnet run
```

## Quick Start

```csharp
Expand Down
32 changes: 32 additions & 0 deletions dotnet/samples/Chat.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using GitHub.Copilot.SDK;

await using var client = new CopilotClient();
await using var session = await client.CreateSessionAsync();

using var _ = session.On(evt =>
{
Console.ForegroundColor = ConsoleColor.Blue;
switch (evt)
{
case AssistantReasoningEvent reasoning:
Console.WriteLine($"[reasoning: {reasoning.Data.Content}]");
break;
case ToolExecutionStartEvent tool:
Console.WriteLine($"[tool: {tool.Data.ToolName}]");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor inconsistency: Missing tool arguments in output

The .NET sample only logs the tool name, while the Node.js, Python, and Go samples all include the tool arguments in their output.

For consistency across all SDK samples, consider adding the arguments:

case ToolExecutionStartEvent tool:
    var args = System.Text.Json.JsonSerializer.Serialize(tool.Data.Arguments);
    Console.WriteLine($"[tool: {tool.Data.ToolName} {args}]");
    break;

This would match the pattern in:

  • Node.js (line 13): `[tool: ${event.data.toolName} ${JSON.stringify(event.data.arguments)}]`
  • Python (line 18): f"[tool: {event.data.tool_name} {json.dumps(event.data.arguments)}]"
  • Go (lines 40-41): fmt.Sprintf("[tool: %s %s]", *event.Data.ToolName, args)

AI generated by SDK Consistency Review Agent for #492

break;
}
Console.ResetColor();
});

Console.WriteLine("Chat with Copilot (Ctrl+C to exit)\n");

while (true)
{
Console.Write("You: ");
var input = Console.ReadLine()?.Trim();
if (string.IsNullOrEmpty(input)) continue;
Console.WriteLine();

var reply = await session.SendAndWaitAsync(new MessageOptions { Prompt = input });
Console.WriteLine($"\nAssistant: {reply?.Data.Content}\n");
}
11 changes: 11 additions & 0 deletions dotnet/samples/Chat.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\src\GitHub.Copilot.SDK.csproj" />
</ItemGroup>
</Project>
9 changes: 9 additions & 0 deletions go/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,15 @@ A Go SDK for programmatic access to the GitHub Copilot CLI.
go get github.com/github/copilot-sdk/go
```

## Run the Sample

Try the interactive chat sample (from the repo root):

```bash
cd go/samples
go run chat.go
```

## Quick Start

```go
Expand Down
70 changes: 70 additions & 0 deletions go/samples/chat.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package main

import (
"bufio"
"context"
"encoding/json"
"fmt"
"os"
"strings"

"github.com/github/copilot-sdk/go"
)

const blue = "\033[34m"
const reset = "\033[0m"

func main() {
ctx := context.Background()
client := copilot.NewClient(nil)
if err := client.Start(ctx); err != nil {
panic(err)
}
defer client.Stop()

session, err := client.CreateSession(ctx, nil)
if err != nil {
panic(err)
}
defer session.Destroy()

session.On(func(event copilot.SessionEvent) {
var output string
switch event.Type {
case copilot.AssistantReasoning:
if event.Data.Content != nil {
output = fmt.Sprintf("[reasoning: %s]", *event.Data.Content)
}
case copilot.ToolExecutionStart:
if event.Data.ToolName != nil {
args, _ := json.Marshal(event.Data.Arguments)
output = fmt.Sprintf("[tool: %s %s]", *event.Data.ToolName, args)
}
}
if output != "" {
fmt.Printf("%s%s%s\n", blue, output, reset)
}
})

fmt.Println("Chat with Copilot (Ctrl+C to exit)\n")
scanner := bufio.NewScanner(os.Stdin)

for {
fmt.Print("You: ")
if !scanner.Scan() {
break
}
input := strings.TrimSpace(scanner.Text())
if input == "" {
continue
}
fmt.Println()

reply, _ := session.SendAndWait(ctx, copilot.MessageOptions{Prompt: input})
content := ""
if reply != nil && reply.Data.Content != nil {
content = *reply.Data.Content
}
fmt.Printf("\nAssistant: %s\n\n", content)
}
}
9 changes: 9 additions & 0 deletions go/samples/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module github.com/github/copilot-sdk/go/samples

go 1.24

require github.com/github/copilot-sdk/go v0.0.0

require github.com/google/jsonschema-go v0.4.2 // indirect

replace github.com/github/copilot-sdk/go => ../
4 changes: 4 additions & 0 deletions go/samples/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/jsonschema-go v0.4.2 h1:tmrUohrwoLZZS/P3x7ex0WAVknEkBZM46iALbcqoRA8=
github.com/google/jsonschema-go v0.4.2/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
10 changes: 10 additions & 0 deletions nodejs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,16 @@ TypeScript SDK for programmatic control of GitHub Copilot CLI via JSON-RPC.
npm install @github/copilot-sdk
```

## Run the Sample

Try the interactive chat sample (from the repo root):

```bash
cd nodejs/samples
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These instructions likely won’t work from a fresh checkout: the samples depend on @github/copilot-sdk which points to dist/ that isn’t built/checked in. Either add a step to build the Node SDK first (e.g., cd nodejs && npm ci && npm run build), or change the sample to import from ../src/index.js so it runs without a build.

Suggested change
cd nodejs/samples
cd nodejs
npm ci
npm run build
cd samples

Copilot uses AI. Check for mistakes.
npm install
npm start
```

## Quick Start

```typescript
Expand Down
33 changes: 33 additions & 0 deletions nodejs/samples/chat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import * as readline from "node:readline";
import { CopilotClient, type SessionEvent } from "@github/copilot-sdk";

async function main() {
const client = new CopilotClient();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cross-SDK Inconsistency - Missing Cleanup: The Node.js sample doesn't close/cleanup the client, which may leave the CLI subprocess running.

For consistency with other SDKs and proper resource management, consider adding cleanup. While Node.js doesn't have a built-in disposal pattern like .NET's using or Python's context managers, you could:

  1. Handle process exit signals:
process.on('SIGINT', async () => {
    await session.destroy();
    await client.stop();
    process.exit();
});
  1. Or document that the CLI process terminates when the parent exits (if that's the intended behavior).

Note: Go uses explicit defer, .NET uses await using, but Node.js has no cleanup shown.

AI generated by SDK Consistency Review Agent for #492

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cross-SDK Consistency Issue: The Node.js sample doesn't call any start method on the client, while Python calls await client.start() and Go calls client.Start(ctx) before using the client.

For consistency, if Node.js clients don't require an explicit start call (auto-start on first use), this should be documented. Alternatively, if Node.js supports a start() method, it should be called here to match the pattern in Python and Go samples.

AI generated by SDK Consistency Review Agent for #492

const session = await client.createSession();

session.on((event: SessionEvent) => {
let output: string | null = null;
if (event.type === "assistant.reasoning") {
output = `[reasoning: ${event.data.content}]`;
} else if (event.type === "tool.execution_start") {
output = `[tool: ${event.data.toolName} ${JSON.stringify(event.data.arguments)}]`;
}
if (output) console.log(`\x1b[34m${output}\x1b[0m`);
});

const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
const prompt = (q: string) => new Promise<string>((r) => rl.question(q, r));

console.log("Chat with Copilot (Ctrl+C to exit)\n");

while (true) {
const input = await prompt("You: ");
if (!input.trim()) continue;
console.log();

const reply = await session.sendAndWait({ prompt: input });
console.log(`\nAssistant: ${reply?.data.content}\n`);
}
}

main().catch(console.error);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cross-SDK Consistency Issue: The Node.js sample lacks resource cleanup, while:

  • Go uses defer client.Stop() and defer session.Destroy()
  • .NET uses await using for automatic disposal
  • Python should also add cleanup but currently doesn't

Consider adding cleanup in a try-finally block or using signal handlers for graceful shutdown to match the cleanup patterns in other SDKs.

AI generated by SDK Consistency Review Agent for #492

14 changes: 14 additions & 0 deletions nodejs/samples/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"name": "copilot-sdk-sample",
"type": "module",
"scripts": {
"start": "npx tsx chat.ts"
},
"dependencies": {
"@github/copilot-sdk": "file:.."
},
Comment on lines +7 to +9
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@github/copilot-sdk resolves to dist/index.js, but this repo does not check in nodejs/dist/. With a file:.. dependency, npm start in this folder will fail unless the SDK is built first. Consider importing from ../src/index.js (like nodejs/examples/basic-example.ts) or add a documented build step / prepare script so the dependency has a built dist/.

Copilot uses AI. Check for mistakes.
"devDependencies": {
"tsx": "^4.20.6",
"@types/node": "^22.0.0"
}
}
9 changes: 9 additions & 0 deletions python/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@ pip install -e ".[dev]"
uv pip install -e ".[dev]"
```

## Run the Sample

Try the interactive chat sample (from the repo root):

```bash
cd python/samples
python chat.py
```

## Quick Start

```python
Expand Down
40 changes: 40 additions & 0 deletions python/samples/chat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import asyncio
import json
from copilot import CopilotClient

BLUE = "\033[34m"
RESET = "\033[0m"


async def main():
async with CopilotClient() as client:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Critical Bug: The Python SDK does not implement async context manager (__aenter__ and __aexit__ methods), so this async with syntax will fail with AttributeError: __aenter__.

The Python sample should follow the explicit lifecycle pattern like Go:

async def main():
    client = CopilotClient()
    await client.start()
    try:
        session = await client.create_session({"streaming": True})
        # ... rest of code
    finally:
        await client.stop()

Cross-SDK Inconsistency: This reveals different lifecycle management patterns across SDKs:

  • .NET: Uses await using for automatic disposal ✅
  • Go: Explicitly calls Start()/Stop() with defer
  • Python: Should use explicit start()/stop() (context manager not implemented)
  • Node.js: No cleanup shown (potential resource leak)

AI generated by SDK Consistency Review Agent for #492

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistency: Python sample uses unsupported async context manager

The Python SDK's CopilotClient does not implement __aenter__/__aexit__ methods, so using async with CopilotClient() as client: will fail at runtime.

Based on the E2E tests and SDK documentation, the correct pattern should be:

client = CopilotClient()
try:
    await client.start()
    session = await client.create_session()
    # ... rest of the code
finally:
    await client.stop()

This matches the pattern used in the .NET, Go, and Node.js samples (accounting for language idioms). Each explicitly manages the client lifecycle with start/stop or using/defer statements.

AI generated by SDK Consistency Review Agent for #492

session = await client.create_session()

def on_event(event):
output = None
if event.type == "assistant.reasoning":
output = f"[reasoning: {event.data.content}]"
elif event.type == "tool.execution_start":
output = f"[tool: {event.data.tool_name} {json.dumps(event.data.arguments)}]"
if output:
print(f"{BLUE}{output}{RESET}")

session.on(on_event)

print("Chat with Copilot (Ctrl+C to exit)\n")

while True:
user_input = input("You: ").strip()
if not user_input:
continue
print()

reply = await session.send_and_wait({"prompt": user_input})
print(f"\nAssistant: {reply.data.content if reply else None}\n")


if __name__ == "__main__":
try:
asyncio.run(main())
except KeyboardInterrupt:
print("\nBye!")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cross-SDK Consistency Issue: The Python sample lacks resource cleanup. According to the memory, Python CopilotClient must be started/stopped explicitly.

For consistency with Go (which uses defer client.Stop()) and .NET (which uses await using), consider adding:

async def main():
    client = CopilotClient()
    try:
        await client.start()
        session = await client.create_session()
        # ... rest of code ...
    finally:
        await client.stop()

This ensures proper cleanup when the user presses Ctrl+C.

AI generated by SDK Consistency Review Agent for #492

Loading