AI-Powered Development Workflows with GitHub Copilot and the gh CLI
Using GitHub Issues as long-term memory, prompt files as workflow automation, and Copilot as the execution engine
Table of Contents
- Introduction
- GitHub Issues as Long-Term Memory
- The gh CLI as Your Workflow Engine
- Prompt Files: Encoding Workflows for Copilot
- Setting Up the "Next Issue" Workflow
- Using the Workflow (includes starter repo and settings guide)
- Tying It All Together: The Instruction File Chain
- Practical Patterns
- Conclusion
Introduction
Type /next-issue in VS Code Copilot Chat. Copilot queries GitHub for your highest-priority open issue, shows you the title and acceptance criteria, creates a feature branch, reads your architecture rules, and starts implementing. When it's done, type /close-issue. Copilot summarizes the changes, creates a pull request, and updates the issue labels. No copy-pasting context. No explaining what you're working on. No starting from zero.
That's the workflow we built and tested. This post walks through exactly how to set it up.
The core idea: GitHub Issues already hold your project's intent, decisions, and scope. The gh CLI makes them accessible from the terminal. Prompt files turn gh commands into one-step slash commands that Copilot executes in agent mode. Put those three together, and Copilot stops being a code generator that forgets everything between sessions. It becomes a workflow engine with persistent memory.
Want to skip ahead? Clone the starter repository and try /next-issue in Copilot Chat right now.
Here's what we'll cover:
- GitHub Issues as long-term memory - structuring issues so Copilot can parse and act on them
-
The
ghCLI as the bridge - how Copilot reads and updates issues through terminal commands - Prompt files - encoding the full issue-to-PR lifecycle as slash commands
- The settings that make it work - the VS Code configuration we discovered through trial and error
-
A starter repository - clone it and try
/next-issuein five minutes
2. GitHub Issues as Long-Term Memory
2.1 The problem with ephemeral context
Copilot works within a context window. When the session ends, the context is gone. You can work around this with chat history - but that's tied to a single session.
Issues are different:
- They persist across sessions and team members
- They have structured metadata (labels, milestones, assignees, projects)
- They support threaded comments for decision history
- They're queryable via
ghCLI with JSON output - They close automatically when a PR merges with
Fixes #123
When Copilot reads an issue before starting work, it picks up the full context: what needs to be done, why, what's out of scope, and what was already tried.
2.2 Structuring issues for Copilot
A well-structured issue serves both humans and Copilot. The key is making the intent, scope, and completion criteria explicit.
## Description
Add pagination to the GET /api/orders endpoint. The current implementation
returns all records, which causes timeout errors for customers with 10k+ orders.
## Acceptance Criteria
- [ ] Endpoint accepts `page` and `pageSize` query parameters
- [ ] Default page size is 25, maximum is 100
- [ ] Response includes total count, page number, and total pages in metadata
- [ ] Existing clients without pagination params get page 1 (backwards compatible)
- [ ] Unit tests cover edge cases: empty results, last page, invalid params
## Non-Goals
- Cursor-based pagination (future consideration)
- Caching layer (separate issue)
## Context
Related to performance investigation in #187.
Previous attempt in PR #201 was reverted due to breaking the CSV export.
The checkboxes in acceptance criteria matter. Copilot can parse these and use them as a checklist to work through. The non-goals section prevents scope creep - without it, Copilot might "helpfully" add cursor pagination because it seems like a good idea.
2.3 Labels as queryable state
Labels turn issues into a queryable task database:
| Label | Purpose |
|---|---|
status:todo |
Ready for work |
status:in-progress |
Currently being worked on |
status:blocked |
Waiting on dependency or decision |
status:in-review |
PR open, awaiting review |
type:bug |
Defect |
type:feature |
New functionality |
type:refactor |
Internal improvement |
priority:high |
Address this sprint |
Copilot can query for the next task using gh:
gh issue list --label "status:todo" --label "priority:high" --json number,title,body
This returns structured JSON that Copilot can parse and act on.
2.4 Comments as decision log
Issue comments create a persistent decision log. When you discuss an approach, document the outcome:
gh issue comment 42 --body "Decided to use offset pagination over cursor-based.
Reason: simpler client integration, acceptable performance for our data volume.
Revisit if any table exceeds 1M rows."
The next Copilot session - or the next developer - gets this context for free.
3. The gh CLI as Your Workflow Engine
3.1 Why gh CLI matters for Copilot workflows
VS Code Copilot Chat in agent mode can run terminal commands. The gh CLI gives Copilot a structured way to interact with GitHub:
- Copilot agent mode runs
ghcommands in the VS Code integrated terminal - JSON output makes
ghresponses machine-parseable for Copilot - It authenticates via your existing GitHub credentials
- Combined with prompt files,
ghcommands become automated workflows
3.2 Issue lifecycle from the terminal
Create an issue:
gh issue create --title "Add pagination to orders endpoint" --body "$(cat issue-body.md)" --label "type:feature" --label "status:todo" --label "priority:high"
List issues ready for work:
gh issue list --label "status:todo" --json number,title,labels --jq '.[] | "\(.number): \(.title)"'
Start working on an issue:
gh issue edit 42 --add-label "status:in-progress" --remove-label "status:todo"
gh issue develop 42 --checkout
The gh issue develop command creates a branch linked to the issue and checks it out. The branch name is derived from the issue title.
Add implementation notes:
gh issue comment 42 --body "Implementation complete. Used EF Core Skip/Take for pagination. Added PaginationMetadata record to Application layer DTOs."
Close via PR:
gh pr create --title "feat(orders): add pagination to GET /api/orders" --body "Fixes #42"
When the PR merges, issue #42 closes automatically.
3.3 JSON output for scripting
Most gh commands support --json output, which Copilot can parse directly:
# Get all high-priority bugs with their assignees
gh issue list --label "type:bug" --label "priority:high" --json number,title,assignees
# Count open issues by label
gh issue list --state open --json labels --jq '[.[].labels[].name] | group_by(.) | map({label: .[0], count: length})'
# Find issues without acceptance criteria
gh issue list --json number,title,body --jq '.[] | select(.body | test("Acceptance Criteria") | not) | "\(.number): \(.title)"'
3.4 Project boards from the CLI
GitHub Projects (v2) are manageable via gh project:
# List projects
gh project list
# Add an issue to a project
gh project item-add PROJECT_NUMBER --owner @me --url https://github.com/owner/repo/issues/42
Project boards give you a visual layer on top of the issue-based workflow without changing how you create or manage issues.
4. Prompt Files: Encoding Workflows for Copilot
4.1 What are prompt files
Prompt files are Markdown files with YAML frontmatter that you invoke as slash commands in VS Code Copilot Chat. They live in .github/prompts/ and let you encode repeatable workflows that Copilot follows step by step.
When you type /next-issue in VS Code Copilot Chat (agent mode), Copilot reads the prompt file, runs the gh commands via the terminal, and starts working. No manual context loading required.
Note: Prompt files are a VS Code Copilot Chat feature. Copilot CLI (gh copilot) does not read .github/prompts/ - it has its own interaction model. The workflows in this guide are designed for VS Code agent mode.
4.2 Prompt file structure
---
description: 'A short description shown when browsing slash commands'
agent: 'agent'
tools: ['*']
---
Your instructions in natural language.
Copilot follows these instructions using the specified tools.
You can reference variables like ${input:variableName} for user input.
Key frontmatter fields:
| Field | Purpose |
|---|---|
description |
Shown in the slash command picker |
agent |
agent (can edit files and run commands), ask (read-only advice) |
model |
Optional model override |
tools |
Tools available to the agent. Use ['*'] to enable all tools (recommended) |
The agent value is critical. With agent, Copilot can run terminal commands (including gh) and edit files. With ask, it can only provide advice.
A note on the tools field: You might be tempted to list specific tools like ['terminal', 'search/codebase']. Don't. The internal tool names vary across VS Code versions and Copilot extension updates. During testing, we found that specifying individual tool names led to Copilot reporting "I don't have terminal tools available" or being unable to edit files. The ['*'] wildcard grants access to all available tools - terminal, file editing, code search, and any MCP servers you've configured - and is the only reliable approach.
4.3 Setting up prompt files
Create the directory structure:
your-repo/
.github/
prompts/
next-issue.prompt.md
start-issue.prompt.md
close-issue.prompt.md
standup.prompt.md
Enable prompt files and agent capabilities in VS Code. You need settings at two levels:
Workspace settings (.vscode/settings.json in your repo - commit this so the team gets it):
{
"chat.promptFiles": true,
"github.copilot.chat.codeGeneration.useInstructionFiles": true,
"github.copilot.chat.agent.terminal": true
}
User-level settings (File > Preferences > Settings, or Ctrl+,):
These control whether Copilot can run terminal commands without prompting you each time. Search for these settings in the VS Code settings UI:
| Setting | Value | Purpose |
|---|---|---|
chat.tools.terminal.enableAutoApprove |
true |
Enables terminal command auto-approval |
chat.tools.terminal.autoApprove |
{ "gh": true } |
Auto-approves gh commands specifically |
Without these user-level settings, Copilot will ask you to manually run every gh command - which defeats the purpose of the workflow automation. You can add other commands to the auto-approve list as you get comfortable (e.g., "git": true, "dotnet": true).
Once enabled, type / in Copilot Chat to see your prompt files listed alongside built-in commands.
5. Setting Up the "Next Issue" Workflow
These prompt files create a workflow where you type a slash command and Copilot handles issue selection, branch creation, implementation, and PR creation.
5.1 The next-issue prompt
Create .github/prompts/next-issue.prompt.md:
---
description: 'Pick up the next priority issue and start working on it'
agent: 'agent'
tools: ['*']
---
Find the highest priority issue assigned to me that is ready for work.
Run this command in the terminal to find the next issue:
gh issue list --assignee @me --label "status:todo" --json number,title,body,labels --jq '.[0]'
If no issues are assigned to me, check for unassigned todo issues:
gh issue list --label "status:todo" --no-assignee --json number,title,body,labels --jq '.[0]'
Once you have an issue:
1. Display the issue number, title, and full body so I can confirm
2. Ask me if I want to proceed with this issue
3. If confirmed, run these commands:
- gh issue edit ISSUE_NUMBER --add-label "status:in-progress" --remove-label "status:todo"
- gh issue develop ISSUE_NUMBER --checkout
4. Read the acceptance criteria from the issue body
5. Search the codebase for related files and existing patterns
6. Begin implementing, working through the acceptance criteria one by one
7. After completing each item, tell me what was done
5.2 The start-issue prompt
Create .github/prompts/start-issue.prompt.md:
---
description: 'Start working on a specific issue by number'
agent: 'agent'
tools: ['*']
---
Start working on issue #${input:issueNumber:Issue number}.
1. Run in terminal: gh issue view ${input:issueNumber} --json number,title,body,comments
2. Display the issue title, description, acceptance criteria, and any comments
3. Run: gh issue edit ${input:issueNumber} --add-label "status:in-progress" --remove-label "status:todo"
4. Run: gh issue develop ${input:issueNumber} --checkout
5. Read the acceptance criteria from the issue body
6. Search the codebase for related files and existing patterns
7. If there are comments with prior decisions or context, factor those into your approach
8. Begin implementing, working through the acceptance criteria one by one
9. After completing each item, tell me what was done
5.3 The close-issue prompt
Create .github/prompts/close-issue.prompt.md:
---
description: 'Wrap up current issue: add notes, create PR, update labels'
agent: 'agent'
tools: ['*']
---
Wrap up the current issue and prepare it for review.
1. Run in terminal: git branch --show-current
2. Extract the issue number from the branch name
3. Run: gh issue view ISSUE_NUMBER --json title,body
4. Review what changed: git diff main...HEAD --stat
5. Add an implementation summary comment:
gh issue comment ISSUE_NUMBER --body "SUMMARY_OF_CHANGES"
6. Create a pull request using conventional commit format:
gh pr create --title "TYPE(SCOPE): DESCRIPTION (#ISSUE_NUMBER)" --body "Fixes #ISSUE_NUMBER"
Include a summary of changes and test plan in the PR body
7. Update the issue label:
gh issue edit ISSUE_NUMBER --add-label "status:in-review" --remove-label "status:in-progress"
8. Display the PR URL
5.4 The standup prompt
Create .github/prompts/standup.prompt.md:
---
description: 'Show my current work status across issues and PRs'
agent: 'agent'
tools: ['*']
---
Give me a quick status report by running these commands in the terminal:
1. gh issue list --assignee @me --label "status:in-progress" --json number,title
2. gh issue list --assignee @me --label "status:in-review" --json number,title
3. gh pr list --author @me --json number,title,reviewDecision
4. gh pr list --search "review-requested:@me" --json number,title,author
5. gh issue list --assignee @me --label "status:todo" --json number,title,labels
Summarize the results as a concise standup report.
Only show sections that have items.
6. Using the Workflow
6.1 Starter repository
We built and tested everything in this guide against a real repository. It's public and you can use it as a starting point:
github.com/markring/copilot-workflow-starter
The repo includes working prompt files, issue templates, Copilot instructions, and the VS Code workspace settings. Clone it, create a few issues with status:todo labels, and try /next-issue in Copilot Chat to see the workflow in action.
6.2 In VS Code Copilot Chat
- Open Copilot Chat (Ctrl+Shift+I / Cmd+Shift+I)
- Make sure you're in Agent mode (select from the mode dropdown)
- Type
/next-issueand press Enter
Copilot reads the prompt file, runs gh issue list in the integrated terminal, presents the issue, and asks for confirmation. Once confirmed, it creates a branch, reads the codebase, and starts implementing.
6.3 Getting the settings right
This is where most people get stuck. The prompt files are the easy part. The VS Code settings are what make the workflow actually run hands-free.
We discovered this the hard way. Our first attempt used tools: ['terminal', 'search/codebase'] in the prompt file frontmatter. Copilot responded with "I don't have terminal tools available." The internal tool names in VS Code (like execute/runInTerminal) don't match anything in the documentation, and they change between versions. Switching to tools: ['*'] fixed it immediately.
The next issue: Copilot could run terminal commands but couldn't edit files. Because we'd only listed terminal tools, file editing wasn't available. Again, tools: ['*'] solved this by granting access to everything.
Even after the prompt files were correct, Copilot kept asking "please run this command in your terminal" instead of running it. This required two user-level settings:
-
chat.tools.terminal.enableAutoApproveset totrue -
chat.tools.terminal.autoApprovewith{ "gh": true }to specifically auto-approveghcommands
These are user-level settings (not workspace), so they don't go in .vscode/settings.json. Set them via File > Preferences > Settings and search for "terminal auto approve."
Once all three pieces were in place - tools: ['*'] in prompt files, workspace settings for prompt file and instruction file support, and user-level settings for terminal auto-approval - Copilot picked up an issue, created a branch, implemented the feature, and created a PR without manual intervention.
6.4 Confirmation and trust settings
In VS Code agent mode, Copilot asks for confirmation before running terminal commands. The permission levels are:
| Setting | Behavior |
|---|---|
| Default | Confirm each tool call |
| Bypass Approvals | Auto-approve all tool calls without dialogs |
| Autopilot (Preview) | Auto-approve everything, auto-respond to questions, work until done |
For the issue workflow, you'll want at minimum the terminal auto-approve settings from section 4.3 so Copilot can run gh commands without prompting each time.
7. Tying It All Together: The Instruction File Chain
For Copilot to implement well, it needs more than just the issue. Your project's .github/ directory should have a layered configuration:
7.1 The four layers
your-repo/
.github/
copilot-instructions.md # Layer 1: Global rules (always loaded)
instructions/
api-controllers.instructions.md # Layer 2: Scoped rules (loaded by file path)
domain-entities.instructions.md
infrastructure-data.instructions.md
testing.instructions.md
prompts/
next-issue.prompt.md # Layer 3: Workflow prompts (invoked manually)
start-issue.prompt.md
close-issue.prompt.md
standup.prompt.md
Plus Layer 4: The issue body - the specific task with acceptance criteria, loaded at runtime by the prompt file via gh issue view.
7.2 What each layer does
| Layer | Loaded | Purpose |
|---|---|---|
copilot-instructions.md |
Automatically, every session | Architecture, naming, libraries, team conventions |
*.instructions.md |
Automatically, by file path glob | Layer-specific rules (controllers vs. domain vs. tests) |
*.prompt.md |
Manually, via /slash-command
|
Workflow automation (fetch issue, create branch, create PR) |
| Issue body | At runtime, via gh issue view
|
Specific task scope, acceptance criteria, non-goals |
The instruction files handle code quality. The prompt files handle workflow. The issue handles scope. Each layer has a distinct job, and Copilot applies all of them together.
7.3 How it plays out
When you type /next-issue in Copilot Chat:
- Copilot loads your
copilot-instructions.md(knows your architecture) - Copilot runs the prompt file (fetches the issue via
gh, creates branch) - Copilot reads the issue body (knows what to build and what not to build)
- Copilot starts editing files - scoped
.instructions.mdfiles activate based on which files it touches - The result: code that follows your conventions, implements the issue's acceptance criteria, and stays within scope
8. Practical Patterns
8.1 Issue templates for consistency
Create .github/ISSUE_TEMPLATE/feature.md:
---
name: Feature Request
about: Propose a new feature
labels: type:feature, status:todo
---
## Description
<!-- What should this feature do and why does it matter? -->
## Acceptance Criteria
- [ ] <!-- Testable outcome 1 -->
- [ ] <!-- Testable outcome 2 -->
- [ ] <!-- Testable outcome 3 -->
## Non-Goals
- <!-- What is explicitly out of scope? -->
## Context
<!-- Related issues, discussions, or prior art -->
This ensures every issue follows the structure that Copilot can reliably parse and work through.
8.2 Conventional commits tied to issues
Link every commit to an issue for full traceability:
git commit -m "feat(orders): add pagination query parameters (#42)"
Format: <type>(<scope>): <description> (#issue)
Types: feat, fix, docs, refactor, test, chore
The /close-issue prompt automatically uses this format when creating PRs.
8.3 Bulk issue creation for sprint planning
When bootstrapping a sprint, create issues in bulk:
gh issue create --title "Set up EF Core DbContext and migrations" --label "type:feature" --label "status:todo" --milestone "Sprint 1"
gh issue create --title "Implement OrderRepository with CRUD operations" --label "type:feature" --label "status:todo" --milestone "Sprint 1"
gh issue create --title "Add FluentValidation for CreateOrderCommand" --label "type:feature" --label "status:todo" --milestone "Sprint 1"
gh issue create --title "Set up xUnit integration tests with WebApplicationFactory" --label "type:test" --label "status:todo" --milestone "Sprint 1"
Or script it from a file:
while IFS='|' read -r title labels; do
gh issue create --title "$title" --label "$labels" --milestone "Sprint 1"
done < sprint-issues.txt
8.4 A prompt for breaking down large issues
Create .github/prompts/break-down-issue.prompt.md:
---
description: 'Break a large issue into smaller, implementable sub-issues'
agent: 'agent'
tools: ['*']
---
Break down issue #${input:issueNumber:Issue number} into smaller sub-issues.
1. Run in terminal: gh issue view ${input:issueNumber} --json title,body
2. Read the description and acceptance criteria
3. Search the codebase to understand the scope of changes needed
4. Propose a breakdown of 3-6 smaller issues, each with:
- A clear title: "PARENT_TITLE - SPECIFIC_PIECE"
- 2-4 acceptance criteria checkboxes
- A reference to the parent issue
5. Ask me to review the breakdown before creating anything
6. Once I approve, create each sub-issue using gh issue create
7. Add a comment on the parent issue listing the sub-issues created
8.5 A prompt for reviewing issue history
Create .github/prompts/issue-context.prompt.md:
---
description: 'Load full context for an issue including comments and related issues'
agent: 'ask'
tools: ['*']
---
Load the full context for issue #${input:issueNumber:Issue number}.
Run these commands in the terminal:
1. gh issue view ${input:issueNumber} --json number,title,body,labels,assignees,milestone,comments
2. gh issue list --search "is:issue mentions:${input:issueNumber}" --json number,title,state
Summarize:
- What the issue is about
- Current status (labels, assignee)
- Key decisions from comments
- Related issues and their state
- Any blockers or dependencies mentioned
Note that this prompt uses agent: 'ask' - it only reads and summarizes, it doesn't make changes. Use this when you want context without starting work.
Conclusion
The tools here aren't new. GitHub Issues, gh CLI, and Copilot Chat have been around. What's new is wiring them together so Copilot operates with full context instead of starting from zero every session.
The real work is in the issues. Well-structured acceptance criteria, clear non-goals, decision history in comments - that's what makes the automation useful rather than just fast. A prompt file can fetch an issue and create a branch in seconds. Whether the implementation is any good depends on what you wrote in the issue body.
To get started, clone the copilot-workflow-starter repository. It has working prompt files, issue templates, and the VS Code settings already configured. Create a few issues, label them status:todo, and type /next-issue.
United States
NORTH AMERICA
Related News

Open Harness: The Multi-Panel AI Powerhouse Revolutionizing Developer Workflows
4h ago
Firefox Announces Built-In VPN and Other New Features - and Introduces Its New Mascot
3h ago
White House Unveils National AI Policy Framework To Limit State Power
3h ago
CBS News Shutters Radio Service After Nearly a Century
3h ago
50% of Consumers Prefer Brands That Avoid GenAI Content
3h ago