Add Java SDK cookbook with 7 recipes

Add complete Java cookbook matching the pattern of existing .NET, Go,
Node.js, and Python cookbooks. All 7 recipes included:

- Ralph Loop: Autonomous AI task loops with JBang
- Error Handling: try-with-resources, ExecutionException, timeouts
- Multiple Sessions: Parallel sessions with CompletableFuture
- Managing Local Files: AI-powered file organization
- PR Visualization: Interactive PR age charts
- Persisting Sessions: Save/resume with custom IDs
- Accessibility Report: WCAG reports via Playwright MCP

Each recipe includes both markdown documentation and a standalone
JBang-runnable Java file in recipe/.
This commit is contained in:
Bruno Borges
2026-04-06 13:51:45 -04:00
parent 5f3d66c380
commit 46f6f3e6db
18 changed files with 2216 additions and 2 deletions

View File

@@ -0,0 +1,123 @@
///usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS com.github:copilot-sdk-java:0.2.1-java.1
import com.github.copilot.sdk.*;
import com.github.copilot.sdk.events.*;
import com.github.copilot.sdk.json.*;
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
/**
* Accessibility Report Generator — analyzes web pages using the Playwright MCP server
* and generates WCAG-compliant accessibility reports.
*
* Usage:
* jbang AccessibilityReport.java
*/
public class AccessibilityReport {
public static void main(String[] args) throws Exception {
System.out.println("=== Accessibility Report Generator ===\n");
var reader = new BufferedReader(new InputStreamReader(System.in));
System.out.print("Enter URL to analyze: ");
String url = reader.readLine().trim();
if (url.isEmpty()) {
System.out.println("No URL provided. Exiting.");
return;
}
if (!url.startsWith("http://") && !url.startsWith("https://")) {
url = "https://" + url;
}
System.out.printf("%nAnalyzing: %s%n", url);
System.out.println("Please wait...\n");
try (var client = new CopilotClient()) {
client.start().get();
// Configure Playwright MCP server for browser automation
var mcpConfig = new McpServerConfig()
.setType("local")
.setCommand("npx")
.setArgs(List.of("@playwright/mcp@latest"))
.setTools(List.of("*"));
var session = client.createSession(
new SessionConfig()
.setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
.setModel("claude-opus-4.6")
.setStreaming(true)
.setMcpServers(Map.of("playwright", mcpConfig))
).get();
// Stream output token-by-token
var idleLatch = new CountDownLatch(1);
session.on(AssistantMessageDeltaEvent.class,
ev -> System.out.print(ev.getData().deltaContent()));
session.on(SessionIdleEvent.class,
ev -> idleLatch.countDown());
session.on(SessionErrorEvent.class, ev -> {
System.err.printf("%nError: %s%n", ev.getData().message());
idleLatch.countDown();
});
String prompt = """
Use the Playwright MCP server to analyze the accessibility of this webpage: %s
Please:
1. Navigate to the URL using playwright-browser_navigate
2. Take an accessibility snapshot using playwright-browser_snapshot
3. Analyze the snapshot and provide a detailed accessibility report
Format the report with emoji indicators:
- 📊 Accessibility Report header
- ✅ What's Working Well (table with Category, Status, Details)
- ⚠️ Issues Found (table with Severity, Issue, WCAG Criterion, Recommendation)
- 📋 Stats Summary (links, headings, focusable elements, landmarks)
- ⚙️ Priority Recommendations
Use ✅ for pass, 🔴 for high severity issues, 🟡 for medium severity, ❌ for missing items.
Include actual findings from the page analysis.
""".formatted(url);
session.send(new MessageOptions().setPrompt(prompt));
idleLatch.await();
System.out.println("\n\n=== Report Complete ===\n");
// Prompt user for test generation
System.out.print("Would you like to generate Playwright accessibility tests? (y/n): ");
String generateTests = reader.readLine().trim();
if (generateTests.equalsIgnoreCase("y") || generateTests.equalsIgnoreCase("yes")) {
var testLatch = new CountDownLatch(1);
session.on(SessionIdleEvent.class,
ev -> testLatch.countDown());
String testPrompt = """
Based on the accessibility report you just generated for %s,
create Playwright accessibility tests in Java.
Include tests for: lang attribute, title, heading hierarchy, alt text,
landmarks, skip navigation, focus indicators, and touch targets.
Use Playwright's accessibility testing features with helpful comments.
Output the complete test file.
""".formatted(url);
System.out.println("\nGenerating accessibility tests...\n");
session.send(new MessageOptions().setPrompt(testPrompt));
testLatch.await();
System.out.println("\n\n=== Tests Generated ===");
}
session.close();
}
}
}

View File

@@ -0,0 +1,34 @@
///usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS com.github:copilot-sdk-java:0.2.1-java.1
import com.github.copilot.sdk.*;
import com.github.copilot.sdk.events.*;
import com.github.copilot.sdk.json.*;
import java.util.concurrent.ExecutionException;
public class ErrorHandling {
public static void main(String[] args) {
try (var client = new CopilotClient()) {
client.start().get();
var session = client.createSession(
new SessionConfig()
.setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
.setModel("gpt-5")).get();
session.on(AssistantMessageEvent.class,
msg -> System.out.println(msg.getData().content()));
session.sendAndWait(
new MessageOptions().setPrompt("Hello!")).get();
session.close();
} catch (ExecutionException ex) {
System.err.println("Error: " + ex.getCause().getMessage());
ex.getCause().printStackTrace();
} catch (Exception ex) {
System.err.println("Error: " + ex.getMessage());
ex.printStackTrace();
}
}
}

View File

@@ -0,0 +1,64 @@
///usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS com.github:copilot-sdk-java:0.2.1-java.1
import com.github.copilot.sdk.CopilotClient;
import com.github.copilot.sdk.events.AssistantMessageEvent;
import com.github.copilot.sdk.events.SessionIdleEvent;
import com.github.copilot.sdk.events.ToolExecutionCompleteEvent;
import com.github.copilot.sdk.events.ToolExecutionStartEvent;
import com.github.copilot.sdk.json.MessageOptions;
import com.github.copilot.sdk.json.PermissionHandler;
import com.github.copilot.sdk.json.SessionConfig;
import java.nio.file.Paths;
import java.util.concurrent.CountDownLatch;
public class ManagingLocalFiles {
public static void main(String[] args) throws Exception {
try (var client = new CopilotClient()) {
client.start().get();
// Create session
var session = client.createSession(
new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL).setModel("gpt-5")).get();
// Set up event handlers
var done = new CountDownLatch(1);
session.on(AssistantMessageEvent.class, msg ->
System.out.println("\nCopilot: " + msg.getData().content())
);
session.on(ToolExecutionStartEvent.class, evt ->
System.out.println(" → Running: " + evt.getData().toolName())
);
session.on(ToolExecutionCompleteEvent.class, evt ->
System.out.println(" ✓ Completed: " + evt.getData().toolCallId())
);
session.on(SessionIdleEvent.class, evt -> done.countDown());
// Ask Copilot to organize files - using a safe example folder
// For real use, replace with your target folder
String targetFolder = args.length > 0 ? args[0] :
System.getProperty("java.io.tmpdir") + "/example-files";
String prompt = String.format("""
Analyze the files in "%s" and show how you would organize them into subfolders.
1. First, list all files and their metadata
2. Preview grouping by file extension
3. Suggest appropriate subfolders (e.g., "images", "documents", "videos")
IMPORTANT: DO NOT move any files. Only show the plan.
""", targetFolder);
session.send(new MessageOptions().setPrompt(prompt));
// Wait for completion
done.await();
session.close();
}
}
}

View File

@@ -0,0 +1,37 @@
///usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS com.github:copilot-sdk-java:0.2.1-java.1
import com.github.copilot.sdk.*;
import com.github.copilot.sdk.json.*;
import java.util.concurrent.CompletableFuture;
public class MultipleSessions {
public static void main(String[] args) throws Exception {
try (var client = new CopilotClient()) {
client.start().get();
var config = new SessionConfig()
.setModel("gpt-5")
.setOnPermissionRequest(PermissionHandler.APPROVE_ALL);
// Create 3 sessions in parallel
var f1 = client.createSession(config);
var f2 = client.createSession(config);
var f3 = client.createSession(new SessionConfig()
.setModel("claude-sonnet-4.5")
.setOnPermissionRequest(PermissionHandler.APPROVE_ALL));
CompletableFuture.allOf(f1, f2, f3).get();
var s1 = f1.get(); var s2 = f2.get(); var s3 = f3.get();
// Send a message to each session
System.out.println("S1: " + s1.sendAndWait(new MessageOptions().setPrompt("Explain Java records")).get().getMessage());
System.out.println("S2: " + s2.sendAndWait(new MessageOptions().setPrompt("Explain sealed classes")).get().getMessage());
System.out.println("S3: " + s3.sendAndWait(new MessageOptions().setPrompt("Explain pattern matching")).get().getMessage());
// Clean up
s1.destroy().get(); s2.destroy().get(); s3.destroy().get();
client.stop().get();
}
}
}

View File

@@ -0,0 +1,182 @@
///usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS com.github:copilot-sdk-java:0.2.1-java.1
import com.github.copilot.sdk.CopilotClient;
import com.github.copilot.sdk.events.AssistantMessageEvent;
import com.github.copilot.sdk.events.ToolExecutionStartEvent;
import com.github.copilot.sdk.json.MessageOptions;
import com.github.copilot.sdk.json.PermissionHandler;
import com.github.copilot.sdk.json.SessionConfig;
import com.github.copilot.sdk.json.SystemMessageConfig;
import java.io.BufferedReader;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.regex.Pattern;
public class PRVisualization {
public static void main(String[] args) throws Exception {
System.out.println("🔍 PR Age Chart Generator\n");
// Determine the repository
String repo;
if (args.length > 0) {
repo = args[0];
System.out.println("📦 Using specified repo: " + repo);
} else if (isGitRepo()) {
String detected = getGitHubRemote();
if (detected != null && !detected.isEmpty()) {
repo = detected;
System.out.println("📦 Detected GitHub repo: " + repo);
} else {
System.out.println("⚠️ Git repo found but no GitHub remote detected.");
repo = promptForRepo();
}
} else {
System.out.println("📁 Not in a git repository.");
repo = promptForRepo();
}
if (repo == null || !repo.contains("/")) {
System.err.println("❌ Invalid repo format. Expected: owner/repo");
System.exit(1);
}
String[] parts = repo.split("/", 2);
String owner = parts[0];
String repoName = parts[1];
// Create Copilot client
try (var client = new CopilotClient()) {
client.start().get();
String cwd = System.getProperty("user.dir");
var systemMessage = String.format("""
<context>
You are analyzing pull requests for the GitHub repository: %s/%s
The current working directory is: %s
</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>
""", owner, repoName, cwd);
var session = client.createSession(
new SessionConfig().setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
.setModel("gpt-5")
.setSystemMessage(new SystemMessageConfig().setContent(systemMessage))
).get();
// Set up event handling
session.on(AssistantMessageEvent.class, msg ->
System.out.println("\n🤖 " + msg.getData().content() + "\n")
);
session.on(ToolExecutionStartEvent.class, evt ->
System.out.println(" ⚙️ " + evt.getData().toolName())
);
// Initial prompt - let Copilot figure out the details
System.out.println("\n📊 Starting analysis...\n");
String prompt = String.format("""
Fetch the open pull requests for %s/%s 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.
""", owner, repoName);
session.send(new MessageOptions().setPrompt(prompt));
// Wait a bit for initial processing
Thread.sleep(10000);
// Interactive loop
System.out.println("\n💡 Ask follow-up questions or type \"exit\" to quit.\n");
System.out.println("Examples:");
System.out.println(" - \"Expand to the last month\"");
System.out.println(" - \"Show me the 5 oldest PRs\"");
System.out.println(" - \"Generate a pie chart instead\"");
System.out.println(" - \"Group by author instead of age\"");
System.out.println();
try (var reader = new BufferedReader(new InputStreamReader(System.in))) {
while (true) {
System.out.print("You: ");
String input = reader.readLine();
if (input == null) break;
input = input.trim();
if (input.isEmpty()) continue;
if (input.equalsIgnoreCase("exit") || input.equalsIgnoreCase("quit")) {
System.out.println("👋 Goodbye!");
break;
}
session.send(new MessageOptions().setPrompt(input));
Thread.sleep(2000); // Give time for response
}
}
session.close();
}
}
// ============================================================================
// Git & GitHub Detection
// ============================================================================
private static boolean isGitRepo() {
try {
Process proc = Runtime.getRuntime().exec(new String[]{"git", "rev-parse", "--git-dir"});
return proc.waitFor() == 0;
} catch (Exception e) {
return false;
}
}
private static String getGitHubRemote() {
try {
Process proc = Runtime.getRuntime().exec(new String[]{"git", "remote", "get-url", "origin"});
try (BufferedReader reader = new BufferedReader(new InputStreamReader(proc.getInputStream()))) {
String remoteURL = reader.readLine();
if (remoteURL == null) return null;
remoteURL = remoteURL.trim();
// Handle SSH: git@github.com:owner/repo.git
var sshPattern = Pattern.compile("git@github\\.com:(.+/.+?)(?:\\.git)?$");
var sshMatcher = sshPattern.matcher(remoteURL);
if (sshMatcher.find()) {
return sshMatcher.group(1);
}
// Handle HTTPS: https://github.com/owner/repo.git
var httpsPattern = Pattern.compile("https://github\\.com/(.+/.+?)(?:\\.git)?$");
var httpsMatcher = httpsPattern.matcher(remoteURL);
if (httpsMatcher.find()) {
return httpsMatcher.group(1);
}
}
} catch (Exception e) {
// Ignore
}
return null;
}
private static String promptForRepo() throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
System.out.print("Enter GitHub repo (owner/repo): ");
String line = reader.readLine();
if (line == null) {
throw new EOFException("End of input while reading repository name");
}
return line.trim();
}
}

View File

@@ -0,0 +1,34 @@
///usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS com.github:copilot-sdk-java:0.2.1-java.1
import com.github.copilot.sdk.*;
import com.github.copilot.sdk.events.*;
import com.github.copilot.sdk.json.*;
public class PersistingSessions {
public static void main(String[] args) throws Exception {
try (var client = new CopilotClient()) {
client.start().get();
// Create a session with a custom ID so we can resume it later
var session = client.createSession(
new SessionConfig()
.setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
.setSessionId("user-123-conversation")
.setModel("gpt-5")
).get();
session.on(AssistantMessageEvent.class,
msg -> System.out.println(msg.getData().content()));
session.sendAndWait(new MessageOptions()
.setPrompt("Let's discuss TypeScript generics")).get();
System.out.println("\nSession ID: " + session.getSessionId());
System.out.println("Session closed — data persisted to disk.");
// Close session but keep data on disk for later resumption
session.close();
}
}
}

View File

@@ -0,0 +1,71 @@
# Runnable Recipe Examples
This folder contains standalone, executable Java examples for each cookbook recipe. Each file can be run directly with [JBang](https://www.jbang.dev/) — no project setup required.
## Prerequisites
- Java 17 or later
- JBang installed:
```bash
# macOS (using Homebrew)
brew install jbangdev/tap/jbang
# Linux/macOS (using curl)
curl -Ls https://sh.jbang.dev | bash -s - app setup
# Windows (using Scoop)
scoop install jbang
```
For other installation methods, see the [JBang installation guide](https://www.jbang.dev/download/).
## Running Examples
Each `.java` file is a complete, runnable program. Simply use:
```bash
jbang <FileName>.java
```
### Available Recipes
| Recipe | Command | Description |
| -------------------- | ------------------------------------ | ------------------------------------------ |
| Error Handling | `jbang ErrorHandling.java` | Demonstrates error handling patterns |
| Multiple Sessions | `jbang MultipleSessions.java` | Manages multiple independent conversations |
| Managing Local Files | `jbang ManagingLocalFiles.java` | Organizes files using AI grouping |
| PR Visualization | `jbang PRVisualization.java` | Generates PR age charts |
| Persisting Sessions | `jbang PersistingSessions.java` | Save and resume sessions across restarts |
| Ralph Loop | `jbang RalphLoop.java` | Autonomous AI task loop |
| Accessibility Report | `jbang AccessibilityReport.java` | WCAG accessibility report generator |
### Examples with Arguments
**PR Visualization with specific repo:**
```bash
jbang PRVisualization.java github/copilot-sdk
```
**Managing Local Files with specific folder:**
```bash
jbang ManagingLocalFiles.java /path/to/your/folder
```
**Ralph Loop in planning mode:**
```bash
jbang RalphLoop.java plan 5
```
## Why JBang?
JBang lets you run Java files as scripts — no `pom.xml`, no `build.gradle`, no project scaffolding. Dependencies are declared inline with `//DEPS` comments and resolved automatically.
## Learning Resources
- [JBang Documentation](https://www.jbang.dev/documentation/guide/latest/)
- [GitHub Copilot SDK for Java](https://github.com/github/copilot-sdk-java)
- [Parent Cookbook](../README.md)

View File

@@ -0,0 +1,55 @@
///usr/bin/env jbang "$0" "$@" ; exit $?
//DEPS com.github:copilot-sdk-java:0.2.1-java.1
import com.github.copilot.sdk.*;
import com.github.copilot.sdk.events.*;
import com.github.copilot.sdk.json.*;
import java.nio.file.*;
/**
* Simple Ralph Loop — reads PROMPT.md and runs it in a fresh session each iteration.
*
* Usage:
* jbang RalphLoop.java # defaults: PROMPT.md, 50 iterations
* jbang RalphLoop.java PROMPT.md 20 # custom prompt file, 20 iterations
*/
public class RalphLoop {
public static void main(String[] args) throws Exception {
String promptFile = args.length > 0 ? args[0] : "PROMPT.md";
int maxIterations = args.length > 1 ? Integer.parseInt(args[1]) : 50;
System.out.printf("Ralph Loop — prompt: %s, max iterations: %d%n", promptFile, maxIterations);
try (var client = new CopilotClient()) {
client.start().get();
String prompt = Files.readString(Path.of(promptFile));
for (int i = 1; i <= maxIterations; i++) {
System.out.printf("%n=== Iteration %d/%d ===%n", i, maxIterations);
// Fresh session each iteration — context isolation is the point
var session = client.createSession(
new SessionConfig()
.setOnPermissionRequest(PermissionHandler.APPROVE_ALL)
.setModel("gpt-5.1-codex-mini")
.setWorkingDirectory(System.getProperty("user.dir"))
).get();
// Log tool usage for visibility
session.on(ToolExecutionStartEvent.class,
ev -> System.out.printf(" ⚙ %s%n", ev.getData().toolName()));
try {
session.sendAndWait(new MessageOptions().setPrompt(prompt)).get();
} finally {
session.close();
}
System.out.printf("Iteration %d complete.%n", i);
}
}
System.out.println("\nAll iterations complete.");
}
}