Skip to content

Add GitHub Copilot CLI support#275

Merged
umputun merged 13 commits intoumputun:masterfrom
AOrlov:add-support-of-copilot-cli
Apr 16, 2026
Merged

Add GitHub Copilot CLI support#275
umputun merged 13 commits intoumputun:masterfrom
AOrlov:add-support-of-copilot-cli

Conversation

@AOrlov
Copy link
Copy Markdown
Contributor

@AOrlov AOrlov commented Apr 9, 2026

Summary

This PR adds first-class GitHub Copilot CLI support to ralphex without changing the existing claude_command / claude_args integration path.

It introduces a wrapper that runs Copilot CLI, translates its JSONL stream into the Claude stream-json format expected by ralphex, and keeps the existing
plan / task / review flows working with minimal executor changes.

What Changed

  • Added a new wrapper under scripts/copilot-as-claude/
  • Translated Copilot CLI streaming events into Claude-compatible stream-json output
  • Passed prompts through stdin instead of -p so large prompts continue to work reliably
  • Preserved <<RALPHEX:...>> control signals in the translated stream
  • Added a review-mode adapter so Copilot can follow ralphex review prompts that refer to Claude task delegation
  • Normalized Copilot output to completed assistant messages instead of token-by-token fragments
  • Added plan-mode boundary handling so Copilot stops at:
    • <<RALPHEX:QUESTION>> ... <<RALPHEX:END>>
    • <<RALPHEX:PLAN_DRAFT>> ... <<RALPHEX:END>>
    • <<RALPHEX:PLAN_READY>>
  • Added non-plan turn truncation so Copilot does not continue autonomously into later tasks inside the same ralphex iteration
  • Added shell-based tests with a mock copilot binary
  • Added setup and usage documentation

Why

Copilot CLI is close enough to use as an alternative provider, but its native streaming and autonomy model does not match ralphex’s control flow out of the
box.

Without this wrapper:

  • output is too fragmented for readable progress logs
  • interactive plan mode can continue past question/draft/ready boundaries
  • task execution can continue into later tasks before ralphex regains control

This PR makes Copilot behave closely enough to the Codex/Claude path that ralphex can drive it safely.

Validation

Ran:

bash scripts/copilot-as-claude/copilot-as-claude_test.sh
bash scripts/copilot-as-claude/copilot-as-claude_docs_test.sh
bash -n scripts/copilot-as-claude/copilot-as-claude.sh

Notes

This is intentionally a wrapper-level integration. It keeps the current executor architecture intact and avoids introducing Copilot-specific logic into the Go task runner.

@AOrlov AOrlov requested a review from umputun as a code owner April 9, 2026 15:26
Copy link
Copy Markdown
Owner

@umputun umputun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the wrapper approach is correct and follows the existing pattern well. a few things to address:

  1. pkg/progress/progress_test.go change is unrelated to copilot support. if it was an intentional fix, pls split it into a separate PR

  2. the main loop forks 3-4 jq processes per JSONL line (validate, extract type, extract content, extract text). the codex wrapper does the entire translation in a single jq -c pipeline per line. not a blocker but worth optimizing - copilot can produce a lot of events

  3. shell tests use macOS-only commands - stat -f '%m', date -v-1H, grep -P. these won't run on linux, which means they can't run in CI or docker. the other wrapper tests avoid platform-specific commands, would be good to follow the same pattern

  4. minor - grep -oE 'docs/plans/[^ ]*\.md' for plan path extraction is fragile if the model formats the path differently. the find fallback helps but the grep could silently grab wrong text

@AOrlov AOrlov force-pushed the add-support-of-copilot-cli branch from f01da94 to 46b9614 Compare April 14, 2026 17:05
@AOrlov
Copy link
Copy Markdown
Contributor Author

AOrlov commented Apr 14, 2026

Thank you for the review. All comments were addressed.

@AOrlov AOrlov requested a review from umputun April 14, 2026 17:06
Copy link
Copy Markdown
Owner

@umputun umputun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thx for addressing the previous comments. most things look solid now, tests pass (59+25 shell, all Go tests, lint clean).

I haven't tried it with an actual copilot cli, so assuming you got it working end-to-end. the questions below are more theoretical than practical.

a few things after deeper review:

  1. jq forks - down to 2/line from 3-4, which is better. the single-pipeline approach doesn't really work here because of per-line flow control decisions (plan boundaries, turn tracking), so I can live with this

  2. plan boundary handling (lines 127-267) - none of the other wrappers (codex, gemini, opencode) implement plan signal parsing. I get why - copilot's autopilot mode runs multiple turns, so the wrapper needs to stop at boundaries. but does copilot cli have smth like --max-turns 1 or similar flag that would make this unnecessary? if the tool itself can limit to single-turn mode, ~90 lines of boundary extraction could go away

  3. touch_existing_plan_for_ready (lines 242-267) - this is coupling between the shell wrapper and Go internals (FindRecent mtime behavior). if FindRecent changes its lookup strategy, this breaks silently. at minimum add a comment explaining why the touch is needed and which Go function depends on it

  4. review adapter size - ~800 chars with a "FORMATTING RULE (strict)" section vs ~350 chars in codex adapter. is the markdown suppression rule actually needed for copilot, or is it defensive? if copilot consistently wraps output in markdown, then fine, but if not, trim it

Andrew and others added 4 commits April 15, 2026 15:59
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The skipIfClaudeNotAvailable change was unrelated to Copilot support.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Owner

@umputun umputun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thx, all prior comments addressed cleanly:

  • progress_test.go reverted
  • jq forks stable at 2/line
  • macOS-only commands gone from tests
  • plan path extraction replaced with content-compare (marker file + cmp -s), no more fragile grep
  • touch_existing_plan_for_ready removed entirely - plan_written_since_start uses a script-internal marker instead of coupling to Go FindRecent
  • plan-boundary comment at copilot-as-claude.sh:135-138 explains clearly why --max-turns wouldn't help (multiple signals in a single assistant.message, not across turns)
  • review adapter size justified by the copilot markdown-default behavior, comment at :122-124 makes it discoverable

shell tests 83/83 + 29/29, go test clean, lint clean, shellcheck clean.

lgtm.

@umputun umputun merged commit 02715b0 into umputun:master Apr 16, 2026
@AOrlov
Copy link
Copy Markdown
Contributor Author

AOrlov commented Apr 16, 2026

@umputun, thank you for reviewing it again. Honestly, I'm not completely satisfied by the fact the script is too bloated (in comparison to others) due to Copilot CLI quirks. While I see some room for improvements - it does the thing end-to-end. I will continue revising it when time allows and get back with a clean up PR soon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants