BrandGhost
GitHub Copilot CLI for .NET Developers: Real Workflows on a C# Codebase

GitHub Copilot CLI for .NET Developers: Real Workflows on a C# Codebase

If you're a senior C# developer, the interesting part of GitHub Copilot CLI for .NET isn't that it can chat in your terminal. The interesting part is whether it can participate in the same engineering loop you already trust: inspect the solution, understand ASP.NET Core conventions, write focused tests, run dotnet build, run dotnet test, review the diff, and leave you with a change you can reason about.

That is the lens for this article. Not a generic install tour. Not a slash-command encyclopedia. This is about using Copilot CLI on a real C# codebase where conventions, test boundaries, nullable reference types, analyzers, and review discipline matter.

Heads up: GitHub Copilot CLI moves fast. It went GA in February 2026 and ships updates constantly, so commands, flags, and available models change often. Everything here was verified against the CLI as of July 2026 -- always check the official GitHub Copilot CLI command reference for the very latest.

Why Copilot CLI Needs Repo-Specific .NET Rules

A .NET solution carries a lot of context that isn't obvious from a single file. Target frameworks matter. Nullable settings matter. Some teams use xUnit, some use NUnit, and some have both because history is a real thing. ASP.NET Core projects can be controller-based, Minimal API-based, Carter-based, or a mix. Integration tests might use WebApplicationFactory<Program>, Testcontainers, SQLite in-memory databases, or local emulators.

GitHub Copilot CLI for .NET becomes useful when you make that context explicit. Otherwise, you're asking an agent to infer project rules from scattered examples. Sometimes it will. Sometimes it won't. The difference between a helpful coding agent and a noisy patch generator is usually the quality of the instructions and the tightness of the validation loop.

If you're also interested in building agentic tooling yourself rather than only using the shipped terminal agent, the mental model overlaps with Build an AI CLI Developer Tool with GitHub Copilot SDK in C#. The product workflow is different, but the constraints around tool use, context, and validation are very familiar.

Setting Up GitHub Copilot CLI for .NET on Windows

GitHub's current installation docs say Windows users need PowerShell v6 or higher, and the npm install path requires Node.js 22 or later. The package is @github/copilot, and the Windows package manager ID is GitHub.Copilot. As of my live check for this article, npm reported @github/copilot version 1.0.68, with 1.0.69-2 on the prerelease tag. The npm page currently says the default model is Claude Sonnet 4.5, but model availability changes often, so use /model in the CLI to see what your account can use.

For a .NET developer on Windows, I would keep setup verification boring and explicit:

# Verify the shell and runtime prerequisites before installing.
pwsh --version
node --version
npm --version

# Install the current stable Copilot CLI package on Windows.
winget install GitHub.Copilot

# Alternative cross-platform install path. Requires Node.js 22+.
npm install -g @github/copilot

# Authenticate when needed, then launch from the repository root.
copilot login
copilot

The key .NET-specific point is where you launch it. Start the CLI from the solution repository root so the .sln, Directory.Build.props, global.json, .editorconfig, and test projects are visible. If your repo needs workloads, containers, or local secrets for tests, put that in instructions instead of repeating it in every prompt.

Encoding .NET Conventions in AGENTS.md and Copilot Instructions

GitHub Copilot supports repository-wide .github/copilot-instructions.md, path-specific .github/instructions/**/*.instructions.md, and agent instructions through AGENTS.md files. The docs frame these as related instruction sources, and the CLI command reference explicitly describes custom instructions as coming from AGENTS.md and related files. For .NET teams, these files are where you encode the stuff that senior developers normally enforce in review.

I like splitting durable engineering rules from task-specific prompting. Put durable rules in the repo. Keep the prompt focused on the change.

# .github/copilot-instructions.md

This repository is a .NET 10 ASP.NET Core solution.
Use C# nullable reference types and do not suppress warnings unless the reason is explicit.
Use xUnit for new tests. Name tests MethodUnderTest_Condition_ExpectedResult.
Prefer Minimal APIs in src/MyService.Api unless an existing controller owns the route group.
Use WebApplicationFactory<Program> for HTTP-level integration tests.
Run the smallest targeted test first, then run dotnet build before summarizing.
Never change public route contracts without calling it out in the final response.
Never run git push. Commits are local only unless a human handles the push.

That short file does a lot of work. It tells Copilot which test framework to choose, where to put API changes, how to validate, and what not to do. It also prevents the agent from inventing a preferred style when your repo already has one.

There is a tradeoff. If the instructions are too vague, the agent improvises. If they're too broad, the important constraints get lost. The best instructions are specific, durable, and easy to verify.

Prompting Patterns for ASP.NET Core Controllers and Minimal APIs

For ASP.NET Core work, I prefer prompts that name the route, the project, the test project, and the validation command. GitHub Copilot CLI supports @ file references in the interactive prompt, so you can attach the files that define the route group, controller, DTOs, or test fixture. The command reference also documents ! COMMAND for running a shell command directly, bypassing a model call, which is handy when you know exactly which dotnet command you want.

Here is the shape of a practical interactive session for a .NET Copilot CLI session:

@src/MyService.Api/Program.cs @tests/MyService.Api.Tests/WidgetEndpointTests.cs
Add GET /api/widgets/{id:int} to the existing widget route group.
Return 404 when the widget is missing and 200 with WidgetResponse when it exists.
Write the failing xUnit integration test first using the existing WebApplicationFactory fixture.
Do not add a new persistence abstraction unless the current repository contract cannot support this.

!dotnet test tests/MyService.Api.Tests/MyService.Api.Tests.csproj --filter WidgetEndpointTests
!dotnet build MyService.sln
/diff
/review Focus on route contract changes, nullability, and missing test coverage.

This is not a magic incantation. It is a compact engineering spec. The @file context points at relevant code, and the direct !dotnet commands keep you in control when you already know the validation command.

A practical ASP.NET Core prompt usually needs these ingredients:

  • The route owner: controller, route group, or endpoint extension method.
  • The route contract: URL template, status codes, DTOs, and authorization policy.
  • The test target: xUnit, NUnit, bUnit, or integration test project.
  • The validation command: the smallest dotnet test command that proves the behavior.

For controller-based projects, change the wording. Tell it which controller owns the route and whether to use attribute routing or conventional routing. For Minimal APIs, tell it which route group owns the endpoint and whether the project uses typed results, endpoint filters, validators, or mediator-style handlers.

If the endpoint touches outbound HTTP, link the agent to the existing pattern rather than letting it invent a new one. For example, this site's HttpClient in C#: The Complete Guide for .NET Developers and Mock HttpClient C#: DelegatingHandler, Mock Handlers, and Integration Testing are good reminders that testability should shape how you design the client boundary.

Generating xUnit or NUnit Tests Before Implementation

GitHub Copilot CLI for .NET is most useful when you force the test-first loop. Ask it to write the failing test, stop, show the diff, and then implement. In interactive mode, you can approve the write operation, run the targeted test, and then decide whether to continue. In prompt mode, you can use --allow-tool to allow only the commands you want for that session.

For example, if your ASP.NET Core service already has integration tests, the test should look like the existing tests, not like a tutorial sample from another architecture. A realistic test target might look like this:

using System.Net;
using System.Net.Http.Json;

using FluentAssertions;
using Xunit;

namespace MyService.Api.Tests;

public sealed class WidgetEndpointTests : IClassFixture<ApiFactory>
{
    private readonly HttpClient _client;

    public WidgetEndpointTests(ApiFactory factory)
    {
        _client = factory.CreateClient();
    }

    [Fact]
    public async Task GetWidget_WhenWidgetExists_ReturnsWidgetResponse()
    {
        var response = await _client.GetAsync("/api/widgets/42");

        response.StatusCode.Should().Be(HttpStatusCode.OK);
        var widget = await response.Content.ReadFromJsonAsync<WidgetResponse>();
        widget.Should().NotBeNull();
        widget!.Id.Should().Be(42);
    }
}

The important part is not the exact assertion library. The important part is that the agent should follow your repo's test style. If your project uses Shouldly, it should use Shouldly. If it uses NUnit, it should use NUnit. If integration tests seed data through a fixture, it should use that fixture instead of adding hidden coupling to a test database.

The upside of agent-written tests is speed. The downside is tautological coverage if the prompt is weak. Tell the agent what behavior matters to clients: status codes, response contracts, authorization, validation, logging side effects, retry behavior, and failure modes.

This connects directly to logging and diagnostics too. If the change adds observability, don't accept random Console.WriteLine calls. Keep the agent aligned with the logging stack your service already uses. For ASP.NET Core services, the guidance in Logging in .NET: The Complete Developer's Guide and How to Set Up Serilog in ASP.NET Core: Step-by-Step Guide is the kind of convention you want encoded in repo instructions.

Running dotnet build and dotnet test Through the Approval Flow

GitHub's tool permission docs matter for .NET work because dotnet test is not always harmless. Unit tests are usually fine. Integration tests might start containers, hit local services, mutate a development database, or call external APIs if the repo is poorly isolated. The CLI's approval model lets you approve tools once, approve them for the session, or reject and redirect.

For headless or scripted use, avoid broad permission flags unless you're in an isolated environment. GitHub documents --allow-tool, --deny-tool, --available-tools, and permissive flags like --allow-all-tools, --allow-all, and --yolo. For a .NET repo, a tighter pattern is usually better:

copilot -p "Add the missing xUnit tests for the WidgetService cache miss path, then implement the smallest fix. Run the targeted test project only." `
  --allow-tool='write' `
  --allow-tool='shell(dotnet test)' `
  --allow-tool='shell(dotnet build)' `
  --deny-tool='shell(git push)' `
  --no-ask-user

This pattern gives the agent enough room to edit files and validate the change, without giving it a blank check for Git history or arbitrary shell commands. Deny rules take precedence over allow rules, which is exactly what I want for operations like git push.

For package and release workflows, be even more conservative. Build and test commands are one thing. Publishing packages is another. If your repo automates release steps, treat that boundary like you would in The Complete Guide to Creating NuGet Packages in .NET: explicit commands, explicit credentials, explicit human review.

Large C# solutions are where context strategy matters. You rarely want to dump the whole repository into a prompt. Instead, start with the files that define the seam: the route mapper, the controller, the service interface, the existing tests, and shared DTOs. Use @file for those. Ask the agent to inspect before editing.

A good first prompt is: "Using @src/MyService.Api/Endpoints/WidgetsEndpoints.cs and @tests/MyService.Api.Tests/WidgetEndpointTests.cs, explain the current endpoint pattern and propose the smallest plan to add GET by id. Do not edit files yet." That keeps the agent in planning mode before edits begin.

GitHub now documents LSP server support for Copilot CLI. The docs say LSP servers can improve code intelligence for definition navigation, references, renames, diagnostics, and similar workflows. They also say Copilot CLI does not bundle language servers; you install one separately and configure it in user-level ~/.copilot/lsp-config.json or repo-level .github/lsp.json. The command reference includes /lsp show, /lsp test SERVER-NAME, and /lsp reload.

For C#, that usually means using a trusted C# language server such as OmniSharp/Roslyn or whatever your team standardizes on. The exact command depends on how you install the server, so verify it locally with /lsp test before assuming the agent has semantic navigation.

The repo-level setup flow is small:

  1. Install the C# language server from a trusted source.
  2. Add .github/lsp.json with the command and .cs file mapping.
  3. Start Copilot CLI from the repo root and run /lsp test SERVER-NAME.
  4. Use /lsp reload after changing the config during a session.
{
  "lspServers": {
    "csharp": {
      "command": "omnisharp",
      "args": ["--languageserver"],
      "fileExtensions": {
        ".cs": "csharp"
      },
      "rootUri": "."
    }
  }
}

I would treat that as a starting point, not a universal C# prescription. The official Copilot CLI docs verify the LSP configuration shape and /lsp management commands, but they do not prescribe a single C# server command. OmniSharp is a Roslyn-based C# language service and has stdio support; depending on the build, the executable might be omnisharp, OmniSharp, or OmniSharp.exe, and some installs may require different arguments. Newer Roslyn-based C# language server setups may use a different command entirely. Verify the exact command locally with /lsp test csharp before assuming Copilot CLI has semantic navigation.

End-to-End Example: Add an Endpoint, Test It, Review It, Commit It

Let's put the workflow together. The goal is small but real: add a GET /api/widgets/{id:int} endpoint to an ASP.NET Core service.

First, start the CLI from the repo root. Use Standard mode for a small change or Plan mode if the route touches multiple projects. Ask for a plan first if you are unsure about the existing architecture.

Then provide the target files with @file context and specify the behavior:

  1. Ask for the failing xUnit or NUnit test first.
  2. Approve the write and run the targeted dotnet test command.
  3. Continue to implementation only after the test fails for the expected reason.
  4. Re-run the targeted test, then run dotnet build for the solution.

If the first failure is a setup problem, fix the test harness issue before letting the agent code around it.

After implementation, run the same targeted test. Then run dotnet build for the solution. If the change affects serialization, routing, authorization, or OpenAPI metadata, run the relevant integration tests too. Do not let the agent summarize success without command output. Senior .NET developers know that "I updated the code" and "the correct test passed" are different claims.

Next, use /diff. Review the route contract, DTO shape, nullability, dependency injection changes, and test assertions. If the agent added a new abstraction, ask why. If it changed existing behavior, ask it to isolate that change or explain the compatibility impact. Use /review as a second-pass code review, but don't outsource your judgment to it.

Finally, commit locally if the diff is coherent. GitHub Copilot CLI can help draft the commit message, but the commit should describe one concern: adding the widget lookup endpoint and tests. Don't bundle formatting churn, package upgrades, or unrelated analyzer fixes into the same commit.

That end-to-end loop is why I like the CLI when the task is bounded. It compresses the mechanical work without removing the engineering checkpoints. You still decide the contract. You still evaluate the tests. You still own the diff.

Where GitHub Copilot CLI for .NET Fits

Copilot CLI is a strong fit for .NET for small-to-medium changes with clear validation: endpoints, tests, refactors with existing coverage, build fixes, dependency injection cleanup, and repository pattern alignment. It is also useful for explaining unfamiliar projects when you give it concrete files and ask for a plan before edits.

It is a weaker fit when the requirements are ambiguous, the repo has poor tests, or the task requires domain judgment that isn't represented in code. In those cases, use the CLI for investigation and plan critique, not autonomous implementation. Ask it to map the affected projects, identify likely tests, or compare implementation paths with pros and cons.

The product also sits beside, not inside, your own agent infrastructure. If you want custom Copilot-powered features in a .NET app, that's closer to the GitHub Copilot SDK for .NET. If you want a terminal-native agent to work in your existing C# repo, the terminal workflow is the path here.

My practical recommendation is simple: make the repository rules explicit, keep permissions narrow, use @file context deliberately, prefer test-first prompts, run dotnet validation through the approval model, and review every diff. That is not slower. It is how you get agent speed without pretending the agent owns the engineering outcome.

FAQ

Is Copilot CLI different from the old gh copilot commands?

Yes. The current GitHub Copilot CLI is the standalone copilot command. It is an agentic terminal coding agent that can inspect files, edit code, run tools, and help review diffs. The retired old gh copilot suggest and gh copilot explain flow is not the workflow this article covers.

Should I use AGENTS.md or .github/copilot-instructions.md for C# conventions?

Use whichever matches your team's instruction strategy, and use both when they serve different purposes. GitHub's docs say root AGENTS.md and root .github/copilot-instructions.md are both used when present. For C# codebases, put durable rules there: target framework, test framework, route style, build commands, and forbidden operations.

Can GitHub Copilot CLI run dotnet test for me?

Yes, with approval. Interactive sessions prompt before tool use when needed, and command-line sessions can use --allow-tool='shell(dotnet test)'. I prefer targeted test commands first, then broader validation once the focused tests pass.

How should I prompt GitHub Copilot CLI for ASP.NET Core Minimal APIs?

Name the route group, route template, expected status codes, DTOs, test project, and validation command. Include relevant files with @file context. For Minimal APIs, specify whether your project uses typed results, endpoint filters, validators, or route-group extension methods.

Does GitHub Copilot CLI support C# LSP configuration?

Copilot CLI supports LSP servers through ~/.copilot/lsp-config.json and .github/lsp.json, and the docs include /lsp commands for show, test, and reload. For C#, install a trusted C# language server separately, configure it, and verify it with /lsp test SERVER-NAME before relying on semantic navigation.

Should I let GitHub Copilot CLI commit .NET changes automatically?

I would separate writing from committing. Let the agent help implement and validate the change, then review /diff yourself. If the diff is coherent, a local commit is reasonable. Avoid automatic pushes and avoid commits that bundle unrelated changes.

What is the safest first workflow for a senior .NET developer?

Start with a read-only planning prompt against a few @file references. Ask for the smallest implementation plan and the targeted dotnet test command. Then allow writes and test execution only after the plan matches the architecture. That keeps GitHub Copilot CLI for .NET useful without giving it more authority than the task requires.

The Old gh copilot Is Retired: Migrating to the New GitHub Copilot CLI

Need a gh copilot suggest replacement? Migrate from the retired GitHub CLI extension to the new GitHub Copilot CLI agent workflow safely and with confidence.

GitHub Copilot CLI Agentic Workflows: Delegating Real Work to Your Terminal

GitHub Copilot CLI agentic workflows help developers plan, test, review, use /fleet, and delegate scoped feature work from the terminal with control today.

GitHub Copilot CLI: The Complete Guide to the Agentic Terminal Agent

GitHub Copilot CLI guide for senior developers: install, modes, MCP, models, headless automation, permissions, and practical .NET workflow tradeoffs today.

An error has occurred. This application may no longer respond until reloaded. Reload