From 7e89b54ea6c6676c7a7613f7cc85e2cd582b9568 Mon Sep 17 00:00:00 2001 From: "Marcus R. Brown" Date: Fri, 24 Apr 2026 10:19:47 -0700 Subject: [PATCH] chore(ci): configure Copilot Coding Agent with setup-steps and instructions --- .github/copilot-instructions.md | 48 ++++++++++++ .github/workflows/copilot-setup-steps.yaml | 31 ++++++++ AGENTS.md | 87 ++++++++++++++++++++++ 3 files changed, 166 insertions(+) create mode 100644 .github/copilot-instructions.md create mode 100644 .github/workflows/copilot-setup-steps.yaml create mode 100644 AGENTS.md diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..6fd6b44 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,48 @@ +# Copilot Instructions + +This is an [OpenCode](https://opencode.ai) plugin written in TypeScript. It delegates tasks to GitHub Copilot CLI (`copilot -p`) as background subprocesses and reports results back through OpenCode's tool system. + +## Project Conventions + +Follow AGENTS.md for architecture, coding standards, and module boundaries. + +## Critical Rules + +- **No type suppression**: never use `as any`, `@ts-ignore`, or `@ts-expect-error` +- **No implicit any**: all function parameters and return types must be inferrable or annotated +- **Discriminated unions over optional properties**: prefer `{ type: 'success', data } | { type: 'error', message }` over `{ data?: T, error?: string }` +- **Parse, don't validate**: use Zod schemas for external input; trust internal types +- **Structured errors**: tools return `{ error: string }` objects, never throw + +## Stack + +- **Runtime**: Bun +- **Language**: TypeScript (strict mode) +- **Linting/formatting**: Biome (not ESLint/Prettier) +- **Test framework**: `bun:test` +- **Build**: `bun build` + `tsc --emitDeclarationOnly` +- **Dependencies**: `fkill` for process cleanup; `@opencode-ai/plugin` and `@opencode-ai/sdk` as peer dependencies + +## Verification Commands + +Run all four before considering work complete: + +```bash +bun test # Unit + integration tests +bun run typecheck # tsc --noEmit (strict) +bun run lint # biome check . +bun run build # Bundle + declaration emit +``` + +## Module Boundaries + +| Module | Owns | Must Not | +|--------|------|----------| +| `src/tools/` | Tool definitions, schema, execute functions | Import from other tools | +| `src/runtime/` | Subprocess management, task registry, JSONL parsing, notifications | Reference OpenCode plugin/tool APIs directly | +| `src/discovery/` | Agent file discovery, description builder | Depend on runtime or tools | +| `src/index.ts` | Plugin entrypoint, wiring | Contain business logic | + +## Changeset Policy + +This package uses `0.x` unstable versioning. User-visible changes require a `.changeset/*.md` entry with a `minor` bump (not `patch`). diff --git a/.github/workflows/copilot-setup-steps.yaml b/.github/workflows/copilot-setup-steps.yaml new file mode 100644 index 0000000..95f2e6b --- /dev/null +++ b/.github/workflows/copilot-setup-steps.yaml @@ -0,0 +1,31 @@ +--- +name: Copilot Setup Steps + +on: + workflow_dispatch: + push: + paths: [.github/workflows/copilot-setup-steps.yaml] + pull_request: + paths: [.github/workflows/copilot-setup-steps.yaml] + +permissions: + contents: read + +jobs: + copilot-setup-steps: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Setup Bun + uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2 + with: + bun-version: latest + + - name: Install dependencies + run: bun install --frozen-lockfile + + - name: Build + run: bun run build diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..66b9287 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,87 @@ +# AGENTS.md + +## What This Project Is + +`opencode-copilot-delegate` is an OpenCode plugin that spawns GitHub Copilot CLI (`copilot -p`) as background subprocesses. It exposes three tools to OpenCode sessions: + +- **`copilot_delegate`** — Spawn a Copilot CLI subprocess with a prompt and optional agent/model +- **`copilot_output`** — Retrieve results from a running or completed delegation (supports blocking with timeout) +- **`copilot_cancel`** — Cancel a running delegation and kill its process tree + +## Architecture + +``` +src/ +├── index.ts # Plugin entrypoint — wires tools to runtime +├── tools/ +│ ├── delegate.ts # copilot_delegate tool +│ ├── output.ts # copilot_output tool +│ └── cancel.ts # copilot_cancel tool +├── runtime/ +│ ├── subprocess.ts # Spawns copilot CLI, streams JSONL stdout +│ ├── task-registry.ts # In-memory task state (create/get/update/delete/cleanup) +│ ├── jsonl-parser.ts # Single-line JSONL parser for Copilot CLI output +│ ├── envelope.ts # Builds structured output envelopes from parsed events +│ └── notify.ts # Injects completion notifications into OpenCode sessions +├── discovery/ +│ ├── agents.ts # Discovers .agent.md files from Copilot agent directories +│ └── description.ts # Builds copilot_delegate tool description from discovered agents +tests/ +├── jsonl-parser.test.ts # Parser unit tests +├── envelope.test.ts # Envelope builder tests +├── subprocess.test.ts # Subprocess wrapper tests (fake copilot binary) +├── discovery.test.ts # Agent discovery tests (temp fixture dirs) +├── notify.test.ts # Notification injection tests +└── tools.test.ts # Tool integration tests (full plugin lifecycle) +tests/fixtures/ +└── jsonl/ # Real Copilot CLI JSONL captures (PII-scrubbed) +``` + +## Design Decisions + +- **Peer dependencies**: `@opencode-ai/plugin` and `@opencode-ai/sdk` are peers — the host OpenCode install provides them +- **Single-line JSONL parser**: `parseJsonlLine` handles one line at a time and returns `{ type: 'unknown' }` for malformed input. Stream-level multiline accumulation belongs in the subprocess wrapper +- **Task IDs**: prefixed with `cpl_` to distinguish from OpenCode-native task IDs +- **Process cleanup**: uses `fkill` with `{ force: false, forceTimeout: 2000, waitForExit: 5000 }` and `.catch()` guards on all `killProcessTree` calls in abort handlers +- **Notification safety**: in-flight counter is decremented synchronously (before any `await`) in close handlers; `isShuttingDown` checks gate `prompt` calls; counter map entries are deleted at zero to prevent memory leaks +- **Agent discovery**: builtin agents (bundled with Copilot CLI) cannot be overridden by user or repo agents +- **Structured errors**: tools return `{ error: string }` objects, never throw exceptions + +## Coding Standards + +### TypeScript + +- Strict mode (`strict: true` in tsconfig) +- No `as any`, `@ts-ignore`, or `@ts-expect-error` +- Prefer `satisfies` over type annotations when you want inference +- Discriminated unions over optional properties +- `const` assertions for literal types +- ESM imports only (this is a `"type": "module"` package) + +### Formatting and Linting + +Biome handles both. Configuration in `biome.json`: +- 2-space indent, single quotes, no semicolons (ASI) +- Recommended lint rules enabled + +Run `bun run lint` to check, `bun run fix` to auto-fix. + +### Testing + +- Framework: `bun:test` +- Pattern: arrange-act-assert with real filesystem fixtures (temp dirs) +- Subprocess tests use a fake `copilot` shell script that emits JSONL +- No mocking libraries — use plain functions and temp dirs +- Tests must be deterministic: no wall-clock timing assertions, use injected timestamps + +### Commits + +- Format: `feat(scope): description`, `fix(scope): description`, `chore(scope): description` +- Scopes: `runtime`, `tools`, `discovery`, `ci`, `docs` +- User-visible changes require a `.changeset/*.md` with `minor` bump (unstable `0.x` series) + +## Security Constraints + +- No secrets, PATs, tokens, or PII in tool return values or log output +- Copilot CLI prompts are visible in `ps` output (upstream limitation) — avoid delegating prompts containing secrets +- Process environment is inherited from the parent OpenCode process (trusted local chain)