Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
Copy Markdown
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
68 changes: 68 additions & 0 deletions go/samples/chat.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package main

import (
"bufio"
"context"
"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()
Comment on lines +24 to +30
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.

client.Stop() returns an error (it can aggregate cleanup errors). Deferring it without checking means cleanup failures are silently lost; wrap the defer in a function and handle/log the returned error.

This issue also appears in the following locations of the same file:

  • line 25
  • line 49
Suggested change
defer client.Stop()
session, err := client.CreateSession(ctx, &copilot.SessionConfig{Streaming: true})
if err != nil {
panic(err)
}
defer session.Destroy()
defer func() {
if err := client.Stop(); err != nil {
fmt.Fprintln(os.Stderr, "failed to stop Copilot client:", err)
}
}()
session, err := client.CreateSession(ctx, &copilot.SessionConfig{Streaming: true})
if err != nil {
panic(err)
}
defer func() {
if err := session.Destroy(); err != nil {
fmt.Fprintln(os.Stderr, "failed to destroy Copilot session:", err)
}
}()

Copilot uses AI. Check for mistakes.

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 {
output = fmt.Sprintf("[tool: %s]", *event.Data.ToolName)
}
}
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=
13 changes: 13 additions & 0 deletions nodejs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,19 @@ 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
npm ci
npm run build
cd samples
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
Copy Markdown
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
Copy Markdown
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();

Comment on lines +5 to +7
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.

The sample handles assistant.message_delta, but streaming is disabled by default. Create the session with { streaming: true } (e.g., await client.createSession({ streaming: true })) so delta events are actually emitted.

Copilot uses AI. Check for mistakes.
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}]`;
}
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`);
}
}
Comment on lines +26 to +31
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.

The loop prints an "Assistant: " prefix before calling sendAndWait, but the event handler also prints a \nAssistant: ... line on the final assistant.message. This creates duplicated/garbled output; prefer letting the event handler own all assistant output (remove the pre-send prefix), or printing only deltas during the call and using the final event just to end the line.

Copilot uses AI. Check for mistakes.

main().catch(console.error);
Copy link
Copy Markdown
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

Loading
Loading