Moving the copilot-sdk cookbook content in here

This commit is contained in:
Aaron Powell
2026-01-29 14:29:36 +11:00
parent ccdfd66cc2
commit f59e0b4080
53 changed files with 5385 additions and 0 deletions

View File

@@ -0,0 +1,84 @@
# Runnable Recipe Examples
This folder contains standalone, executable TypeScript examples for each cookbook recipe. Each file can be run directly with `tsx` or via npm scripts.
## Prerequisites
- Node.js 18 or later
- Install dependencies (this links to the local SDK in the repo):
```bash
npm install
```
## Running Examples
Each `.ts` file is a complete, runnable program. You can run them in two ways:
### Using npm scripts:
```bash
npm run <script-name>
```
### Using tsx directly:
```bash
npx tsx <filename>.ts
```
### Available Recipes
| Recipe | npm script | Direct command | Description |
| -------------------- | ------------------------------ | --------------------------------- | ------------------------------------------ |
| Error Handling | `npm run error-handling` | `npx tsx error-handling.ts` | Demonstrates error handling patterns |
| Multiple Sessions | `npm run multiple-sessions` | `npx tsx multiple-sessions.ts` | Manages multiple independent conversations |
| Managing Local Files | `npm run managing-local-files` | `npx tsx managing-local-files.ts` | Organizes files using AI grouping |
| PR Visualization | `npm run pr-visualization` | `npx tsx pr-visualization.ts` | Generates PR age charts |
| Persisting Sessions | `npm run persisting-sessions` | `npx tsx persisting-sessions.ts` | Save and resume sessions across restarts |
### Examples with Arguments
**PR Visualization with specific repo:**
```bash
npx tsx pr-visualization.ts --repo github/copilot-sdk
```
**Managing Local Files (edit the file to change target folder):**
```bash
# Edit the targetFolder variable in managing-local-files.ts first
npx tsx managing-local-files.ts
```
## Local SDK Development
The `package.json` references the local Copilot SDK using `"file:../../.."`. This means:
- Changes to the SDK source are immediately available
- No need to publish or install from npm
- Perfect for testing and development
If you modify the SDK source, you may need to rebuild:
```bash
cd ../../..
npm run build
```
## TypeScript Features
These examples use modern TypeScript/Node.js features:
- Top-level await (requires `"type": "module"` in package.json)
- ESM imports
- Type safety with TypeScript
- async/await patterns
## Learning Resources
- [TypeScript Documentation](https://www.typescriptlang.org/docs/)
- [Node.js Documentation](https://nodejs.org/docs/latest/api/)
- [GitHub Copilot SDK for Node.js](https://github.com/github/copilot-sdk/blob/main/nodejs/README.md)
- [Parent Cookbook](../README.md)

View File

@@ -0,0 +1,17 @@
import { CopilotClient } from "@github/copilot-sdk";
const client = new CopilotClient();
try {
await client.start();
const session = await client.createSession({ model: "gpt-5" });
const response = await session.sendAndWait({ prompt: "Hello!" });
console.log(response?.data.content);
await session.destroy();
} catch (error: any) {
console.error("Error:", error.message);
} finally {
await client.stop();
}

View File

@@ -0,0 +1,47 @@
import { CopilotClient } from "@github/copilot-sdk";
import * as os from "node:os";
import * as path from "node:path";
// Create and start client
const client = new CopilotClient();
await client.start();
// Create session
const session = await client.createSession({
model: "gpt-5",
});
// Event handler
session.on((event) => {
switch (event.type) {
case "assistant.message":
console.log(`\nCopilot: ${event.data.content}`);
break;
case "tool.execution_start":
console.log(` → Running: ${event.data.toolName} ${event.data.toolCallId}`);
break;
case "tool.execution_complete":
console.log(` ✓ Completed: ${event.data.toolCallId}`);
break;
}
});
// Ask Copilot to organize files
// Change this to your target folder
const targetFolder = path.join(os.homedir(), "Downloads");
await session.sendAndWait({
prompt: `
Analyze the files in "${targetFolder}" and organize them into subfolders.
1. First, list all files and their metadata
2. Preview grouping by file extension
3. Create appropriate subfolders (e.g., "images", "documents", "videos")
4. Move each file to its appropriate subfolder
Please confirm before moving any files.
`,
});
await session.destroy();
await client.stop();

View File

@@ -0,0 +1,33 @@
import { CopilotClient } from "@github/copilot-sdk";
const client = new CopilotClient();
await client.start();
// Create multiple independent sessions
const session1 = await client.createSession({ model: "gpt-5" });
const session2 = await client.createSession({ model: "gpt-5" });
const session3 = await client.createSession({ model: "claude-sonnet-4.5" });
console.log("Created 3 independent sessions");
// Each session maintains its own conversation history
await session1.sendAndWait({ prompt: "You are helping with a Python project" });
await session2.sendAndWait({ prompt: "You are helping with a TypeScript project" });
await session3.sendAndWait({ prompt: "You are helping with a Go project" });
console.log("Sent initial context to all sessions");
// Follow-up messages stay in their respective contexts
await session1.sendAndWait({ prompt: "How do I create a virtual environment?" });
await session2.sendAndWait({ prompt: "How do I set up tsconfig?" });
await session3.sendAndWait({ prompt: "How do I initialize a module?" });
console.log("Sent follow-up questions to each session");
// Clean up all sessions
await session1.destroy();
await session2.destroy();
await session3.destroy();
await client.stop();
console.log("All sessions destroyed successfully");

View File

@@ -0,0 +1,629 @@
{
"name": "copilot-sdk-cookbook-recipes",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "copilot-sdk-cookbook-recipes",
"version": "1.0.0",
"dependencies": {
"@github/copilot-sdk": "file:../../src"
},
"devDependencies": {
"@types/node": "^22.19.7",
"tsx": "^4.19.2",
"typescript": "^5.7.2"
}
},
"../..": {
"name": "@github/copilot-sdk",
"version": "0.1.8",
"license": "MIT",
"dependencies": {
"@github/copilot": "^0.0.388-1",
"vscode-jsonrpc": "^8.2.1",
"zod": "^4.3.5"
},
"devDependencies": {
"@types/node": "^22.19.6",
"@typescript-eslint/eslint-plugin": "^8.0.0",
"@typescript-eslint/parser": "^8.0.0",
"esbuild": "^0.27.0",
"eslint": "^9.0.0",
"glob": "^11.0.0",
"json-schema": "^0.4.0",
"json-schema-to-typescript": "^15.0.4",
"prettier": "^3.4.0",
"quicktype-core": "^23.2.6",
"rimraf": "^6.1.2",
"semver": "^7.7.3",
"tsx": "^4.20.6",
"typescript": "^5.0.0",
"vitest": "^4.0.16"
},
"engines": {
"node": ">=18.0.0"
}
},
"../../..": {},
"../../src": {},
"node_modules/@esbuild/aix-ppc64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz",
"integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"aix"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz",
"integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-arm64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz",
"integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/android-x64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz",
"integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-arm64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz",
"integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/darwin-x64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz",
"integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-arm64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz",
"integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/freebsd-x64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz",
"integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"freebsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz",
"integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==",
"cpu": [
"arm"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-arm64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz",
"integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ia32": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz",
"integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-loong64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz",
"integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==",
"cpu": [
"loong64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-mips64el": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz",
"integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==",
"cpu": [
"mips64el"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-ppc64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz",
"integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==",
"cpu": [
"ppc64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-riscv64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz",
"integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==",
"cpu": [
"riscv64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-s390x": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz",
"integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==",
"cpu": [
"s390x"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/linux-x64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz",
"integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-arm64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz",
"integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/netbsd-x64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz",
"integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"netbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-arm64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz",
"integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openbsd-x64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz",
"integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openbsd"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/openharmony-arm64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz",
"integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"openharmony"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/sunos-x64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz",
"integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"sunos"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-arm64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz",
"integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-ia32": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz",
"integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==",
"cpu": [
"ia32"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@esbuild/win32-x64": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz",
"integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=18"
}
},
"node_modules/@github/copilot-sdk": {
"resolved": "../../src",
"link": true
},
"node_modules/@types/node": {
"version": "22.19.7",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.7.tgz",
"integrity": "sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~6.21.0"
}
},
"node_modules/esbuild": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz",
"integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
"esbuild": "bin/esbuild"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"@esbuild/aix-ppc64": "0.27.2",
"@esbuild/android-arm": "0.27.2",
"@esbuild/android-arm64": "0.27.2",
"@esbuild/android-x64": "0.27.2",
"@esbuild/darwin-arm64": "0.27.2",
"@esbuild/darwin-x64": "0.27.2",
"@esbuild/freebsd-arm64": "0.27.2",
"@esbuild/freebsd-x64": "0.27.2",
"@esbuild/linux-arm": "0.27.2",
"@esbuild/linux-arm64": "0.27.2",
"@esbuild/linux-ia32": "0.27.2",
"@esbuild/linux-loong64": "0.27.2",
"@esbuild/linux-mips64el": "0.27.2",
"@esbuild/linux-ppc64": "0.27.2",
"@esbuild/linux-riscv64": "0.27.2",
"@esbuild/linux-s390x": "0.27.2",
"@esbuild/linux-x64": "0.27.2",
"@esbuild/netbsd-arm64": "0.27.2",
"@esbuild/netbsd-x64": "0.27.2",
"@esbuild/openbsd-arm64": "0.27.2",
"@esbuild/openbsd-x64": "0.27.2",
"@esbuild/openharmony-arm64": "0.27.2",
"@esbuild/sunos-x64": "0.27.2",
"@esbuild/win32-arm64": "0.27.2",
"@esbuild/win32-ia32": "0.27.2",
"@esbuild/win32-x64": "0.27.2"
}
},
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
"integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/get-tsconfig": {
"version": "4.13.0",
"resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.13.0.tgz",
"integrity": "sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"resolve-pkg-maps": "^1.0.0"
},
"funding": {
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
}
},
"node_modules/resolve-pkg-maps": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz",
"integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==",
"dev": true,
"license": "MIT",
"funding": {
"url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1"
}
},
"node_modules/tsx": {
"version": "4.21.0",
"resolved": "https://registry.npmjs.org/tsx/-/tsx-4.21.0.tgz",
"integrity": "sha512-5C1sg4USs1lfG0GFb2RLXsdpXqBSEhAaA/0kPL01wxzpMqLILNxIxIOKiILz+cdg/pLnOUxFYOR5yhHU666wbw==",
"dev": true,
"license": "MIT",
"dependencies": {
"esbuild": "~0.27.0",
"get-tsconfig": "^4.7.5"
},
"bin": {
"tsx": "dist/cli.mjs"
},
"engines": {
"node": ">=18.0.0"
},
"optionalDependencies": {
"fsevents": "~2.3.3"
}
},
"node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici-types": {
"version": "6.21.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
"dev": true,
"license": "MIT"
}
}
}

View File

@@ -0,0 +1,21 @@
{
"name": "copilot-sdk-cookbook-recipes",
"version": "1.0.0",
"type": "module",
"description": "Runnable examples for GitHub Copilot SDK cookbook recipes",
"scripts": {
"error-handling": "tsx error-handling.ts",
"multiple-sessions": "tsx multiple-sessions.ts",
"managing-local-files": "tsx managing-local-files.ts",
"pr-visualization": "tsx pr-visualization.ts",
"persisting-sessions": "tsx persisting-sessions.ts"
},
"dependencies": {
"@github/copilot-sdk": "*"
},
"devDependencies": {
"@types/node": "^22.19.7",
"tsx": "^4.19.2",
"typescript": "^5.7.2"
}
}

View File

@@ -0,0 +1,37 @@
import { CopilotClient } from "@github/copilot-sdk";
const client = new CopilotClient();
await client.start();
// Create a session with a memorable ID
const session = await client.createSession({
sessionId: "user-123-conversation",
model: "gpt-5",
});
await session.sendAndWait({ prompt: "Let's discuss TypeScript generics" });
console.log(`Session created: ${session.sessionId}`);
// Destroy session but keep data on disk
await session.destroy();
console.log("Session destroyed (state persisted)");
// Resume the previous session
const resumed = await client.resumeSession("user-123-conversation");
console.log(`Resumed: ${resumed.sessionId}`);
await resumed.sendAndWait({ prompt: "What were we discussing?" });
// List sessions
const sessions = await client.listSessions();
console.log(
"Sessions:",
sessions.map((s) => s.sessionId)
);
// Delete session permanently
await client.deleteSession("user-123-conversation");
console.log("Session deleted");
await resumed.destroy();
await client.stop();

View File

@@ -0,0 +1,179 @@
#!/usr/bin/env tsx
import { CopilotClient } from "@github/copilot-sdk";
import { execSync } from "node:child_process";
import * as readline from "node:readline";
// ============================================================================
// Git & GitHub Detection
// ============================================================================
function isGitRepo(): boolean {
try {
execSync("git rev-parse --git-dir", { stdio: "ignore" });
return true;
} catch {
return false;
}
}
function getGitHubRemote(): string | null {
try {
const remoteUrl = execSync("git remote get-url origin", {
encoding: "utf-8",
}).trim();
// Handle SSH: git@github.com:owner/repo.git
const sshMatch = remoteUrl.match(/git@github\.com:(.+\/.+?)(?:\.git)?$/);
if (sshMatch) return sshMatch[1];
// Handle HTTPS: https://github.com/owner/repo.git
const httpsMatch = remoteUrl.match(/https:\/\/github\.com\/(.+\/.+?)(?:\.git)?$/);
if (httpsMatch) return httpsMatch[1];
return null;
} catch {
return null;
}
}
function parseArgs(): { repo?: string } {
const args = process.argv.slice(2);
const repoIndex = args.indexOf("--repo");
if (repoIndex !== -1 && args[repoIndex + 1]) {
return { repo: args[repoIndex + 1] };
}
return {};
}
async function promptForRepo(): Promise<string> {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
return new Promise((resolve) => {
rl.question("Enter GitHub repo (owner/repo): ", (answer) => {
rl.close();
resolve(answer.trim());
});
});
}
// ============================================================================
// Main Application
// ============================================================================
async function main() {
console.log("🔍 PR Age Chart Generator\n");
// Determine the repository
const args = parseArgs();
let repo: string;
if (args.repo) {
repo = args.repo;
console.log(`📦 Using specified repo: ${repo}`);
} else if (isGitRepo()) {
const detected = getGitHubRemote();
if (detected) {
repo = detected;
console.log(`📦 Detected GitHub repo: ${repo}`);
} else {
console.log("⚠️ Git repo found but no GitHub remote detected.");
repo = await promptForRepo();
}
} else {
console.log("📁 Not in a git repository.");
repo = await promptForRepo();
}
if (!repo || !repo.includes("/")) {
console.error("❌ Invalid repo format. Expected: owner/repo");
process.exit(1);
}
const [owner, repoName] = repo.split("/");
// Create Copilot client - no custom tools needed!
const client = new CopilotClient({ logLevel: "error" });
const session = await client.createSession({
model: "gpt-5",
systemMessage: {
content: `
<context>
You are analyzing pull requests for the GitHub repository: ${owner}/${repoName}
The current working directory is: ${process.cwd()}
</context>
<instructions>
- Use the GitHub MCP Server tools to fetch PR data
- Use your file and code execution tools to generate charts
- Save any generated images to the current working directory
- Be concise in your responses
</instructions>
`,
},
});
// Set up event handling
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
session.on((event) => {
if (event.type === "assistant.message") {
console.log(`\n🤖 ${event.data.content}\n`);
} else if (event.type === "tool.execution_start") {
console.log(` ⚙️ ${event.data.toolName}`);
}
});
// Initial prompt - let Copilot figure out the details
console.log("\n📊 Starting analysis...\n");
await session.sendAndWait({
prompt: `
Fetch the open pull requests for ${owner}/${repoName} from the last week.
Calculate the age of each PR in days.
Then generate a bar chart image showing the distribution of PR ages
(group them into sensible buckets like <1 day, 1-3 days, etc.).
Save the chart as "pr-age-chart.png" in the current directory.
Finally, summarize the PR health - average age, oldest PR, and how many might be considered stale.
`,
});
// Interactive loop
const askQuestion = () => {
rl.question("You: ", async (input) => {
const trimmed = input.trim();
if (trimmed.toLowerCase() === "exit" || trimmed.toLowerCase() === "quit") {
console.log("👋 Goodbye!");
rl.close();
await session.destroy();
await client.stop();
process.exit(0);
}
if (trimmed) {
await session.sendAndWait({ prompt: trimmed });
}
askQuestion();
});
};
console.log('💡 Ask follow-up questions or type "exit" to quit.\n');
console.log("Examples:");
console.log(' - "Expand to the last month"');
console.log(' - "Show me the 5 oldest PRs"');
console.log(' - "Generate a pie chart instead"');
console.log(' - "Group by author instead of age"');
console.log("");
askQuestion();
}
main().catch(console.error);