Skip to main content

Lab: Copilot CLI: Zero to Hero

Duration: ~1.5–2 hours | Level: Beginner → Intermediate | Prerequisites: Active GitHub Copilot subscription, Node.js 22+, Docker Desktop 4.58+ (for Exercise 10)

Objective

In this lab you will build a complete Task Manager REST API from scratch — using only GitHub Copilot CLI as your coding partner. You won't open an editor. Every file, test, bug fix, and commit happens through the terminal.

Along the way you'll learn installation, interactive chat, plan mode, non-interactive scripting (copilot -p), custom instructions, code review, IDE integration, security sandboxing, and more — all in context, as you need them.

Copilot CLI is evolving fast

Features and commands change frequently. Run /changelog inside the CLI or check the official docs if something looks different from what you see below.


What You'll Build

A Task Manager REST API with:

  • Express.js server with CRUD endpoints (GET, POST, PUT, DELETE)
  • Input validation with Zod
  • Jest test suite with >80% coverage
  • Error handling middleware
  • Custom Copilot instructions that enforce your coding standards
  • Git history with AI-generated commit messages
  • A code review pass that catches real bugs
  • A React frontend (via plugin) connected to the API
  • End-to-end Playwright tests generated by Copilot

Exercise 1 — Install & Launch

1.1 Install the CLI

Choose the method that fits your platform. Official documentation is here.

curl -fsSL https://gh.io/copilot-install | bash

1.2 Create Your Project Directory

mkdir task-manager-api && cd task-manager-api
git init

1.3 Launch Copilot CLI

copilot

On first launch you'll see an animated banner. If prompted, authenticate:

/login

Follow the browser flow to sign in with your GitHub account.

note

You will also be prompted to trust the folder you are in. This is a security measure to prevent Copilot from running in untrusted locations. You can choose to trust the current folder, or a parent folder if you plan to use Copilot in multiple projects.

1.4 Verify Everything Works

Try these commands inside the CLI:

/version
/model

/version shows your installed version. /model lets you pick an AI model — Choose Claude Sonnet 4.6 with medium thinking for this lab.

tip

Run /update if your version is more than a few weeks old — features land fast... Almost daily!

✅ Checkpoint

You should have: Copilot CLI installed, authenticated, running inside an empty task-manager-api/ Git repo.

Exercise 2 — Scaffold with Plan Mode

Instead of writing boilerplate yourself, you'll use Plan Mode to have Copilot design and build the project skeleton.

2.1 Switch to Plan Mode

Press Shift+Tab to cycle from interactive to plan mode. You'll see the mode indicator change in your prompt.

2.2 Describe What You Want

Prompt with the following text and watch as it executes. This will take a minute or less.

Create a Node.js REST API project for a task manager. Use Express.js and Zod for validation. Set up the project with:
- package.json with scripts for start, dev, test, and lint
- src/index.js as the entry point (port 3000)
- src/routes/tasks.js with full CRUD routes (GET /, GET /:id, POST /, PUT /:id, DELETE /:id)
- src/middleware/errorHandler.js for centralized error handling
- An in-memory array as the data store (no database needed)
- Jest configured for testing
- A .gitignore for Node.js

2.3 Review the Plan

Copilot will present a step-by-step plan — which files to create, what each will contain, and which commands to run. Read through it:

  • Does the file structure make sense?
  • Are the dependencies correct?
  • Anything you'd change?

You can ask follow-up questions before approving. Select Suggest Changes to modify the plan:

Can you also add input validation on the POST and PUT routes using Zod schemas?

2.4 Approve and Execute

Once you're satisfied, select Accept plan and build on autopilot (recommended). Note that autopilot may still need to have /experimental features enabled. When prompted, select Enable all permissions (recommended). Copilot will now execute the entire plan — creating files, writing code, and running commands — without asking for approval at each step!

  1. Create project structure and each file
  2. Run npm init and npm install
  3. Create and run tests, fixing any issues until they pass

Watch the output in real time as it builds.

2.5 Verify the Scaffold

Use the ! prefix to run shell commands without leaving Copilot:

!ls src/
!cat package.json
!npm start

Hit Ctrl+C or Esc to stop the server after confirming it starts.

✅ Checkpoint

You should have a running Express server with route files, error handling middleware, and a valid package.json. All created by Copilot — you didn't write a line yourself!

Exercise 3 — Build the API Interactively

Now switch back to interactive mode (press Shift+Tab) and flesh out the API by chatting with Copilot.

3.1 Reset Allowed Tools

We just used autopilot for the scaffold, which enabled all permissions. For interactive mode, let's reset to base permissions so we require approval for each action:

/reset-allowed-tools

3.2 Add Task Validation

Reference specific files using @ mentions:

Look at @src/routes/tasks.js — add Zod validation for the POST and PUT routes. A task should have: title (string, required, 1-100 chars), description (string, optional, max 500 chars), and status (enum: "todo", "in-progress", "done", defaults to "todo").

Copilot will show you the proposed changes as a diff. Review and approve. Notice that Copilot prompts you for each step to approve changes and command execution? This is the core of interactive mode — you have full control and can guide the process at every step.

3.3 Improve Error Handling

Update @src/middleware/errorHandler.js to handle Zod validation errors. Specifically, return a 400 with the validation details. Also handle 404s for missing tasks.

3.4 Add Request Logging

Add simple request logging middleware to @src/index.js that logs the method, URL, and response time for each request.

3.5 Test It Manually

!npm start

In another terminal (or use Ctrl+C to stop, then use Copilot):

!curl -s http://localhost:3000/tasks
!curl -s -X POST http://localhost:3000/tasks -H "Content-Type: application/json" -d '{"title": "Learn Copilot CLI"}'
!curl -s http://localhost:3000/tasks
note

Using a terminal such as Ghostty allows you to split your screen and run the server in one pane while sending curl requests from another. Super useful for running servers, testing endpoints, and watching logs in real time without leaving the terminal.

3.6 Explore What Copilot Built

Ask Copilot to explain its own work:

Summarize the current architecture of this project. What does each file do?

✅ Checkpoint

You have a working API with validation, error handling, and logging — built through conversation. You've learned @ file mentions, the diff-and-approve workflow, and interactive Q&A.

Exercise 4 — Add Custom Instructions

Custom instructions teach Copilot your team's conventions. Let's see the difference they make.

4.1 Initialize Instructions

Use the /init command to create a baseline instruction file:

/init

This creates a .github/copilot-instructions.md file. Review the file and see what it's created. (Note you can use !cat .github/copilot-instructions.md to view it without leaving the CLI.)

That's a good start, but let's write our own!

Create a .github/copilot-instructions.md with these project rules:

- Language: JavaScript (ES2022+, no TypeScript)
- Framework: Express.js with Zod validation
- Testing: Jest with describe/it blocks, aim for >80% coverage
- Error handling: Always use the AppError pattern from src/middleware/errorHandler.js
- Style: Use const over let, arrow functions, async/await (no callbacks)
- Naming: camelCase for variables/functions, PascalCase for classes
- All route handlers must validate input before processing
- Include JSDoc comments on all exported functions

If you want to be specific you could state to overwrite the existing file. Otherwise it will likely attempt to merge the instructions.

Run /restart to have Copilot reload the instructions.

4.2 See It in Action

Now ask Copilot to add a new feature. Notice how it follows your instructions:

Add a PATCH /tasks/:id/status endpoint that only updates the status field.  Follow the project conventions.

Check the output — it should use const, arrow functions, Zod validation, and JSDoc comments.

4.3 View Active Instructions

/instructions

This shows which instruction files Copilot is currently using. Note that it won't show instructions created in the middle of a session until you restart or reload.

4.4 Add Path-Specific Instructions (Optional)

For larger projects, you can scope instructions to specific paths:

Create .github/instructions/tests.instructions.md with frontmatter "applyTo: **/*.test.js" and these rules:
- Use describe/it blocks (not test())
- Each describe block should have beforeEach for setup
- Test both success and error cases
- Use meaningful test names that describe behavior

✅ Checkpoint

Copilot now follows your project conventions automatically. Every response reflects your style guide.

Exercise 5 — Break It, Then Fix It

Real development involves debugging. Let's introduce a bug and use Copilot to find and fix it.

5.1 Introduce a Bug

Ask Copilot to make a specific (intentional) mistake:

In @src/routes/tasks.js, change the DELETE route so it uses findIndex but forgets to check if the index is -1 before splicing. Just make the change, don't fix the bug.

5.2 Watch It Fail

First, start the server:

!npm start

In another terminal (or in a new Copilot prompt while keeping the server running), create a couple of tasks so we have something to inadvertently delete:

!curl -s -X POST http://localhost:3000/tasks -H "Content-Type: application/json" -d '{"title": "Task 1"}'
!curl -s -X POST http://localhost:3000/tasks -H "Content-Type: application/json" -d '{"title": "Task 2"}'

Now try to delete a task with a nonexistent ID:

!curl -s -X DELETE http://localhost:3000/tasks/nonexistent-id
!curl -s http://localhost:3000/tasks

The server won't crash or error, but you'll notice that one of your existing tasks is gone — silently deleted. The bug: when findIndex returns -1 (not found), calling splice(-1, 1) removes the last item in the array. A subtle but serious bug!

5.3 Ask Copilot to Find It

I think there's a bug in the DELETE endpoint. Review @src/routes/tasks.js and find any issues.

Copilot should identify the missing bounds check and propose a fix.

Prompt Yes to apply the fix!

5.4 Use /review for a Broader Check

/review

The code review agent scans all your uncommitted changes and flags issues — not just the one you know about. It may find additional improvements. If its asking for approval too much you may want to auto-approve. This is a fairly small codebase and it's unlikely to find much for issues.

note

You can also prompt here for specific feedback, e.g. "/review Review for security issues" or "/review Review for performance improvements".

5.5 Use /diff to See All Changes

/diff

Scroll left to right to see the different file changes.

This shows a summary of everything that's changed since your last commit. Good habit before committing.

✅ Checkpoint

You found and fixed a bug using conversational debugging and the /review agent. You also used /diff to review changes.

Exercise 6 — Automate with copilot -p

The -p flag sends a single prompt and outputs the result — no interactive session. This is the key to scripting with Copilot.

6.1 Generate a Commit Message

First, exit the interactive session (Ctrl+C twice or /exit). Notice you get a link to resume your past session! Also be aware you can just run copilot --resume to see a list of previous sessions!

Have Copilot generate a commit message based on the staged changes:

git add -A
git diff --cached | copilot -p "Write a concise conventional commit message for these changes."

If you like the output, use it! Note you need to allow the tool call for interacting with git. We could probably limit this to just git commit instead of allowing all git commands, but for now we'll allow all git commands.

git diff --cached | copilot --allow-tool='shell(git:*)' -p "Write a concise conventional commit message for these changes. And git commit with that message."

You can take a look at the generated commit with git log.

6.2 Generate a README

Note here we have used a least privileged approach by only allowing the specific tool call needed to write the README file. This is a good practice to control unexpected behavior.

copilot --allow-tool='write(README.md)' -p "Look at the files in this project and generate a comprehensive README.md with: project description, setup instructions, API endpoint documentation with curl examples, and how to run tests. Write the output to README.md."

This may take a minute! Review the generated README.md with cat README.md. This is a great way to create documentation based on your code.

6.3 Generate Test Scaffolding

Beyond allowing a specific write, we can also specify the model to use for the task.

mkdir -p src/routes/__tests__
cat src/routes/tasks.js | copilot --allow-tool='write(src/routes/__tests__/tasks.test.js)' --model claude-haiku-4.5 -p "Generate a comprehensive Jest test file for this Express router. Test all CRUD operations, validation errors, and edge cases. Use supertest for HTTP assertions. Write the test file to src/routes/__tests__/tasks.test.js"

6.4 Pipe Errors for Diagnosis

npm test 2>&1 | copilot --allow-tool='shell(npm:*)' -p "These are test results. Explain any failures and suggest fixes." 2>/dev/null

The 2>/dev/null at the end suppresses Copilot's verbose execution output, leaving only the analysis.

6.5 Batch Operations in a Script

Create a simple automation script:

copilot --allow-tool='write(scripts/pre-commit.sh), shell(mkdir), shell(chmod)' -p "Write a shell script called scripts/pre-commit.sh that:
1. Runs npm test
2. If tests pass, generates a conventional commit message from the staged diff
3. Prints the suggested message and asks for confirmation
Output ONLY the shell script content."

Review the code that was generated in scripts/pre-commit.sh.

✅ Checkpoint

You can now use copilot -p for scripting, CI/CD, commit messages, and batch automation — all non-interactively.

Exercise 7 — Review and Ship

Let's get this code committed, reviewed, and ready to ship.

7.1 Re-enter Interactive Mode

copilot

7.2 Add Tests (if you haven't already)

Run the existing tests with `npm test`. If there are failures, fix them. If coverage is below 80%, add more tests to `src/routes/__tests__/tasks.test.js`.

Let Copilot iterate: run tests → see failures → fix → repeat.

tip

You can use /allow-all to speed up this iterative process since you're actively monitoring it. Just remember to reset permissions afterward for safety.

7.3 Final Code Review

/review

Address any issues the review agent finds. Then check what you're about to commit:

/diff

7.4 Commit Everything

These may already be changed, but just in case, add everything:

!git status
!git add -A

Use Copilot to write the commit message:

Look at the staged changes with `git diff --cached` and write a conventional commit message. Then run `git commit` with that message.

7.5 Share Your Session

Export your session as a Markdown file or GitHub Gist — useful for documentation or showing teammates how you built something.

/share
tip

When you /exit your session you will see statistics about the number of premium requests and tokens per model used. You also get a link to the session to resume later if needed.

7.6 Push to GitHub

Push this repo to GitHub in order to prepare for the next exercise where you'll delegate work to the GitHub agent. If you haven't already, create a new bare repo on GitHub called your-username-task-manager-api and push your code there. Use a GitHub organization and not a personal account because we're going to use Coding Agent which doesn't work with personal accounts.

git remote add origin git@github.com:your-org/your-username-task-manager-api.git
git push -u origin main

✅ Checkpoint

Your project is tested, reviewed, committed, and ready to ship. You've used Copilot as a complete development workflow — from first line to review.

Exercise 8 — Delegate to GitHub Copilot Coding Agent

You've completed your task manager API locally. Now imagine new requirements arrive: add task filtering by status and a /stats endpoint. Instead of building locally, you'll use plan mode to design it, then delegate to the Copilot Coding Agent on GitHub — which executes the work asynchronously and opens a PR.

8.1 Why Delegate?

Local development is great for learning and hands-on control. But delegation unlocks:

AspectLocal ExecutionGitHub Delegation
ExecutionBlocks your terminalRuns async on GitHub
SecurityRuns on your machineIsolated cloud environment
SpeedYou monitor each stepAgent works while you're offline
CollaborationChanges only locallyPR enables team review & discussion
Audit TrailGit historyFull PR with reasoning & decisions logged

Use delegation when: You have a well-scoped feature, clear instructions, and want async execution with built-in code review.

8.2 Switch to Plan Mode

Start a new CLI session and press Shift+Tab to enter plan mode. This time, instead of approving and executing locally, you'll use the plan to hand off to the agent.

8.3 Describe the New Feature

Add two new features:

1. GET /tasks?status=todo — filter tasks by status (query parameter)
2. GET /tasks/stats — return { total, byStatus: { todo, inProgress, done }, lastCreated }

Both should:
- Follow existing code conventions
- Have tests with >80% coverage
- Update existing tests if needed
- Be documented in JSDoc comments

Plan the changes but don't execute yet.

Copilot creates a step-by-step plan. Review it. Feel free to ask follow-up questions or request changes to the plan.

Once satisfied, select Exit plan mode and I will prompt myself. We want to delegate the plan to Coding Agent!

8.4 Delegate the Plan

Instead of executing locally, run:

/delegate

Copilot will:

  1. Prompt if you have open changes to commit or push
  2. Push a branch to your GitHub repo
  3. Trigger the Copilot Coding Agent on GitHub to execute the plan
  4. Open a draft PR for the work
  5. Stream logs of the session to the CLI in real time
  6. Give you the option to start a new session rather than watch the agent work
important

You can also watch the agent session on GitHub.com. Go to your repo and click the Agents tab to see the live session (or review a past session). You can also get to this by going to the Pull requests tab, opening the pull request, and clicking View Session

8.5 Review the PR

Once the agent completes the work (typically within minutes), you'll see:

  • Commits showing each step of the plan
  • Changed files with diffs
  • PR description explaining what was built and why
  • Test results showing coverage and passing tests

You can also assign Copilot to review its work. In the reviewers section of the pull request, assign Copilot and it will review the changes.

✅ Checkpoint

You've used the plan → delegate → PR workflow. The agent executed features asynchronously while you were free to do other work. The changes are now part of your PR history and ready to review.

Exercise 9 — Customization with Plugins

Copilot CLI supports several customization mechanisms that shape how Copilot behaves, what tools it can use, and how it integrates with external systems. In this exercise you'll learn what each mechanism does and the CLI commands to manage them — then see how plugins bundle everything together for instant productivity.

tip

Skills, custom agents, hooks, and MCP servers are covered in depth in the Copilot Customization Workshop. Here we'll focus on the CLI-specific commands and then use a plugin to put them all into practice.

9.1 Customization Mechanisms

Skills

Skills are portable, reusable capabilities defined in SKILL.md files. When Copilot's prompt matches a skill's description, it's automatically invoked — injecting specialized instructions, scripts, and resources into the conversation.

WhatWhere
Project skills.github/skills/<skill-name>/SKILL.md
User-wide skills~/.copilot/skills/<skill-name>/SKILL.md

Key CLI commands:

/skills list          # See all loaded skills
/skills info <name> # View a skill's details
/skills add # Add a skill interactively
/skills remove <name> # Remove a skill
/skills reload # Reload after changes

Custom Agents

Custom agents are specialized AI personas defined in Markdown files. Each agent can have its own model, tool restrictions, and domain expertise. Copilot can auto-delegate to them or you can invoke them directly.

WhatWhere
Project agents.github/agents/<name>.agent.md
User-wide agents~/.copilot/agents/<name>.agent.md

Key CLI commands:

/agent                         # Browse and select from available agents
copilot --agent=<name> # Launch CLI with a specific agent (from terminal)

Copilot includes built-in agents: code-review, explore, general-purpose, research, and task.

Hooks

Hooks execute custom shell commands at key lifecycle points during a session — enabling validation, logging, security scanning, or workflow automation. They're defined as JSON files and auto-loaded from .github/hooks/.

Hook EventWhen It FiresCan Block?
sessionStartNew or resumed session beginsNo
preToolUseBefore each tool executesYes — allow, deny, or modify
postToolUseAfter each tool completesNo
agentStopMain agent finishes a turnYes — can force continuation
subagentStopA subagent completesYes — can force continuation

There are two hook types:

  • Command hooks — run shell scripts (e.g., log every command, scan for secrets before file writes)
  • Prompt hooks — auto-submit text on sessionStart (e.g., always run /compact at the start of a session)

Hook files live in .github/hooks/*.json and are loaded automatically — there's no slash command to manage them.

MCP Servers

MCP (Model Context Protocol) servers give Copilot access to external tools and data sources — databases, APIs, browser automation, and more.

Key CLI commands:

/mcp show              # List configured servers and their status
/mcp add # Add a new MCP server interactively
/mcp edit <name> # Edit an existing server
/mcp delete <name> # Remove a server
/mcp disable <name> # Temporarily disable without removing
/mcp enable <name> # Re-enable a disabled server

Copilot CLI includes four built-in MCP servers that work out of the box:

ServerWhat It Does
githubGitHub API — issues, PRs, commits, code search, Actions
playwrightBrowser automation — navigate, click, type, screenshot
fetchHTTP requests via the fetch tool
timeTime utilities — get current time, convert between zones

User-configured servers are stored in ~/.copilot/mcp-config.json. Repository-level config goes in .github/mcp.json.

9.2 Simplify with Plugins

Creating skills, agents, hooks, and MCP configs individually is powerful. However, it is much more convenient to use plugins to bundle them all together for common use cases. This also allows the community (either public or internal to your company) to share and reuse capabilities.

tip

Here we will use the awesome-copilot plugin marketplace. It is included in the CLI by default. However, you can also create your own private plugin registry for your organization or team. It is basically just a repository with json metadata. See the docs for details on how to do that.

Install the frontend-web-dev plugin from the awesome-copilot plugin marketplace:

/plugin install frontend-web-dev@awesome-copilot
note

You can also do this from the terminal with copilot plugin install PLUGIN-NAME@MARKETPLACE-NAME if you prefer not to use the interactive prompt.

Now see what was added:

/skills list
/agent

You should see new capabilities that the plugin brought in:

TypeNameWhat It Does
Skillplaywright-explore-websiteWebsite exploration for testing using Playwright MCP
Skillplaywright-generate-testGenerate Playwright tests from scenario descriptions
AgentExpert React Frontend EngineerExpert React 19 engineer — modern hooks, Server Components, TypeScript, performance
AgentElectron Code Review Mode InstructionsElectron + Angular code review specialist

Compare this to setting up each piece manually. The plugin can handle agents, skills, MCP servers, LSP, and hooks integration in one command.

Plugin management commands:

/plugin list                           # See installed plugins
/plugin uninstall frontend-web-dev # Remove a plugin
/plugin update frontend-web-dev # Update to latest version

9.3 Build a React Frontend

Now let's put the plugin to work. Use the expert-react-frontend-engineer agent to build a frontend for your Task Manager API:

Use the expert-react-frontend-engineer agent to create a React frontend for the Task Manager API. It should have:
- A task list view that fetches from http://localhost:3000/tasks
- A form to create new tasks (title, description, status)
- Status filter buttons (All, Todo, In Progress, Done)
- Edit and delete functionality for each task
- Clean, modern styling

Scaffold it in a `frontend/` directory using Vite. Include a proxy config so API calls to /tasks go to localhost:3000.

Let Copilot scaffold the frontend. It will likely run in the background and you can use /tasks to check on progress.

Once it's done you you can start both servers and verify they connect. Open 2 terminals for this project and start the backend in one and then the frontend in the other.

# In the root directory: 
npm start

# In the frontend directory:
cd frontend && npm start

Open http://localhost:5173 in your browser. You should see the task manager UI connected to your Express backend.

9.4 Generate E2E Tests with Playwright

With both servers running, use the plugin's Playwright skills to generate end-to-end tests:

First, explore the running application:

Explore the app with playwright-explore-website

When prompted, provide the URL http://localhost:5173. You may want to allow all calls and actions to this URL for this current session. Copilot will use the built-in Playwright MCP server and open a browser to navigate the app and understand its structure.

It's possible it finds a bug when exploring! You could ask it to fix the bug if you want. Or you could just continue wiht test generation.

Generate tests for key user flows. You probably don't need to mention the skill by name here since the agent should know to use it based on the task, but you can if you want to be specific:

Generate tests for key user flows with playwright-generate-test

You may be prompted to describe the scenario or it may choose automatically based on exploration. If prompted, here is a sample to describe the scenario:

Generate Playwright tests for these flows:
1. Create a new task with title "Buy groceries" and verify it appears in the list
2. Filter tasks by "todo" status and verify only matching tasks show
3. Delete a task and verify it's removed from the list

It has likely already run the tests, but you can run them any time now. Make certain you are in the frontend directory for this as follows:

! cd frontend && npx playwright test 
note

If Playwright isn't installed yet, Copilot will likely handle the setup. If not, run !npx playwright install first.

✅ Checkpoint

You now have a brief understanding of the customization mechanisms and their CLI commands. You installed a plugin that bundled agents and skills together, used them to build a React frontend, and generated end-to-end Playwright tests. All from the CLI.

Exercise 10 — Orchestration, Permission Controls, and Sandbox

Throughout this lab you've been approving tool calls one by one or occasionally using /allow-all. Now let's understand the full permission model, learn how to run Copilot safely in autonomous mode, and explore parallel workflows.

10.1 Allow-All / YOLO Mode

The --allow-all flag (aliased as --yolo) combines three permissions into one:

FlagWhat It Allows
--allow-all-toolsSkip tool approval — Copilot runs any command without asking
--allow-all-pathsDisable file path verification — access any file on your system
--allow-all-urlsDisable URL verification — fetch any URL without approval

In an interactive session, the equivalent slash commands are /allow-all or /yolo.

When it's appropriate:

  • You're actively watching the session and can Ctrl+C at any time
  • Working in a trusted, throwaway environment (dev container, sandbox)
  • Running well-tested automation you've verified before

The risks are real:

  • Copilot can run rm -rf ./, git push --force, or any destructive command
  • It can read sensitive files like .env, SSH keys, or credentials
  • It can fetch arbitrary URLs and exfiltrate data

That's not saying it is likely to do those things, but it has the power to if you give it permission. Generally your dev laptop likely has a lot of sensitive data and access to important systems, so you want to be careful with these permissions.

10.2 Granular Permission Controls

Instead of all-or-nothing, use permission patterns for least-privilege access:

Permission pattern syntax:

# Allow specific commands
copilot --allow-tool='shell(git:*)' # All git subcommands
copilot --allow-tool='shell(npm test)' # Only npm test

# Allow file writes to specific paths
copilot --allow-tool='write(src/*.js)' # Only JS files in src/
copilot --allow-tool='write(README.md)' # Only README.md

# Allow specific MCP server tools
copilot --allow-tool='github(create_issue)' # Only create_issue from GitHub MCP
copilot --allow-tool='playwright' # All Playwright tools

# Allow URL access
copilot --allow-tool='url(github.com)' # Only github.com
copilot --allow-tool='url(https://*.github.com)' # Any GitHub subdomain

# Combine allow + deny for precise control
copilot --allow-all-tools \
--deny-tool='shell(rm)' \
--deny-tool='shell(git push)' \
--deny-tool='shell(git push --force)'

Practical example: Create a shell alias for a "safe mode" that blocks destructive commands:

alias copilot-safe='copilot --allow-all-tools \
--deny-tool="shell(rm)" \
--deny-tool="shell(git push)" \
--deny-tool="shell(git reset)" \
--deny-tool="shell(git checkout -- .)"'

You can also limit which tools are available at all with --available-tools:

# Only allow file viewing and grep — no editing, no shell
copilot --available-tools='view, grep, glob' -p "Analyze this codebase for security issues"

So how do you manage this at scale? There are a few approaches:

  1. Just keep a long alias or shell wrapper script included in your PATH. This is simple but can get unwieldy with many rules.
  2. Use a custom hook that runs on preToolUse. This allows you to write a shell script that evaluates each tool call and decides whether to allow or deny it based on your custom logic. You could even integrate with an external policy engine or database for dynamic rules. This could be shared across the team as a common security policy.
  3. Use the --available-tools flag to create a "profile" of allowed tools for different scenarios. For example, you could have a "read-only" profile that only allows viewing files and making HTTP requests, and a "full-access" profile that allows everything. Then use aliases or scripts to launch Copilot with the appropriate profile based on the task at hand.

10.3 Isolation Options

Beyond permissions, you could isolate Copilot from your main environment entirely. Here you could opt to allow all permissions because you are isolating the agent in a sandbox that has no access to your sensitive data or important systems. This is a great way to safely run in autonomous mode without having to worry about the agent doing something unexpected on your machine.

Some ideal solutions that are currently available include:

  • GitHub Copilot Coding Agent - This runs on GitHub infrastructure and can be delegated to from your local CLI. It has access to the code in your GitHub repo but has firewall rules preventing external access unless allowed.
  • Codespaces - This gives you an isolated VSCode environment where you can run multiple CLI sessions.
  • Separate Dev Machine or VM - You could use a separate physical machine or a virtual machine that you snapshot and can revert if needed. For Example, an Azure VM or AWS EC2 instance that you use solely for running Copilot CLI sessions.

GitHub is also working on other isolation options so expect this space to evolve.

One solution we will explore below is Docker Sandboxes. These are experimental and provide microVM isolation. Nvidia recently open sources a solution called OpenShell aiming to solve a similar problem.

10.3.1 Docker Sandbox

For maximum isolation, run Copilot CLI inside a Docker Sandbox. This confines all file access, shell commands, and network traffic to an isolated microVM. You can use --yolo and still prevent destructive actions because of an isolated filesystem and network controls.

note

Docker Sandboxes require Docker Desktop 4.58 or later and are currently experimental (macOS and Windows).

Quick start:

docker sandbox run copilot ~/task-manager-api

This creates a microVM sandbox, syncs your workspace into it, and starts Copilot CLI inside the isolated environment.

For this lab you will need to run /login within the sandbox to authenticate with GitHub via OAUTH. That might get irritating to do lots of times so you can also set a global token in your shell config which will be picked up by the Docker Sandbox daemon.

Passing CLI options can be done by using -- to separate sandbox args from Copilot args:

docker sandbox run my-sandbox -- --yolo

Network policies can be configured for fine-grained controls. By default, sandboxes block access to private networks and localhost.

# Denylist mode: block everything except what you allow
docker sandbox network proxy my-sandbox \
--policy deny \
--allow-host "*.npmjs.org" \
--allow-host github.com \
--allow-host "*.githubusercontent.com"

# Monitor what the agent is accessing
docker sandbox network log

For more details, see the Docker Sandbox network policies documentation.

Overall this is one option available for you to move fast with fewer permission prompts while still maintaining safety through isolation. It is a bit more setup and overhead than just running locally, so consider if simpler solutions like offloading work to Copilot Coding Agent may be more appropriate for your use case.

10.4 Git Worktrees for Parallel CLI Sessions

The CLI opens up the opportunity to have multiple threads running in parallel. This is great for efficiency, but when they are all in the same repo they will likely step on each others toes. To avoid this, you need separate working directories. Git worktrees solve this without cloning the repo multiple times.

A quick review of git worktrees: they allow you to have multiple working directories attached to the same Git repository. Each worktree can be on a different branch, and they all share the same Git history and object database. This is perfect for running parallel CLI sessions that need to read/write files without conflicts.

Limitations to be aware of:

  • All worktrees share the same Git index and object database, so they are not completely isolated. Changes in one worktree can affect others if they modify the same files or branches.
  • You cannot have two worktrees checked out to the same branch at the same time. Each worktree must be on a different branch.
  • Deleting a worktree does not delete the branch it was on, so you need to manage branches separately.
  • Each worktree needs its own node_modules if you're doing npm installs, since they are separate directories.
  • Easy to forget about and accumulate worktrees, so make sure to clean up when done. (use git worktree list to see them all)

Create worktrees for parallel work:

# From your main task-manager-api directory
git worktree add ../task-manager-feature-a -b feature-a
git worktree add ../task-manager-feature-b -b feature-b

Each worktree is a full working directory with its own checked-out branch, but they share the same Git history.

Run separate CLI sessions:

# Terminal 1
cd ../task-manager-feature-a && copilot

# Terminal 2
cd ../task-manager-feature-b && copilot

Each session has its own file context, so there are no conflicts as both agents read and write files independently.

Combine with /fleet — within a single session, /fleet spawns parallel subagents that work on different parts of a task simultaneously. Worktrees are for when you need completely independent sessions (different features, different branches).

Cleanup when done:

git worktree list                              # See all worktrees

# Remove worktrees when done
git worktree remove ../task-manager-feature-a
git worktree remove ../task-manager-feature-b

# Check your branches - note they are not deleted by removing the worktree
git branch -l

✅ Checkpoint

You understand the permission model from /allow-all down to granular --allow-tool patterns. You've seen how solutions such as Copilot Cloud Agent, Codespaces, and Docker Sandboxes isolate Copilot for safe autonomous operation. In addition, you've seen how Git worktrees enable parallel CLI sessions on the same repo.

Exercise 11 — IDE Integration & Advanced Tips

11.1 Connect CLI to VS Code

If you use VS Code, you can bridge the CLI session to your editor:

/ide

This lets the CLI agent see your open files, editor state, and project context. The workflow becomes:

  1. CLI for scaffolding, automation, and multi-file tasks
  2. VS Code for visual diffs, inline editing, and detailed refinement
  3. Both connected for shared context

11.2 Deep Research

Need to understand something before coding? Use the research agent:

/research What are the best practices for rate limiting Express.js APIs in production?

This performs a deep investigation using GitHub search and web sources.

11.3 Session Management

Your sessions persist across terminal restarts:

/session          # View current and past sessions
/resume # Switch to a different session
/rename # Give this session a meaningful name
/context # See how much of the context window you've used
/compact # Summarize conversation to free up context

11.4 LSP for Code Intelligence

Copilot CLI does not bundle Language Server Protocol (LSP) servers. This gives Copilot go-to-definition, diagnostics, and type information — making its suggestions more accurate. To configure an LSP server, first install and then update your json file. For typeScript, you can use typescript-language-server.:

npm install -g typescript-language-server

Create .github/lsp.json in your repository (or ~/.copilot/lsp-config.json for user-wide) with the following content:

{
"lspServers": {
"typescript": {
"command": "typescript-language-server",
"args": ["--stdio"],
"fileExtensions": {
".ts": "typescript",
".tsx": "typescript"
}
}
}
}

Test as follows:

copilot
/lsp show
/lsp test typescript

The show command should list the Typescript server. The test command should start the server and then kill it after successfully testing.

As long as the server supports the LSP standard and communicates over --stdio, you can configure it to work with Copilot CLI. A few options include:

LanguageServerInstall
TypeScript/JStypescript-language-servernpm i -g typescript-language-server
Pythonpyright or pylsppip install pyright or npm i -g pyright
Rustrust-analyzerrustup component add rust-analyzer
Gogoplsgo install golang.org/x/tools/gopls@latest
JavaEclipse JDT LSdownload from Eclipse or brew install jdtls
Kotlinkotlin-language-serverbrew install JetBrains/utils/kotlin-lsp
C/C++clangdinstall via package manager or npm i -g clangd
C#csharp-lsdotnet tool install --global csharp-ls
Rubyruby-lspgem install ruby-lsp
Swiftsourcekit-lspbrew install swift or swift.org

There are a number of other LSP servers listed here.

11.5 Switch Models

At any time you can switch AI models to find the best fit for your task:

/model

11.6 Fleet Mode

For large tasks, enable parallel subagents:

/fleet <prompt>

Copilot can spawn multiple agents to work on different parts of a task simultaneously. Often after planning mode the CLI will prompt you to offload to fleet and autopilot, but be aware you can also trigger fleet mode directly.

11.7 Monitor Your Usage

/usage

Each prompt (interactive or -p) counts as one premium request. Keep an eye on consumption.

11.8 Querying CLI Session History

Copilot CLI stores your entire session history — prompts, responses, tool calls, and outcomes — in a SQLite database in ~/.copilot/. This database has a full-text search index, which means you can ask Copilot natural language questions about your past sessions.

note

This feature uses the experimental sql tool. Enable it with /experimental on if it's not already active. As an experimental feature, the syntax and availability may change.

Try querying your session history:

Search my session history for when I fixed the DELETE endpoint bug.
Show me all sessions where I used the expert-react-frontend-engineer agent.

Copilot translates your natural language question into a SQL query against the session database and returns matching results. This is useful for:

  • Finding past solutions — "How did I configure the Playwright tests last time?"
  • Auditing work — "What files did I modify in yesterday's session?"
  • Onboarding teammates — share session exports from /share and let them search for context

✅ Checkpoint

You now know how to bridge CLI and VS Code, manage sessions, switch models, and use advanced features like research, fleet mode, and LSP.

Quick Reference

Keyboard Shortcuts

ShortcutAction
Shift+TabCycle modes (interactive → plan → autopilot)
Ctrl+SRun command, keep input
Ctrl+CCancel / clear
Ctrl+C ×2Exit CLI
Ctrl+TToggle reasoning display
Ctrl+OExpand recent timeline
Ctrl+GEdit prompt in external editor
Ctrl+LClear screen
EscCancel current operation
!commandRun shell command directly
@fileMention file for context

Essential Slash Commands

CommandPurpose
/helpList all commands
/modelSwitch AI model
/compactCompress conversation history
/contextShow token usage
/diffReview local changes
/reviewAI code review
/shareExport session
/resumeResume previous session
/initBootstrap repo instructions
/instructionsView active instruction files
/mcpManage MCP servers (show, add, edit, delete)
/agentBrowse/select agents
/skillsManage skills (list, info, add, remove, reload)
/ideConnect to VS Code
/delegateSend session to GitHub → PR
/researchDeep research with web sources
/fleetParallel subagent execution
/usageCheck premium request usage
/allow-allAuto-approve all actions
/updateUpdate CLI
copilot pluginManage plugins (install, list, uninstall, update)

Custom Instruction Locations

FileScope
.github/copilot-instructions.mdRepository-wide
.github/instructions/**/*.instructions.mdPath-specific (with applyTo)
AGENTS.md / CLAUDE.md / GEMINI.mdAgent/model-specific
~/.copilot/copilot-instructions.mdUser-wide (all projects)