Introduce TUI server mode so ask_user can be intercepted without replacing entire TUI
Nobody has claimed this yet.
Assessment
- Difficulty
- 5/5
- Estimated time
- Over a week
- Newbie friendliness
- 35/100
Research direction
Start in dotnet/src/Types.cs and Client.cs, especially StartCliServerAsync, the port-discovery loop, and the stderr capture loop. Confirm the CLI flag, TCP routing for userInput.request, and port behavior with the CLI team before changing the SDK. Done means TuiServerMode is validated, launches the intended mode, preserves terminal output, and has tests for its option handling.
Written by the indexing model from the issue text.
Description
According to copilot analysis, there's a --ui-server switch in the sources of the SDK that could be used to preserve the full copilot TUI while intercepting events via TCP.
Unknowns to raise with the CLI team
-
CLI flag name — SDK source comments say --ui-server; copilot --help shows --acp. Which flag actually starts "TUI+server" mode? Are they the same thing?
-
ask_user routing — This is the key question. When the CLI is in TUI+server mode AND an SDK client is connected via TCP, does the CLI:
a. Route
userInput.requestto the SDK client (over TCP), letting the SDK handler intercept it? ← what you need
b. Handleask_userin the TUI itself and not send userInput.request to the SDK at all?If it's currently (b), a small CLI-side change would be needed: when an SDK client is connected and has registered a userInput.request handler, prefer routing to the client.
-
Port announcement — When running without --headless, does the CLI still write "Listening on port N" to stdout (now the terminal)? That's fine to leave in — users won't mind seeing
it once on startup — but the SDK approach above bypasses it entirely by requiring an explicit port.
Proposal: Add TuiServerMode to CopilotClientOptions. Launches the CLI with its full terminal UX intact (streaming, colors, tool indicators, etc.) while the SDK connects via TCP to intercept only specific callbacks — specifically OnUserInputRequest — replacing the CLI's own prompt with a custom UX (e.g. a native popup). All stdout/stderr flow through to the user's terminal unchanged.
The SDK diff is ~30 lines. The only thing that may require CLI cooperation is whether userInput.request is already routed to TCP clients in TUI+server mode.
SDK changes needed (dotnet/src/)
- Types.cs — new option on CopilotClientOptions
/// <summary>
/// When true, launches the CLI in TUI+server mode (--ui-server) instead of
/// headless mode. The CLI renders its own terminal UX while the SDK connects
/// via TCP to intercept callbacks such as <see cref="SessionConfig.OnUserInputRequest"/>.
/// Requires <see cref="Port"/> to be set (stdout is not available for port discovery).
/// Mutually exclusive with <see cref="UseStdio"/>.
/// </summary>
public bool TuiServerMode { get; set; }
- Client.cs — StartCliServerAsync, args
- args.AddRange(["--headless", "--no-auto-update", "--log-level", options.LogLevel]);
+ if (options.TuiServerMode)
+ args.Add("--ui-server"); // ← CLI flag name TBD (see unknowns)
+ else
+ args.Add("--headless");
+ args.AddRange(["--no-auto-update", "--log-level", options.LogLevel]);
- Client.cs — StartCliServerAsync, ProcessStartInfo
var startInfo = new ProcessStartInfo
{
...
- RedirectStandardInput = options.UseStdio,
- RedirectStandardOutput = true,
- RedirectStandardError = true,
- CreateNoWindow = true,
+ RedirectStandardInput = options.UseStdio && !options.TuiServerMode,
+ RedirectStandardOutput = !options.TuiServerMode,
+ RedirectStandardError = !options.TuiServerMode,
+ CreateNoWindow = !options.TuiServerMode,
};
- Client.cs — StartCliServerAsync, port discovery
- var detectedLocalhostTcpPort = (int?)null;
- if (!options.UseStdio)
- {
- // reads port announcement from stdout...
- }
+ var detectedLocalhostTcpPort = options.TuiServerMode
+ ? options.Port // must be pre-configured; stdout is the user's terminal
+ : await DetectPortFromStdoutAsync(options, cliProcess, cancellationToken);
(extract the existing port-reading loop into DetectPortFromStdoutAsync)
- Client.cs — validation in constructor
+ if (options.TuiServerMode && options.UseStdio)
+ throw new ArgumentException("TuiServerMode is mutually exclusive with UseStdio");
+ if (options.TuiServerMode && options.Port <= 0)
+ throw new ArgumentException("TuiServerMode requires Port to be set explicitly");
The stderr capture loop (lines ~1247–1265) is also skipped when TuiServerMode — otherwise it would try to read from an unredirected stream.
- Dominant language
- Java
- Stars
- 10.5k
- Forks
- 1.5k
- Avg merge
- 1d 9h
- Merged PRs (30d)
- 129
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from github/copilot-sdk
-
agentic-workflows
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
github/copilot-sdk#2760 · 1 comment ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 65/100
github/copilot-sdk#2759 ·
-
documentation
Difficulty 1/5 Under an hour Newbie friendliness 85/100
github/copilot-sdk#2758 ·
-
agentic-workflows
Difficulty 2/5 1-3 hours Newbie friendliness 68/100
github/copilot-sdk#2709 · 1 comment ·
-
Difficulty 1/5 Under an hour Newbie friendliness 78/100
github/copilot-sdk#2673 ·
All issues in github/copilot-sdk
Similar issues
-
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
HL7/fhir-ig-publisher#1375 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 74/100
-
Flaky: a relaunched catch-up replay can still report catching up right after its marker is written Openbug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
johanhaleby/occurrent#1134 ·
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
objectionary/jeo-maven-plugin#1811 ·
-
Difficulty 2/5 1-3 hours Newbie friendliness 88/100