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.
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.
- Install script (macOS / Linux)
- Homebrew (macOS / Linux)
- npm (all platforms)
- WinGet (Windows)
curl -fsSL https://gh.io/copilot-install | bash
brew install copilot-cli
npm install -g @github/copilot
winget install GitHub.Copilot
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.
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.
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!
- Create project structure and each file
- Run
npm initandnpm install - 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
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.
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.
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
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:
| Aspect | Local Execution | GitHub Delegation |
|---|---|---|
| Execution | Blocks your terminal | Runs async on GitHub |
| Security | Runs on your machine | Isolated cloud environment |
| Speed | You monitor each step | Agent works while you're offline |
| Collaboration | Changes only locally | PR enables team review & discussion |
| Audit Trail | Git history | Full 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:
- Prompt if you have open changes to commit or push
- Push a branch to your GitHub repo
- Trigger the Copilot Coding Agent on GitHub to execute the plan
- Open a draft PR for the work
- Stream logs of the session to the CLI in real time
- Give you the option to start a new session rather than watch the agent work
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.
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.
| What | Where |
|---|---|
| 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.
| What | Where |
|---|---|
| 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 Event | When It Fires | Can Block? |
|---|---|---|
sessionStart | New or resumed session begins | No |
preToolUse | Before each tool executes | Yes — allow, deny, or modify |
postToolUse | After each tool completes | No |
agentStop | Main agent finishes a turn | Yes — can force continuation |
subagentStop | A subagent completes | Yes — 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/compactat 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:
| Server | What It Does |
|---|---|
github | GitHub API — issues, PRs, commits, code search, Actions |
playwright | Browser automation — navigate, click, type, screenshot |
fetch | HTTP requests via the fetch tool |
time | Time 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.
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
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:
| Type | Name | What It Does |
|---|---|---|
| Skill | playwright-explore-website | Website exploration for testing using Playwright MCP |
| Skill | playwright-generate-test | Generate Playwright tests from scenario descriptions |
| Agent | Expert React Frontend Engineer | Expert React 19 engineer — modern hooks, Server Components, TypeScript, performance |
| Agent | Electron Code Review Mode Instructions | Electron + 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
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:
| Flag | What It Allows |
|---|---|
--allow-all-tools | Skip tool approval — Copilot runs any command without asking |
--allow-all-paths | Disable file path verification — access any file on your system |
--allow-all-urls | Disable 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+Cat 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:
- Just keep a long alias or shell wrapper script included in your PATH. This is simple but can get unwieldy with many rules.
- 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. - Use the
--available-toolsflag 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.
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 listto 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:
- CLI for scaffolding, automation, and multi-file tasks
- VS Code for visual diffs, inline editing, and detailed refinement
- 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:
| Language | Server | Install |
|---|---|---|
| TypeScript/JS | typescript-language-server | npm i -g typescript-language-server |
| Python | pyright or pylsp | pip install pyright or npm i -g pyright |
| Rust | rust-analyzer | rustup component add rust-analyzer |
| Go | gopls | go install golang.org/x/tools/gopls@latest |
| Java | Eclipse JDT LS | download from Eclipse or brew install jdtls |
| Kotlin | kotlin-language-server | brew install JetBrains/utils/kotlin-lsp |
| C/C++ | clangd | install via package manager or npm i -g clangd |
| C# | csharp-ls | dotnet tool install --global csharp-ls |
| Ruby | ruby-lsp | gem install ruby-lsp |
| Swift | sourcekit-lsp | brew 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.
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
/shareand 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
| Shortcut | Action |
|---|---|
Shift+Tab | Cycle modes (interactive → plan → autopilot) |
Ctrl+S | Run command, keep input |
Ctrl+C | Cancel / clear |
Ctrl+C ×2 | Exit CLI |
Ctrl+T | Toggle reasoning display |
Ctrl+O | Expand recent timeline |
Ctrl+G | Edit prompt in external editor |
Ctrl+L | Clear screen |
Esc | Cancel current operation |
!command | Run shell command directly |
@file | Mention file for context |
Essential Slash Commands
| Command | Purpose |
|---|---|
/help | List all commands |
/model | Switch AI model |
/compact | Compress conversation history |
/context | Show token usage |
/diff | Review local changes |
/review | AI code review |
/share | Export session |
/resume | Resume previous session |
/init | Bootstrap repo instructions |
/instructions | View active instruction files |
/mcp | Manage MCP servers (show, add, edit, delete) |
/agent | Browse/select agents |
/skills | Manage skills (list, info, add, remove, reload) |
/ide | Connect to VS Code |
/delegate | Send session to GitHub → PR |
/research | Deep research with web sources |
/fleet | Parallel subagent execution |
/usage | Check premium request usage |
/allow-all | Auto-approve all actions |
/update | Update CLI |
copilot plugin | Manage plugins (install, list, uninstall, update) |
Custom Instruction Locations
| File | Scope |
|---|---|
.github/copilot-instructions.md | Repository-wide |
.github/instructions/**/*.instructions.md | Path-specific (with applyTo) |
AGENTS.md / CLAUDE.md / GEMINI.md | Agent/model-specific |
~/.copilot/copilot-instructions.md | User-wide (all projects) |