Skip to content

Latest commit

 

History

History
318 lines (231 loc) · 12.7 KB

File metadata and controls

318 lines (231 loc) · 12.7 KB

Copilot Instructions for copilot-sdk-java

A Java SDK for programmatic control of GitHub Copilot CLI. This is a port of the official .NET SDK, targeting Java 17+.

About These Instructions

These instructions guide GitHub Copilot when assisting with this repository. They cover:

  • Tech Stack: Java 17+, Maven, Jackson for JSON, JUnit for testing
  • Purpose: Provide a Java SDK for programmatic control of GitHub Copilot CLI
  • Architecture: JSON-RPC client communicating with Copilot CLI over stdio
  • Key Goals: Maintain parity with upstream .NET SDK while following Java idioms

Build & Test Commands

# Build and run all tests
mvn clean verify

# Run a single test class
mvn test -Dtest=CopilotClientTest

# Run a single test method
mvn test -Dtest=ToolsTest#testToolInvocation

# Format code (required before commit)
mvn spotless:apply

# Check formatting only
mvn spotless:check

# Build without tests
mvn clean package -DskipTests

# Run tests with debug logging
mvn test -Pdebug

Running Tests from AI Agents / Copilot

When running tests to verify changes, always use mvn verify without -q and without piping through grep. The full output is needed to diagnose failures. Do NOT use commands like:

# BAD - hides critical failure details, often requires a second run
mvn verify -q 2>&1 | grep -E 'Tests run:|BUILD'
mvn verify 2>&1 | grep -E 'Tests run.*in com|BUILD|test failure'

Instead, use one of these approaches:

# GOOD - run tests with full output (preferred for investigating failures)
mvn verify

# GOOD - run tests showing just the summary and result using Maven's built-in log level
mvn verify -B --fail-at-end 2>&1 | tail -30

# GOOD - run a single test class when debugging a specific test
mvn test -Dtest=CopilotClientTest

Interpreting results:

  • BUILD SUCCESS at the end means all tests passed. No further investigation needed.
  • BUILD FAILURE means something failed. The lines immediately above BUILD FAILURE will contain the relevant error information. Look for [ERROR] lines near the bottom of the output.
  • The Surefire summary line Tests run: X, Failures: Y, Errors: Z, Skipped: W appears near the end and gives the counts.
  • Do NOT run tests a second time just to get different output formatting. One run with full output is sufficient.

Architecture

Core Components

  • CopilotClient - Main entry point. Manages connection to Copilot CLI server via JSON-RPC over stdio. Spawns CLI process or connects to existing server.
  • CopilotSession - Represents a conversation session. Handles event subscriptions, tool registration, permissions, and message sending.
  • JsonRpcClient - Low-level JSON-RPC protocol implementation using Jackson for serialization.

Package Structure

  • com.github.copilot.sdk - Core classes (CopilotClient, CopilotSession, JsonRpcClient)
  • com.github.copilot.sdk.json - DTOs, request/response types, handler interfaces (SessionConfig, MessageOptions, ToolDefinition, etc.)
  • com.github.copilot.sdk.events - Event types for session streaming (AssistantMessageEvent, SessionIdleEvent, ToolExecutionStartEvent, etc.)

Test Infrastructure

Tests use the official copilot-sdk test harness from https://github.com/github/copilot-sdk. The harness is automatically cloned during generate-test-resources phase to target/copilot-sdk/.

  • E2ETestContext - Manages test environment with CapiProxy for deterministic API responses
  • CapiProxy - Node.js-based replaying proxy using YAML snapshots from test/snapshots/
  • Test snapshots are stored in the upstream repo's test/snapshots/ directory

Key Conventions

Upstream Merging

This SDK tracks the official .NET implementation at github/copilot-sdk. The .lastmerge file contains the last merged upstream commit hash. Use the agentic-merge-upstream skill (see .github/prompts/agentic-merge-upstream.prompt.md) to port changes.

When porting from .NET:

  • Adapt to Java idioms, don't copy C# patterns directly
  • Convert async/awaitCompletableFuture
  • Convert C# properties → Java getters/setters or fluent setters
  • Use Jackson for JSON (ObjectMapper, @JsonProperty)

Code Style

  • 4-space indentation (enforced by Spotless with Eclipse formatter)
  • Fluent setter pattern for configuration classes (e.g., new SessionConfig().setModel("gpt-5").setTools(tools))
  • Public APIs require Javadoc (enforced by Checkstyle, except json and events packages)
  • Pre-commit hook runs mvn spotless:check - Must be manually enabled with: git config core.hooksPath .githooks, except in the Copilot coding agent environment. This hook is explicitly enabled in the Copilot coding agent environment. See copilot-setup-steps.yml.

Handler Pattern

Handlers use functional interfaces with CompletableFuture returns:

session.createSession(new SessionConfig()
    .setOnPermissionRequest((request, invocation) -> 
        CompletableFuture.completedFuture(new PermissionRequestResult().setKind("allow")))
    .setOnUserInput((request, invocation) -> 
        CompletableFuture.completedFuture(new UserInputResponse().setResponse("user input")))
);

Event Handling

Sessions emit typed events via session.on():

session.on(AssistantMessageEvent.class, msg -> System.out.println(msg.getData().content()));
session.on(SessionIdleEvent.class, idle -> done.complete(null));

Sealed Event Hierarchy

AbstractSessionEvent is a sealed class permitting specific event types. Use pattern matching:

switch (event) {
    case AssistantMessageEvent msg -> handleMessage(msg);
    case ToolExecutionStartEvent tool -> handleToolStart(tool);
    case SessionIdleEvent idle -> handleIdle();
    default -> { }
}

Tool Definition Pattern

Custom tools use ToolDefinition.create() with JSON Schema parameters and a ToolHandler:

var tool = ToolDefinition.create(
    "get_weather",
    "Get weather for a location",
    Map.of(
        "type", "object",
        "properties", Map.of("location", Map.of("type", "string")),
        "required", List.of("location")
    ),
    invocation -> {
        // Type-safe: invocation.getArgumentsAs(WeatherArgs.class)
        // Or Map-based: invocation.getArguments().get("location")
        return CompletableFuture.completedFuture(result);
    }
);

Testing Conventions

E2E Test Structure

Tests extend the shared context pattern:

private static E2ETestContext ctx;

@BeforeAll
static void setup() throws Exception {
    ctx = E2ETestContext.create();
}

@AfterAll
static void teardown() throws Exception {
    if (ctx != null) ctx.close();
}

@Test
void testFeature() throws Exception {
    ctx.configureForTest("category", "test_name");  // Loads test/snapshots/category/test_name.yaml
    try (CopilotClient client = ctx.createClient()) {
        // Test logic
    }
}

Snapshot Naming

Test method names are converted to lowercase snake_case for snapshot filenames to avoid case collisions on macOS/Windows.

JSON Serialization

  • Uses Jackson with @JsonProperty annotations
  • @JsonInclude(JsonInclude.Include.NON_NULL) on DTOs to omit null fields
  • ObjectMapper configured via JsonRpcClient.getObjectMapper() with:
    • JavaTimeModule for date/time handling
    • FAIL_ON_UNKNOWN_PROPERTIES = false for forward compatibility

Documentation

  • Site docs in src/site/markdown/ (filtered for ${project.version} substitution)
  • Update src/site/site.xml when adding new documentation pages
  • Javadoc required for public APIs except json and events packages (self-documenting DTOs)
  • Copilot CLI Version: When updating the required Copilot CLI version in README.md, also update it in src/site/markdown/index.md to keep them in sync

Boundaries and Restrictions

What NOT to Modify

  • DO NOT edit .github/agents/ directory - these contain instructions for other agents
  • DO NOT modify target/ directory - this contains build artifacts
  • DO NOT edit pom.xml dependencies without careful consideration - this SDK has minimal dependencies by design
  • DO NOT change the Jackson version without testing against all serialization patterns
  • DO NOT modify test snapshots in target/copilot-sdk/test/snapshots/ - these come from upstream
  • DO NOT alter the Eclipse formatter configuration in pom.xml without team consensus
  • DO NOT remove or skip Checkstyle or Spotless checks

Security Guidelines

  • NEVER commit secrets, API keys, tokens, or credentials to the repository
  • NEVER commit .env files or any files containing sensitive configuration
  • NEVER log sensitive data in code (API keys, tokens, user data)
  • Always use try-with-resources for streams and readers to prevent resource leaks
  • Always use StandardCharsets.UTF_8 when creating InputStreamReader/OutputStreamWriter
  • Review any new dependencies for known security vulnerabilities before adding them
  • When handling user input in tools or handlers, consider injection risks

Dependency Management

This SDK is designed to be lightweight with minimal dependencies:

  • Core dependencies: Jackson (JSON), JUnit (tests only)
  • Before adding new dependencies:
    1. Consider if the functionality can be implemented without a new dependency
    2. Check if Jackson already provides the needed functionality
    3. Ensure the dependency is actively maintained and widely used
    4. Verify compatibility with Java 17+
    5. Check for security vulnerabilities
    6. Get team approval for non-trivial additions

Pre-commit Hooks and Formatting (Coding Agent)

The repository has a pre-commit hook (.githooks/pre-commit) that is automatically enabled in the Copilot coding agent environment via copilot-setup-steps.yml. The hook runs mvn spotless:check on any commit that includes changes under src/.

If a commit fails due to the pre-commit hook:

  1. Run mvn spotless:apply to auto-fix formatting issues.
  2. Re-stage the changed files with git add -u.
  3. Retry the commit.

Best practice: Always run mvn spotless:apply before committing Java source changes to avoid hook failures in the first place. If you forget and the hook rejects the commit, follow the three steps above and continue.

Commit and PR Guidelines

Commit Messages

  • Use clear, descriptive commit messages
  • Start with a verb in present tense (e.g., "Add", "Fix", "Update", "Refactor")
  • Keep the first line under 72 characters
  • Add details in the body if needed
  • Examples:
    • Add support for streaming responses
    • Fix resource leak in JsonRpcClient
    • Update documentation for tool handlers
    • Refactor event handling to use sealed classes

Pull Requests

  • Keep PRs focused and minimal - one feature/fix per PR
  • Ensure all tests pass before requesting review
  • Run mvn spotless:apply before committing
  • Include tests for new functionality
  • Update documentation if adding/changing public APIs
  • Reference related issues using #issue-number
  • For upstream merges, follow the agentic-merge-upstream skill workflow

Development Workflow

  1. Setup: Enable git hooks with git config core.hooksPath .githooks
  2. Branch: Create feature branches from main
  3. Code: Write code following the conventions above
  4. Format: Run mvn spotless:apply to format code
  5. Test: Run mvn clean verify to ensure all tests pass
  6. Commit: Make focused commits with clear messages
  7. Push: Push your branch and create a PR
  8. Review: Address review feedback and iterate

Release Process

The release process is automated via the publish-maven.yml GitHub Actions workflow. Key steps:

  1. CHANGELOG Update: The script .github/scripts/release/update-changelog.sh automatically:

    • Converts the ## [Unreleased] section to ## [version] - date
    • Creates a new empty ## [Unreleased] section at the top
    • Updates version comparison links at the bottom of CHANGELOG.md
    • Injects the upstream SDK commit hash (from .lastmerge) as a > **Upstream sync:** blockquote in both the new [Unreleased] section and the released version section
  2. Upstream Sync Tracking: Each release records which commit from the official github/copilot-sdk it is synced to:

    • The .lastmerge file is read during the release workflow
    • The commit hash is injected into CHANGELOG.md under the release heading
    • Format: > **Upstream sync:** [\github/copilot-sdk@SHORT_HASH`](link-to-commit)`
  3. Documentation Updates: README.md and jbang-example.java are updated with the new version.

  4. Maven Release: Uses maven-release-plugin to:

    • Update pom.xml version
    • Create a git tag
    • Deploy to Maven Central
  5. Rollback: If the release fails, the documentation commit is automatically reverted

The workflow is triggered manually via workflow_dispatch with optional version parameters.