This commit is contained in:
Anthony Shaw
2026-02-11 14:23:25 -08:00
parent 84486c2e46
commit ff69b804ac
5 changed files with 710 additions and 70 deletions

View File

@@ -39,7 +39,7 @@ A [Ralph loop](https://ghuntley.com/ralph/) is an autonomous development workflo
## Simple Version ## Simple Version
The minimal Ralph loop — the SDK equivalent of `while :; do cat PROMPT.md | claude ; done`: The minimal Ralph loop — the SDK equivalent of `while :; do cat PROMPT.md | copilot ; done`:
```csharp ```csharp
using GitHub.Copilot.SDK; using GitHub.Copilot.SDK;
@@ -205,9 +205,9 @@ creating ad-hoc copies.
4. When tests pass, update IMPLEMENTATION_PLAN.md, then `git add -A` 4. When tests pass, update IMPLEMENTATION_PLAN.md, then `git add -A`
then `git commit` with a descriptive message. then `git commit` with a descriptive message.
99999. When authoring documentation, capture the why. 5. When authoring documentation, capture the why.
999999. Implement completely. No placeholders or stubs. 6. Implement completely. No placeholders or stubs.
9999999. Keep IMPLEMENTATION_PLAN.md current — future iterations depend on it. 7. Keep IMPLEMENTATION_PLAN.md current — future iterations depend on it.
``` ```
### Example `AGENTS.md` ### Example `AGENTS.md`
@@ -241,12 +241,14 @@ dotnet build
## When to Use a Ralph Loop ## When to Use a Ralph Loop
**Good for:** **Good for:**
- Implementing features from specs with test-driven validation - Implementing features from specs with test-driven validation
- Large refactors broken into many small tasks - Large refactors broken into many small tasks
- Unattended, long-running development with clear requirements - Unattended, long-running development with clear requirements
- Any work where backpressure (tests/builds) can verify correctness - Any work where backpressure (tests/builds) can verify correctness
**Not good for:** **Not good for:**
- Tasks requiring human judgment mid-loop - Tasks requiring human judgment mid-loop
- One-shot operations that don't benefit from iteration - One-shot operations that don't benefit from iteration
- Vague requirements without testable acceptance criteria - Vague requirements without testable acceptance criteria

View File

@@ -39,7 +39,7 @@ A [Ralph loop](https://ghuntley.com/ralph/) is an autonomous development workflo
## Simple Version ## Simple Version
The minimal Ralph loop — the SDK equivalent of `while :; do cat PROMPT.md | claude ; done`: The minimal Ralph loop — the SDK equivalent of `while :; do cat PROMPT.md | copilot ; done`:
```go ```go
package main package main
@@ -241,9 +241,9 @@ creating ad-hoc copies.
4. When tests pass, update IMPLEMENTATION_PLAN.md, then `git add -A` 4. When tests pass, update IMPLEMENTATION_PLAN.md, then `git add -A`
then `git commit` with a descriptive message. then `git commit` with a descriptive message.
99999. When authoring documentation, capture the why. 5. When authoring documentation, capture the why.
999999. Implement completely. No placeholders or stubs. 6. Implement completely. No placeholders or stubs.
9999999. Keep IMPLEMENTATION_PLAN.md current — future iterations depend on it. 7. Keep IMPLEMENTATION_PLAN.md current — future iterations depend on it.
``` ```
### Example `AGENTS.md` ### Example `AGENTS.md`
@@ -277,12 +277,14 @@ go build ./...
## When to Use a Ralph Loop ## When to Use a Ralph Loop
**Good for:** **Good for:**
- Implementing features from specs with test-driven validation - Implementing features from specs with test-driven validation
- Large refactors broken into many small tasks - Large refactors broken into many small tasks
- Unattended, long-running development with clear requirements - Unattended, long-running development with clear requirements
- Any work where backpressure (tests/builds) can verify correctness - Any work where backpressure (tests/builds) can verify correctness
**Not good for:** **Not good for:**
- Tasks requiring human judgment mid-loop - Tasks requiring human judgment mid-loop
- One-shot operations that don't benefit from iteration - One-shot operations that don't benefit from iteration
- Vague requirements without testable acceptance criteria - Vague requirements without testable acceptance criteria

View File

@@ -39,35 +39,35 @@ A [Ralph loop](https://ghuntley.com/ralph/) is an autonomous development workflo
## Simple Version ## Simple Version
The minimal Ralph loop — the SDK equivalent of `while :; do cat PROMPT.md | claude ; done`: The minimal Ralph loop — the SDK equivalent of `while :; do cat PROMPT.md | copilot ; done`:
```typescript ```typescript
import { readFile } from "fs/promises"; import { readFile } from "fs/promises";
import { CopilotClient } from "@github/copilot-sdk"; import { CopilotClient } from "@github/copilot-sdk";
async function ralphLoop(promptFile: string, maxIterations: number = 50) { async function ralphLoop(promptFile: string, maxIterations: number = 50) {
const client = new CopilotClient(); const client = new CopilotClient();
await client.start(); await client.start();
try { try {
const prompt = await readFile(promptFile, "utf-8"); const prompt = await readFile(promptFile, "utf-8");
for (let i = 1; i <= maxIterations; i++) { for (let i = 1; i <= maxIterations; i++) {
console.log(`\n=== Iteration ${i}/${maxIterations} ===`); console.log(`\n=== Iteration ${i}/${maxIterations} ===`);
// Fresh session each iteration — context isolation is the point // Fresh session each iteration — context isolation is the point
const session = await client.createSession({ model: "gpt-5.1-codex-mini" }); const session = await client.createSession({ model: "gpt-5.1-codex-mini" });
try { try {
await session.sendAndWait({ prompt }, 600_000); await session.sendAndWait({ prompt }, 600_000);
} finally { } finally {
await session.destroy(); await session.destroy();
} }
console.log(`Iteration ${i} complete.`); console.log(`Iteration ${i} complete.`);
}
} finally {
await client.stop();
} }
} finally {
await client.stop();
}
} }
// Usage: point at your PROMPT.md // Usage: point at your PROMPT.md
@@ -87,50 +87,50 @@ import { CopilotClient } from "@github/copilot-sdk";
type Mode = "plan" | "build"; type Mode = "plan" | "build";
async function ralphLoop(mode: Mode, maxIterations: number = 50) { async function ralphLoop(mode: Mode, maxIterations: number = 50) {
const promptFile = mode === "plan" ? "PROMPT_plan.md" : "PROMPT_build.md"; const promptFile = mode === "plan" ? "PROMPT_plan.md" : "PROMPT_build.md";
const client = new CopilotClient(); const client = new CopilotClient();
await client.start(); await client.start();
console.log(`Mode: ${mode} | Prompt: ${promptFile}`); console.log(`Mode: ${mode} | Prompt: ${promptFile}`);
try { try {
const prompt = await readFile(promptFile, "utf-8"); const prompt = await readFile(promptFile, "utf-8");
for (let i = 1; i <= maxIterations; i++) { for (let i = 1; i <= maxIterations; i++) {
console.log(`\n=== Iteration ${i}/${maxIterations} ===`); console.log(`\n=== Iteration ${i}/${maxIterations} ===`);
const session = await client.createSession({ const session = await client.createSession({
model: "gpt-5.1-codex-mini", model: "gpt-5.1-codex-mini",
// Pin the agent to the project directory // Pin the agent to the project directory
workingDirectory: process.cwd(), workingDirectory: process.cwd(),
// Auto-approve tool calls for unattended operation // Auto-approve tool calls for unattended operation
onPermissionRequest: async () => ({ allow: true }), onPermissionRequest: async () => ({ allow: true }),
}); });
// Log tool usage for visibility // Log tool usage for visibility
session.on((event) => { session.on((event) => {
if (event.type === "tool.execution_start") { if (event.type === "tool.execution_start") {
console.log(`${event.data.toolName}`); console.log(`${event.data.toolName}`);
}
});
try {
await session.sendAndWait({ prompt }, 600_000);
} finally {
await session.destroy();
}
console.log(`Iteration ${i} complete.`);
} }
} finally { });
await client.stop();
try {
await session.sendAndWait({ prompt }, 600_000);
} finally {
await session.destroy();
}
console.log(`Iteration ${i} complete.`);
} }
} finally {
await client.stop();
}
} }
// Parse CLI args: npx tsx ralph-loop.ts [plan] [max_iterations] // Parse CLI args: npx tsx ralph-loop.ts [plan] [max_iterations]
const args = process.argv.slice(2); const args = process.argv.slice(2);
const mode: Mode = args.includes("plan") ? "plan" : "build"; const mode: Mode = args.includes("plan") ? "plan" : "build";
const maxArg = args.find(a => /^\d+$/.test(a)); const maxArg = args.find((a) => /^\d+$/.test(a));
const maxIterations = maxArg ? parseInt(maxArg) : 50; const maxIterations = maxArg ? parseInt(maxArg) : 50;
ralphLoop(mode, maxIterations); ralphLoop(mode, maxIterations);
@@ -182,9 +182,9 @@ creating ad-hoc copies.
4. When tests pass, update IMPLEMENTATION_PLAN.md, then `git add -A` 4. When tests pass, update IMPLEMENTATION_PLAN.md, then `git add -A`
then `git commit` with a descriptive message. then `git commit` with a descriptive message.
99999. When authoring documentation, capture the why. 5. When authoring documentation, capture the why.
999999. Implement completely. No placeholders or stubs. 6. Implement completely. No placeholders or stubs.
9999999. Keep IMPLEMENTATION_PLAN.md current — future iterations depend on it. 7. Keep IMPLEMENTATION_PLAN.md current — future iterations depend on it.
``` ```
### Example `AGENTS.md` ### Example `AGENTS.md`
@@ -219,12 +219,14 @@ npm run build
## When to Use a Ralph Loop ## When to Use a Ralph Loop
**Good for:** **Good for:**
- Implementing features from specs with test-driven validation - Implementing features from specs with test-driven validation
- Large refactors broken into many small tasks - Large refactors broken into many small tasks
- Unattended, long-running development with clear requirements - Unattended, long-running development with clear requirements
- Any work where backpressure (tests/builds) can verify correctness - Any work where backpressure (tests/builds) can verify correctness
**Not good for:** **Not good for:**
- Tasks requiring human judgment mid-loop - Tasks requiring human judgment mid-loop
- One-shot operations that don't benefit from iteration - One-shot operations that don't benefit from iteration
- Vague requirements without testable acceptance criteria - Vague requirements without testable acceptance criteria

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": "*"
},
"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

@@ -43,7 +43,7 @@ A [Ralph loop](https://ghuntley.com/ralph/) is an autonomous development workflo
## Simple Version ## Simple Version
The minimal Ralph loop — the SDK equivalent of `while :; do cat PROMPT.md | claude ; done`: The minimal Ralph loop — the SDK equivalent of `while :; do cat PROMPT.md | copilot ; done`:
```python ```python
import asyncio import asyncio
@@ -117,14 +117,17 @@ async def ralph_loop(mode: str = "build", max_iterations: int = 50):
# Pin the agent to the project directory # Pin the agent to the project directory
working_directory=str(Path.cwd()), working_directory=str(Path.cwd()),
# Auto-approve tool calls for unattended operation # Auto-approve tool calls for unattended operation
on_permission_request=lambda _req, _ctx: {"kind": "approved", "rules": []}, on_permission_request=lambda _req, _ctx: {
"kind": "approved", "rules": []
},
)) ))
# Log tool usage for visibility # Log tool usage for visibility
session.on(lambda event: def log_tool_event(event):
print(f"{event.data.tool_name}") if event.type.value == "tool.execution_start":
if event.type.value == "tool.execution_start" else None print(f"{event.data.tool_name}")
)
session.on(log_tool_event)
try: try:
await session.send_and_wait( await session.send_and_wait(
@@ -193,9 +196,9 @@ creating ad-hoc copies.
4. When tests pass, update IMPLEMENTATION_PLAN.md, then `git add -A` 4. When tests pass, update IMPLEMENTATION_PLAN.md, then `git add -A`
then `git commit` with a descriptive message. then `git commit` with a descriptive message.
99999. When authoring documentation, capture the why. 5. When authoring documentation, capture the why.
999999. Implement completely. No placeholders or stubs. 6. Implement completely. No placeholders or stubs.
9999999. Keep IMPLEMENTATION_PLAN.md current — future iterations depend on it. 7. Keep IMPLEMENTATION_PLAN.md current — future iterations depend on it.
``` ```
### Example `AGENTS.md` ### Example `AGENTS.md`
@@ -230,12 +233,14 @@ python -m pytest
## When to Use a Ralph Loop ## When to Use a Ralph Loop
**Good for:** **Good for:**
- Implementing features from specs with test-driven validation - Implementing features from specs with test-driven validation
- Large refactors broken into many small tasks - Large refactors broken into many small tasks
- Unattended, long-running development with clear requirements - Unattended, long-running development with clear requirements
- Any work where backpressure (tests/builds) can verify correctness - Any work where backpressure (tests/builds) can verify correctness
**Not good for:** **Not good for:**
- Tasks requiring human judgment mid-loop - Tasks requiring human judgment mid-loop
- One-shot operations that don't benefit from iteration - One-shot operations that don't benefit from iteration
- Vague requirements without testable acceptance criteria - Vague requirements without testable acceptance criteria