From 908d625835b2e222f974847566ac8fa1624f5421 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 8 May 2026 22:48:56 +0000 Subject: [PATCH 01/11] Initial plan From 4b9e925f8afd31f081ff4e0c9a31957f2f533617 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 8 May 2026 22:52:08 +0000 Subject: [PATCH 02/11] Initial plan: add scenario scaffolding system Co-authored-by: GeekTrainer <6109729+GeekTrainer@users.noreply.github.com> --- client/package-lock.json | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 6011fa3d..d4b233e8 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1633,7 +1633,6 @@ "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-5.1.1.tgz", "integrity": "sha512-Y1Cs7hhTc+a5E9Va/xwKlAJoariQyHY+5zBgCZg4PFWNYQ1nMN9sjK1zhw1gK69DuqVP++sht/1GZg1aRwmAXQ==", "license": "MIT", - "peer": true, "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^4.0.1", "debug": "^4.4.1", @@ -2172,7 +2171,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2325,7 +2323,6 @@ "resolved": "https://registry.npmjs.org/astro/-/astro-5.15.5.tgz", "integrity": "sha512-A56u4H6gFHEb0yRHcGTOADBb7jmEwfDjQpkqVV/Z+ZWlu6mYuwCrIcOUtZjNno0chrRKmOeZWDofW23ql18y3w==", "license": "MIT", - "peer": true, "dependencies": { "@astrojs/compiler": "^2.12.2", "@astrojs/internal-helpers": "0.7.4", @@ -2561,7 +2558,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -5291,7 +5287,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -5669,7 +5664,6 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.1.tgz", "integrity": "sha512-33xGNBsDJAkzt0PvninskHlWnTIPgDtTwhg0U38CUoNP/7H6wI2Cz6dUeoNPbjdTdsYTGuiFFASuUOWovH0SyQ==", "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -5935,7 +5929,6 @@ "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.43.5.tgz", "integrity": "sha512-HQoZArIewxQVNedseDsgMgnRSC4XOXczxXLF9rOJaPIJkg58INOPUiL8aEtzqZIXNSZJyw8NmqObwg/voajiHQ==", "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", @@ -6108,7 +6101,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -6497,7 +6489,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", @@ -6939,6 +6930,22 @@ "prettier": "2.8.7" } }, + "node_modules/yaml-language-server/node_modules/prettier": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz", + "integrity": "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==", + "license": "MIT", + "optional": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/yaml-language-server/node_modules/request-light": { "version": "0.5.8", "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.5.8.tgz", @@ -7109,7 +7116,6 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } From f742bf8e360c224e6ee19f074fe5c400a5b6293f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 8 May 2026 22:58:37 +0000 Subject: [PATCH 03/11] Add scenario scaffolding system for workshop authors - scenarios/tailspin-toys/scenario.yml: default scenario config with metadata, tech stack, variables, and skip list - scenarios/tailspin-toys/overrides/README.md: placeholder for exercise overrides - scenarios/README.md: overview and directory layout docs - docs/authoring/new-scenario-guide.md: step-by-step guide for adding new scenarios - docs/authoring/README.md: index for authoring docs - scripts/new-scenario.py: scaffolding script for new scenarios - scripts/validate-scenario.py: validation script (checks structure, fields, skip list, overrides) - .github/prompts/create-scenario-app.prompt.md: AI prompt for creating/updating sample apps - .github/copilot-instructions.md: update Scripts and Repository Structure sections Co-authored-by: GeekTrainer <6109729+GeekTrainer@users.noreply.github.com> --- .github/copilot-instructions.md | 6 + .github/prompts/create-scenario-app.prompt.md | 93 +++++ docs/authoring/README.md | 16 + docs/authoring/new-scenario-guide.md | 226 +++++++++++ scenarios/README.md | 59 +++ scenarios/tailspin-toys/overrides/README.md | 19 + scenarios/tailspin-toys/scenario.yml | 55 +++ scripts/new-scenario.py | 212 ++++++++++ scripts/validate-scenario.py | 379 ++++++++++++++++++ 9 files changed, 1065 insertions(+) create mode 100644 .github/prompts/create-scenario-app.prompt.md create mode 100644 docs/authoring/README.md create mode 100644 docs/authoring/new-scenario-guide.md create mode 100644 scenarios/README.md create mode 100644 scenarios/tailspin-toys/overrides/README.md create mode 100644 scenarios/tailspin-toys/scenario.yml create mode 100644 scripts/new-scenario.py create mode 100644 scripts/validate-scenario.py diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 62c23253..4e6b963e 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -60,6 +60,8 @@ This is a crowdfunding platform for games with a developer theme. The applicatio - `scripts/setup-env.sh`: Performs installation of all Python and Node dependencies - `scripts/run-server-tests.sh`: Calls setup-env, then runs all Python tests - `scripts/start-app.sh`: Calls setup-env, then starts both backend and frontend servers + - `scripts/new-scenario.py`: Scaffolds a new workshop scenario (run with `python scripts/new-scenario.py `) + - `scripts/validate-scenario.py`: Validates a scenario's structure and configuration (run with `python scripts/validate-scenario.py ` or `--all`) ## Repository Structure @@ -75,4 +77,8 @@ This is a crowdfunding platform for games with a developer theme. The applicatio - `src/styles/`: CSS and Tailwind configuration - `scripts/`: Development and deployment scripts - `data/`: Database files +- `scenarios/`: Workshop scenario packs (one subdirectory per scenario) + - Each scenario has a `scenario.yml` and optional `overrides/` for exercise-specific content +- `docs/authoring/`: Documentation for workshop authors +- `.github/prompts/`: Copilot prompt files (e.g. `create-scenario-app.prompt.md`) - `README.md`: Project documentation diff --git a/.github/prompts/create-scenario-app.prompt.md b/.github/prompts/create-scenario-app.prompt.md new file mode 100644 index 00000000..c3e14613 --- /dev/null +++ b/.github/prompts/create-scenario-app.prompt.md @@ -0,0 +1,93 @@ +--- +description: Create or update a sample application to use as a workshop scenario +mode: agent +--- + +# Create or update a workshop scenario application + +You are helping a workshop author create or update a sample application that will be used as the hands-on codebase for the "Agents in the SDLC" workshop. The workshop teaches learners how to use GitHub Copilot's agent capabilities across the software development lifecycle. + +## Your task + +${input:task:Choose one — (a) Create a brand-new sample application, or (b) Adapt an existing application to fit the workshop. Describe your choice and the application concept here.} + +## Workshop exercise requirements + +The sample application must support the following exercises. Review each requirement and confirm your application design meets it before generating code. + +### Exercise 1 — MCP and issue creation +- No application requirements (learners create GitHub issues for their backlog). + +### Exercise 2 — Custom instructions +- The application must have an API with unit tests. +- The application must have a UI (frontend). +- The codebase should lack docstrings/comment headers so learners can add them. +- A `.github/copilot-instructions.md` should NOT exist yet (learners create it). +- A `.github/instructions/` directory with one or more `*.instructions.md` files for specific languages or file types IS expected (learners reference these). + +### Exercise 3 — Copilot agent mode (implement a new feature) +- The application must have an API endpoint that returns a list of the primary entities (e.g. `/api/games`). +- The primary entities must have at least two categorical attributes suitable for filtering (e.g. `category` and `publisher`). +- The application must have a frontend page that displays those entities. +- The existing filtering functionality must NOT be implemented yet — learners implement it. +- The API must have a test suite that Copilot can run as part of implementing the feature. + +### Exercise 4 — Copilot coding agent (async tasks) +- The application must lack docstrings/comment headers (first async task: add them). +- The application must have a set of CRUD endpoints that are partially implemented — specifically, only the "read" (GET) endpoints exist. Learners assign the task of adding create/update/delete endpoints to Copilot coding agent. +- A `.github/workflows/copilot-setup-steps.yml` must exist that installs all dependencies and runs tests. This file is pre-created ahead of the workshop. + +### Exercise 5 — Custom agents (accessibility) +- The application must have a UI with a dark colour scheme. +- The UI must not yet have a high-contrast or accessibility toggle — learners add it with a custom accessibility agent. +- A `.github/agents/accessibility.md` custom agent definition must exist that describes how to implement accessibility features for this UI. + +### Exercise 6 — Managing agents +- No additional application requirements. + +### Exercise 7 — Iterating on Copilot's work +- No additional application requirements beyond what exercises 4 and 5 created. + +## Application structure requirements + +- The application must be runnable inside a **GitHub Codespace** using a `.devcontainer/` configuration. +- A `scripts/` directory must contain: + - `setup-env.sh` — installs all dependencies. + - `run-server-tests.sh` — calls setup and runs backend tests. + - `start-app.sh` — calls setup and starts both frontend and backend servers. +- The application must use a **database** (SQLite is fine) with seed data so it works out of the box. +- **Separate frontend and backend** processes are required (e.g. a Python/Node API on one port, a Svelte/React/Vue SPA on another). + +## What to generate + +1. **Describe the application concept** — company name, what the app does, primary entities, and tech stack. +2. **Review each exercise requirement** and confirm the application design satisfies it. Note any gaps. +3. **Generate the full application** including: + - Backend API with seed data and tests (only GET endpoints for the primary entities). + - Frontend displaying the primary entities (no filtering yet). + - `.devcontainer/devcontainer.json` for Codespaces. + - `scripts/setup-env.sh`, `scripts/run-server-tests.sh`, `scripts/start-app.sh`. + - `.github/workflows/copilot-setup-steps.yml`. + - `.github/agents/accessibility.md`. + - `.github/instructions/` with relevant instruction files for the chosen stack. + - A `README.md` describing the application and how to run it. +4. **Do NOT generate**: + - `.github/copilot-instructions.md` (learners create this in exercise 2). + - Filter endpoints or filter UI (learners build these in exercise 3). + - Create/update/delete endpoints (learners assign these to coding agent in exercise 4). + - Docstrings or comment headers (learners add these via coding agent in exercise 4). + - High-contrast mode (learners build this with the custom agent in exercise 5). + +## Validation checklist + +Before finishing, confirm: + +- [ ] Application runs in a fresh Codespace with `scripts/start-app.sh`. +- [ ] Backend tests pass with `scripts/run-server-tests.sh`. +- [ ] `copilot-setup-steps.yml` completes successfully when triggered via `workflow_dispatch`. +- [ ] Primary entities list endpoint returns at least 8–10 seed records. +- [ ] Each entity has at least two categorical attributes suitable for filtering. +- [ ] The UI renders the entity list with no errors. +- [ ] Dark colour scheme is present in the UI. +- [ ] `accessibility.md` custom agent exists in `.github/agents/`. +- [ ] No filtering, no CRUD-write endpoints, no docstrings in the initial state. diff --git a/docs/authoring/README.md b/docs/authoring/README.md new file mode 100644 index 00000000..4b6c56fc --- /dev/null +++ b/docs/authoring/README.md @@ -0,0 +1,16 @@ +# Workshop Authoring Documentation + +This directory contains documentation for **workshop authors** — people who want to contribute content or add new scenario applications to the "Agents in the SDLC" workshop. + +## Contents + +| File | Purpose | +|---|---| +| [new-scenario-guide.md](./new-scenario-guide.md) | Step-by-step guide for adding a new scenario application | + +## Quick links + +- **Scenario directory**: [`scenarios/`](../../scenarios/) — existing scenario packs +- **Scaffolding script**: `python scripts/new-scenario.py ` +- **Validation script**: `python scripts/validate-scenario.py ` +- **AI prompt for app creation**: [`.github/prompts/create-scenario-app.prompt.md`](../../.github/prompts/create-scenario-app.prompt.md) diff --git a/docs/authoring/new-scenario-guide.md b/docs/authoring/new-scenario-guide.md new file mode 100644 index 00000000..70cc7939 --- /dev/null +++ b/docs/authoring/new-scenario-guide.md @@ -0,0 +1,226 @@ +# Authoring Guide — Adding a New Workshop Scenario + +This guide walks you through every step needed to introduce a new sample application into the "Agents in the SDLC" workshop. After following it, learners will be able to use your application as the concrete codebase they work with while exploring GitHub Copilot's agent capabilities. + +## Prerequisites + +- Python 3.8 or later (for the scaffolding and validation scripts). +- A sample application repository accessible on GitHub. If you don't have one yet, see [Using the AI prompt to create a sample app](#using-the-ai-prompt-to-create-a-sample-app). +- Familiarity with the workshop structure — skim through `workshop-content/README.md` and a couple of the exercise files before you begin. + +--- + +## Step 1 — Understand what makes a good workshop scenario + +A scenario application should: + +- **Be small enough to navigate quickly.** Learners spend time in the codebase during exercises; a sprawling monorepo is hard to work with in a short workshop. +- **Have clear separation between frontend and backend** (even if it's a single-language stack). Exercises like 3 and 4 assume there is both a UI and an API to update. +- **Have a test suite** that Copilot can run and iterate against. +- **Tell a story.** Learners take on the role of a developer at a fictional company. Choose a company concept that is interesting and lends itself to obvious feature work. +- **Be deployable in a GitHub Codespace.** The `.devcontainer/` configuration in the main repo drives the learner environment. + +Exercises you may want to **skip** for a particular scenario: + +| Exercise | Common reason to skip | +|---|---| +| `3-copilot-agent-mode-vscode` | App has no frontend, or frontend is purely static | +| `5-custom-agents` | Scenario doesn't include an accessibility use case | + +Skipped exercises are listed in `scenario.yml` and are omitted when the workshop is rendered for that scenario. + +--- + +## Step 2 — Create the scaffolding + +Run the scaffolding script from the repository root: + +```bash +python scripts/new-scenario.py +``` + +Replace `` with a short, lowercase, hyphenated identifier (e.g. `java-bookstore`, `node-blog`). + +The script creates: + +``` +scenarios/ + / + scenario.yml ← pre-filled template; edit this next + overrides/ + README.md ← placeholder for exercise overrides +``` + +--- + +## Step 3 — Fill in `scenario.yml` + +Open `scenarios//scenario.yml` and complete every field: + +```yaml +name: +description: > + One or two sentences describing the application. + +repository: / # GitHub repo for your sample app + +tech_stack: + frontend: + name: + language: + notes: > + Any notes relevant to the exercises. + backend: + name: + language: + notes: > + Any notes relevant to the exercises. + database: + +variables: + company_name: + company_description: + product_type: + primary_entities: + filter_feature: + new_endpoints_feature: + accessibility_feature: + documentation_feature: + +skip: + # List exercise filenames (without .md) that do NOT apply to your scenario + # - 5-custom-agents +``` + +### Required variables + +| Variable | Used in | Example | +|---|---|---| +| `company_name` | All exercises | `Tailspin Toys` | +| `company_description` | README, exercise intros | `a crowdfunding platform for board games` | +| `product_type` | README scenario section | `crowdfunding platform` | +| `primary_entities` | Exercises 3 & 4 | `games, publishers, categories` | +| `filter_feature` | Exercise 3 | `filter games by category and publisher` | +| `new_endpoints_feature` | Exercise 4 | `endpoints to create, update, and delete games` | +| `accessibility_feature` | Exercise 5 | `high-contrast mode toggle` | +| `documentation_feature` | Exercise 4 | `docstrings for all functions` | + +--- + +## Step 4 — Identify which exercises to skip + +Work through `workshop-content/` one exercise at a time and ask: *"Can a learner complete this exercise with my application?"* + +Common mismatches: + +- **Exercise 3** asks learners to add a filter feature that spans frontend and backend. If your app has no frontend, skip it. +- **Exercise 5** builds an accessibility agent for a dark-themed UI. If your app uses a different colour scheme or has no significant CSS layer, you may want to skip it or provide an alternative task in an override. + +Add each exercise to be skipped to the `skip` list in `scenario.yml`. + +--- + +## Step 5 — Write exercise overrides (as needed) + +Each exercise in `workshop-content/` contains a `## Scenario` section that describes the task in the context of Tailspin Toys. You will need to override this section for any exercise where the Tailspin-specific context does not make sense for your application. + +Create the override file at: + +``` +scenarios//overrides/.md +``` + +The override file should be a **complete exercise file** structured identically to the original in `workshop-content/`, with the `## Scenario` section rewritten for your application and any Tailspin-specific references replaced. + +> [!TIP] +> Start by copying the original exercise file, then search for "Tailspin" and replace every occurrence with your scenario's equivalent. Update code snippets and prompts to match your tech stack. + +### Minimal override example + +If only the `## Scenario` section needs to change: + +```markdown + + +# Exercise 1 - Setting up the backlog with Copilot agent mode and GitHub's MCP Server + +...same content as workshop-content/1-mcp.md, except:... + +## Scenario + +You are a developer for . You've been +assigned tasks to introduce new functionality. To file your backlog of issues +you'll enlist the help of Copilot Chat in agent mode and the GitHub MCP server. + +...rest of file unchanged... +``` + +--- + +## Step 6 — Validate your scenario + +Run the validation script to catch common problems: + +```bash +python scripts/validate-scenario.py +``` + +The script checks: + +- All required fields are present in `scenario.yml`. +- Every exercise listed in `skip` exists in `workshop-content/`. +- Every override file in `overrides/` corresponds to an exercise in `workshop-content/`. +- No override exists for an exercise in the `skip` list (that would be a conflict). + +Fix any issues reported before continuing. + +--- + +## Step 7 — Update the scenarios table + +Add your scenario to the table in `scenarios/README.md`: + +```markdown +| `` | | | 🚧 In progress | +``` + +Change the status to ✅ once the scenario is complete and validated. + +--- + +## Step 8 — Test the scenario end-to-end + +Follow the workshop yourself from start to finish using your scenario: + +1. Clone your sample app repository into a codespace. +2. Work through each exercise (skipping those listed in `skip`). +3. Verify that every prompt in the exercises produces reasonable output from Copilot for your tech stack. +4. Adjust `scenario.yml` variables and/or override files as needed based on your experience. + +--- + +## Using the AI prompt to create a sample app + +If you need to create or adapt a sample application for your scenario, use the prompt file at `.github/prompts/create-scenario-app.prompt.md`. Open it in VS Code with GitHub Copilot Chat in **agent mode** and follow the instructions embedded in the prompt. + +The prompt guides Copilot to: + +- Create a minimal but realistic application matching your tech stack. +- Include a working test suite. +- Structure the code in a way that maps cleanly to the workshop exercises. +- Add a `.devcontainer/` configuration for Codespaces. + +--- + +## Checklist + +Use this checklist to track your progress: + +- [ ] Identified and evaluated a suitable sample application +- [ ] Created scaffolding with `scripts/new-scenario.py` +- [ ] Completed all fields in `scenario.yml` +- [ ] Identified exercises to skip +- [ ] Written overrides for exercises with Tailspin-specific content +- [ ] Validated with `scripts/validate-scenario.py` (zero errors) +- [ ] Updated `scenarios/README.md` table +- [ ] Tested the scenario end-to-end diff --git a/scenarios/README.md b/scenarios/README.md new file mode 100644 index 00000000..bde21d5a --- /dev/null +++ b/scenarios/README.md @@ -0,0 +1,59 @@ +# Scenarios + +This directory contains **scenario packs** — self-contained bundles of configuration and content that let authors swap the sample application used in the "Agents in the SDLC" workshop without rewriting the core exercises. + +## What is a scenario? + +A scenario is a specific sample application (e.g. Tailspin Toys, a Java e-commerce store, a Node.js blog platform) that learners interact with as they work through the workshop exercises. The exercises themselves teach the same Copilot concepts regardless of the scenario; the scenario just provides the concrete codebase and context. + +## Directory structure + +``` +scenarios/ + / + scenario.yml # Required: metadata, variables, skip list + overrides/ # Optional: exercise content overrides + .md # Replaces or supplements the named exercise + README.md # Optional: notes for maintainers +``` + +### `scenario.yml` — required fields + +| Field | Type | Description | +|---|---|---| +| `name` | string | Human-readable scenario name | +| `description` | string | Short description of the sample app | +| `repository` | string | GitHub repo in `org/repo` format | +| `tech_stack` | map | Frontend, backend, and database details | +| `variables` | map | Content variables (company name, features, etc.) | +| `skip` | list | Exercise filenames (without `.md`) to omit from this scenario | + +### `overrides/` — optional + +When the core workshop exercises contain scenario-specific wording (company name, feature descriptions, etc.) that does not match your application, create a replacement Markdown file in `overrides/` with the same filename as the exercise. The replacement file should contain a `## Scenario` section that describes what learners are building in the context of your application, plus any other section-level rewrites needed. + +See [docs/authoring/new-scenario-guide.md](../docs/authoring/new-scenario-guide.md) for step-by-step instructions. + +## Available scenarios + +| Scenario ID | Application | Tech stack | Status | +|---|---|---|---| +| `tailspin-toys` | Tailspin Toys (crowdfunding for board games) | Python/Flask · Astro/Svelte | ✅ Default | + +## Adding a new scenario + +Use the scaffolding script to get started: + +```bash +python scripts/new-scenario.py +``` + +This creates the directory structure, a pre-filled `scenario.yml`, and placeholder override files. See the [authoring guide](../docs/authoring/new-scenario-guide.md) for full instructions. + +## Validating a scenario + +Before submitting a new scenario, run the validation script to catch common issues: + +```bash +python scripts/validate-scenario.py +``` diff --git a/scenarios/tailspin-toys/overrides/README.md b/scenarios/tailspin-toys/overrides/README.md new file mode 100644 index 00000000..ee606159 --- /dev/null +++ b/scenarios/tailspin-toys/overrides/README.md @@ -0,0 +1,19 @@ +# Tailspin Toys — Scenario Overrides + +This directory holds exercise-specific content overrides for the **Tailspin Toys** scenario. + +## About overrides + +Because Tailspin Toys is the **default scenario** — meaning all of the exercises in `workshop-content/` were authored against it — this directory is intentionally empty. No overrides are needed. + +## When to add an override + +If you fork the workshop to use a different sample application you may need to replace the *Scenario* section of one or more exercises with text that matches your application. For a new scenario, place the replacement file here with the **same filename** as the exercise it overrides: + +``` +overrides/ + 1-mcp.md ← replaces the Scenario section of exercise 1 + 3-copilot-agent-mode-vscode.md +``` + +See [scenarios/README.md](../../../scenarios/README.md) and the [authoring guide](../../../docs/authoring/new-scenario-guide.md) for full details. diff --git a/scenarios/tailspin-toys/scenario.yml b/scenarios/tailspin-toys/scenario.yml new file mode 100644 index 00000000..c82d2d33 --- /dev/null +++ b/scenarios/tailspin-toys/scenario.yml @@ -0,0 +1,55 @@ +# Tailspin Toys Scenario +# This is the default scenario for the "Agents in the SDLC" workshop. +# See scenarios/README.md for full documentation on the scenario format. + +# Human-readable name displayed in the workshop +name: Tailspin Toys + +# Short description of the application used in this scenario +description: > + A crowdfunding platform for board games with a developer theme. + The app uses a Python/Flask REST API, a SQLite database, and an + Astro/Svelte frontend with Tailwind CSS. + +# GitHub repository for the sample application (org/repo) +repository: github-samples/tailspin-toys + +# Technology stack — used when generating scenario-specific content +tech_stack: + frontend: + name: Astro + Svelte + language: TypeScript / JavaScript + notes: > + Astro handles routing and static rendering. + Svelte components (with runes-based reactivity) handle interactivity. + backend: + name: Flask + language: Python + notes: > + RESTful API using Flask blueprints and SQLAlchemy ORM. + Python type hints are required on all function signatures. + database: SQLite (via SQLAlchemy) + +# Content variables — these values are substituted into workshop content +# and override prompts when rendering scenario-specific materials. +variables: + company_name: Tailspin Toys + company_description: a crowdfunding platform for board games with a developer theme + product_type: crowdfunding platform + primary_entities: games, publishers, categories + filter_feature: filter games by category and publisher + new_endpoints_feature: endpoints to create, update, and delete games + accessibility_feature: high-contrast mode toggle + documentation_feature: docstrings for all functions + +# Exercises to skip for this scenario. +# List exercise filenames (without the .md extension) from workshop-content/ +# that are NOT applicable to this scenario. +# Leave empty (or omit) if all exercises apply. +skip: [] + +# Notes for workshop authors or maintainers (not shown to participants) +author_notes: > + This is the canonical scenario. All workshop-content/ exercises were + written against this scenario, so no overrides are needed. + When creating a new scenario, use this file as a reference. diff --git a/scripts/new-scenario.py b/scripts/new-scenario.py new file mode 100644 index 00000000..13e591c6 --- /dev/null +++ b/scripts/new-scenario.py @@ -0,0 +1,212 @@ +#!/usr/bin/env python3 +""" +new-scenario.py — Scaffold a new workshop scenario. + +Usage: + python scripts/new-scenario.py + +Creates the directory structure and starter files for a new scenario under +scenarios//. After running this script, follow the instructions +in docs/authoring/new-scenario-guide.md to complete the scenario. +""" + +import sys +import os +import re +import textwrap +from pathlib import Path + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +def fail(message: str) -> None: + """Print an error message and exit with a non-zero status.""" + print(f"Error: {message}", file=sys.stderr) + sys.exit(1) + + +def validate_scenario_id(scenario_id: str) -> None: + """Enforce a simple naming convention: lowercase letters, digits, hyphens.""" + if not re.fullmatch(r"[a-z][a-z0-9-]*", scenario_id): + fail( + f"'{scenario_id}' is not a valid scenario ID. " + "Use only lowercase letters, digits, and hyphens (must start with a letter)." + ) + + +def repo_root() -> Path: + """Return the repository root (the directory that contains scripts/).""" + scripts_dir = Path(__file__).resolve().parent + return scripts_dir.parent + + +# --------------------------------------------------------------------------- +# File content templates +# --------------------------------------------------------------------------- + +SCENARIO_YML_TEMPLATE = """\ +# {scenario_id} — scenario configuration +# Edit every field below before opening a pull request. +# See docs/authoring/new-scenario-guide.md for full documentation. + +# Human-readable name displayed in the workshop +name: {display_name} + +# Short description of the application used in this scenario +description: > + TODO: Describe the sample application in one or two sentences. + What does it do? Who is the fictional company? + +# GitHub repository for the sample application (org/repo) +repository: TODO/TODO + +# Technology stack — used when generating scenario-specific content +tech_stack: + frontend: + name: TODO + language: TODO + notes: > + TODO: Describe the frontend framework and any notes relevant to the exercises. + backend: + name: TODO + language: TODO + notes: > + TODO: Describe the backend framework and any notes relevant to the exercises. + database: TODO + +# Content variables — substituted into workshop content and prompts +variables: + company_name: TODO + company_description: TODO # one-line description, e.g. "a crowdfunding platform for board games" + product_type: TODO # e.g. "e-commerce store", "blog platform" + primary_entities: TODO # e.g. "products, orders, customers" + filter_feature: TODO # what learners build in exercise 3 + new_endpoints_feature: TODO # what the coding agent builds in exercise 4 + accessibility_feature: TODO # what the custom agent builds in exercise 5 + documentation_feature: TODO # what is documented in exercise 4's first task + +# Exercises to skip for this scenario. +# List exercise filenames (without the .md extension) from workshop-content/ +# that are NOT applicable to this scenario. +skip: [] + # Examples: + # - 3-copilot-agent-mode-vscode # skip if the app has no frontend + # - 5-custom-agents # skip if accessibility task doesn't apply + +# Notes for workshop authors or maintainers (not shown to participants) +author_notes: > + TODO: Add any notes for future maintainers about this scenario. +""" + +OVERRIDES_README_TEMPLATE = """\ +# {display_name} — Exercise Overrides + +This directory contains exercise-specific content overrides for the **{display_name}** scenario. + +## How overrides work + +Place a Markdown file here with the **same filename** as an exercise in `workshop-content/` to replace +its contents for this scenario. Typically you only need to rewrite the `## Scenario` section of each +exercise to match your application — copy the original exercise file and replace every Tailspin-specific +reference with your application's equivalent. + +``` +overrides/ + 1-mcp.md ← replaces Scenario section for exercise 1 + 3-copilot-agent-mode-vscode.md ← replaces Scenario section for exercise 3 +``` + +Overrides must NOT be created for exercises listed in the `skip` field of `scenario.yml`. + +See [docs/authoring/new-scenario-guide.md](../../../docs/authoring/new-scenario-guide.md) for full instructions. + +## Files in this directory + +*(None yet — add override files as needed.)* +""" + + +# --------------------------------------------------------------------------- +# Scaffolding logic +# --------------------------------------------------------------------------- + +def title_case(scenario_id: str) -> str: + """Convert 'my-scenario-id' to 'My Scenario Id' as a placeholder name.""" + return " ".join(word.capitalize() for word in scenario_id.split("-")) + + +def create_scenario(scenario_id: str) -> None: + root = repo_root() + scenario_dir = root / "scenarios" / scenario_id + overrides_dir = scenario_dir / "overrides" + + # Guard against overwriting an existing scenario + if scenario_dir.exists(): + fail( + f"scenarios/{scenario_id}/ already exists. " + "Remove it first or choose a different scenario ID." + ) + + display_name = title_case(scenario_id) + + # Create directories + overrides_dir.mkdir(parents=True) + print(f"Created: scenarios/{scenario_id}/") + print(f"Created: scenarios/{scenario_id}/overrides/") + + # Write scenario.yml + yml_content = SCENARIO_YML_TEMPLATE.format( + scenario_id=scenario_id, + display_name=display_name, + ) + (scenario_dir / "scenario.yml").write_text(yml_content, encoding="utf-8") + print(f"Created: scenarios/{scenario_id}/scenario.yml") + + # Write overrides/README.md + overrides_readme = OVERRIDES_README_TEMPLATE.format(display_name=display_name) + (overrides_dir / "README.md").write_text(overrides_readme, encoding="utf-8") + print(f"Created: scenarios/{scenario_id}/overrides/README.md") + + # Summary + print() + print("Scaffolding complete!") + print() + print("Next steps:") + print(f" 1. Edit scenarios/{scenario_id}/scenario.yml — fill in every TODO field.") + print(f" 2. Add exercise overrides to scenarios/{scenario_id}/overrides/ as needed.") + print(f" 3. Run: python scripts/validate-scenario.py {scenario_id}") + print(f" 4. Update the scenario table in scenarios/README.md.") + print() + print("Full instructions: docs/authoring/new-scenario-guide.md") + + +# --------------------------------------------------------------------------- +# Entry point +# --------------------------------------------------------------------------- + +def main() -> None: + if len(sys.argv) != 2: + print( + textwrap.dedent( + """\ + Usage: + python scripts/new-scenario.py + + Example: + python scripts/new-scenario.py java-bookstore + + The scenario ID must be lowercase letters, digits, and hyphens. + """ + ) + ) + sys.exit(1) + + scenario_id = sys.argv[1].strip() + validate_scenario_id(scenario_id) + create_scenario(scenario_id) + + +if __name__ == "__main__": + main() diff --git a/scripts/validate-scenario.py b/scripts/validate-scenario.py new file mode 100644 index 00000000..731cc245 --- /dev/null +++ b/scripts/validate-scenario.py @@ -0,0 +1,379 @@ +#!/usr/bin/env python3 +""" +validate-scenario.py — Validate a workshop scenario's structure and configuration. + +Usage: + python scripts/validate-scenario.py + python scripts/validate-scenario.py --all # validate every scenario + +Exit codes: + 0 All checks passed. + 1 One or more errors found. +""" + +import sys +import os +import re +from pathlib import Path +from typing import Any + + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +def repo_root() -> Path: + """Return the repository root (the directory that contains scripts/).""" + return Path(__file__).resolve().parent.parent + + +def load_yaml(path: Path) -> dict[str, Any]: + """Load a YAML file, returning a dict. Requires PyYAML.""" + try: + import yaml # type: ignore[import] + except ImportError: + # Fall back to a minimal parser for simple flat YAML structures + # so the script works without installing extra dependencies. + return _minimal_yaml_parse(path) + + with open(path, encoding="utf-8") as fh: + return yaml.safe_load(fh) or {} + + +def _minimal_yaml_parse(path: Path) -> dict[str, Any]: + """ + Minimal YAML parser that handles the subset of YAML used in scenario.yml: + - Top-level key: value pairs (strings, empty, block scalars) + - Nested maps at two levels of indentation + - Lists (inline [] or block - items) + Comments and blank lines are ignored. + """ + lines = path.read_text(encoding="utf-8").splitlines() + return _parse_block(lines, 0, 0)[0] + + +def _skip_to_next_indent(lines: list[str], start: int, base_indent: int) -> int: + """Advance past all lines that are more indented than base_indent.""" + i = start + while i < len(lines): + raw = lines[i] + stripped = raw.strip() + if not stripped or stripped.startswith("#"): + i += 1 + continue + indent = len(raw) - len(raw.lstrip()) + if indent <= base_indent: + break + i += 1 + return i + + +def _parse_block( + lines: list[str], start: int, base_indent: int +) -> "tuple[dict[str, Any], int]": + """ + Parse a YAML mapping block starting at `start`, where all keys are + indented exactly `base_indent` spaces. Returns (mapping_dict, next_line_index). + """ + result: dict[str, Any] = {} + i = start + + while i < len(lines): + raw = lines[i] + stripped = raw.strip() + + # Skip blank lines and comments + if not stripped or stripped.startswith("#"): + i += 1 + continue + + indent = len(raw) - len(raw.lstrip()) + + # If we've de-dented below our base, we're done with this block + if indent < base_indent: + break + + # If we're deeper than expected, skip (handled by recursive calls) + if indent > base_indent: + i += 1 + continue + + # Expect a mapping key at this indent level + if ":" not in stripped: + i += 1 + continue + + key, _, rest = stripped.partition(":") + key = key.strip() + rest = rest.strip() + + i += 1 # advance past this key line + + if rest in (">", "|"): + # Block scalar: consume its content lines and mark as present + i = _skip_to_next_indent(lines, i, base_indent) + result[key] = "__present__" + elif rest == "": + # Could be a nested map or an empty value; peek ahead + j = i + while j < len(lines) and (not lines[j].strip() or lines[j].strip().startswith("#")): + j += 1 + + if j < len(lines): + next_indent = len(lines[j]) - len(lines[j].lstrip()) + next_stripped = lines[j].strip() + + if next_indent > base_indent: + if next_stripped.startswith("- "): + items, i = _parse_list(lines, j, next_indent) + result[key] = items + else: + nested, i = _parse_block(lines, j, next_indent) + result[key] = nested + else: + result[key] = "__present__" + else: + result[key] = "__present__" + elif rest == "[]": + result[key] = [] + else: + result[key] = rest.strip("'\"") + + return result, i + + +def _parse_list(lines: list[str], start: int, base_indent: int) -> "tuple[list[str], int]": + """Parse a YAML sequence (list of '- item' lines) at `base_indent`.""" + items: list[str] = [] + i = start + + while i < len(lines): + raw = lines[i] + stripped = raw.strip() + + if not stripped or stripped.startswith("#"): + i += 1 + continue + + indent = len(raw) - len(raw.lstrip()) + + if indent < base_indent: + break + + if stripped.startswith("- "): + item = stripped[2:].strip().strip("'\"") + # Skip commented-out items + if not item.startswith("#"): + items.append(item) + + i += 1 + + return items, i + + +# --------------------------------------------------------------------------- +# Validation checks +# --------------------------------------------------------------------------- + +REQUIRED_TOP_LEVEL_FIELDS = ["name", "description", "repository", "tech_stack", "variables", "skip"] +REQUIRED_VARIABLES = [ + "company_name", + "company_description", + "product_type", + "primary_entities", + "filter_feature", + "new_endpoints_feature", + "accessibility_feature", + "documentation_feature", +] + + +class Validator: + def __init__(self, scenario_id: str) -> None: + self.scenario_id = scenario_id + self.root = repo_root() + self.scenario_dir = self.root / "scenarios" / scenario_id + self.workshop_dir = self.root / "workshop-content" + self.errors: list[str] = [] + self.warnings: list[str] = [] + + def err(self, msg: str) -> None: + self.errors.append(msg) + + def warn(self, msg: str) -> None: + self.warnings.append(msg) + + def exercises(self) -> list[str]: + """Return all exercise filenames (without .md) from workshop-content/.""" + if not self.workshop_dir.exists(): + return [] + return [ + p.stem + for p in sorted(self.workshop_dir.glob("*.md")) + if p.name != "README.md" + ] + + def run(self) -> bool: + """Run all checks. Return True if no errors were found.""" + self._check_directory_exists() + if self.errors: + return False # Cannot continue without the directory + + config = self._check_scenario_yml() + if config is not None: + self._check_required_fields(config) + self._check_variables(config) + skip_list = self._check_skip_list(config) + self._check_overrides(skip_list) + else: + # scenario.yml missing — overrides check may still be useful + self._check_overrides([]) + + return len(self.errors) == 0 + + def _check_directory_exists(self) -> None: + if not self.scenario_dir.is_dir(): + self.err( + f"scenarios/{self.scenario_id}/ does not exist. " + f"Run: python scripts/new-scenario.py {self.scenario_id}" + ) + + def _check_scenario_yml(self) -> "dict[str, Any] | None": + yml_path = self.scenario_dir / "scenario.yml" + if not yml_path.exists(): + self.err(f"scenarios/{self.scenario_id}/scenario.yml is missing.") + return None + try: + config = load_yaml(yml_path) + except Exception as exc: + self.err(f"scenarios/{self.scenario_id}/scenario.yml cannot be parsed: {exc}") + return None + return config + + def _check_required_fields(self, config: dict[str, Any]) -> None: + for field in REQUIRED_TOP_LEVEL_FIELDS: + val = config.get(field) + if val is None: + self.err(f"scenario.yml: required field '{field}' is missing or empty.") + # skip: [] is an empty list — that is valid (means "no exercises skipped") + + def _check_variables(self, config: dict[str, Any]) -> None: + variables = config.get("variables") or {} + if not isinstance(variables, dict): + self.err("scenario.yml: 'variables' must be a mapping.") + return + for var in REQUIRED_VARIABLES: + val = variables.get(var) + if val is None: + self.err(f"scenario.yml: variables.{var} is missing.") + elif isinstance(val, str) and (val.upper().startswith("TODO") or val == "__present__"): + self.warn(f"scenario.yml: variables.{var} still contains a TODO placeholder.") + + # Check top-level fields for TODOs + for field in ["name", "description", "repository"]: + val = config.get(field) + if isinstance(val, str) and val.upper().startswith("TODO"): + self.warn(f"scenario.yml: field '{field}' still contains a TODO placeholder.") + + def _check_skip_list(self, config: dict[str, Any]) -> list[str]: + skip = config.get("skip") or [] + if not isinstance(skip, list): + self.err("scenario.yml: 'skip' must be a list.") + return [] + + valid_exercises = self.exercises() + for entry in skip: + if not isinstance(entry, str): + self.err(f"scenario.yml: skip entry {entry!r} is not a string.") + elif entry not in valid_exercises: + self.err( + f"scenario.yml: skip entry '{entry}' does not match any exercise " + f"in workshop-content/ (valid: {', '.join(valid_exercises)})." + ) + return [s for s in skip if isinstance(s, str)] + + def _check_overrides(self, skip_list: list[str]) -> None: + overrides_dir = self.scenario_dir / "overrides" + if not overrides_dir.is_dir(): + self.warn( + f"scenarios/{self.scenario_id}/overrides/ does not exist. " + "Create it (even empty) to hold exercise overrides." + ) + return + + valid_exercises = set(self.exercises()) + for override_file in overrides_dir.glob("*.md"): + if override_file.name == "README.md": + continue + stem = override_file.stem + if stem not in valid_exercises: + self.err( + f"overrides/{override_file.name}: does not correspond to any exercise " + f"in workshop-content/." + ) + elif stem in skip_list: + self.err( + f"overrides/{override_file.name}: exercise '{stem}' is in the skip " + "list but also has an override — remove one or the other." + ) + + +# --------------------------------------------------------------------------- +# Entry point +# --------------------------------------------------------------------------- + +def validate_one(scenario_id: str) -> bool: + print(f"Validating scenario: {scenario_id}") + v = Validator(scenario_id) + passed = v.run() + + for warning in v.warnings: + print(f" ⚠ {warning}") + for error in v.errors: + print(f" ✗ {error}") + + if passed and not v.warnings: + print(f" ✓ All checks passed.") + elif passed: + print(f" ✓ No errors (but see warnings above).") + else: + print(f" ✗ {len(v.errors)} error(s) found.") + + print() + return passed + + +def main() -> None: + root = repo_root() + scenarios_dir = root / "scenarios" + + if len(sys.argv) < 2: + print(__doc__) + sys.exit(1) + + arg = sys.argv[1] + + if arg == "--all": + if not scenarios_dir.is_dir(): + print("No scenarios/ directory found.", file=sys.stderr) + sys.exit(1) + scenario_ids = [ + d.name + for d in sorted(scenarios_dir.iterdir()) + if d.is_dir() and not d.name.startswith(".") + ] + if not scenario_ids: + print("No scenarios found in scenarios/.") + sys.exit(0) + all_passed = True + for sid in scenario_ids: + if not validate_one(sid): + all_passed = False + sys.exit(0 if all_passed else 1) + else: + passed = validate_one(arg) + sys.exit(0 if passed else 1) + + +if __name__ == "__main__": + main() From be03fdf95ca87eed512d8ce370e60b00bd177f85 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 8 May 2026 23:00:39 +0000 Subject: [PATCH 04/11] Address code review feedback - Extract _PRESENT_MARKER constant in validate-scenario.py - Rename title_case to scenario_id_to_display_name in new-scenario.py - Update Python version requirement to 3.9+ in authoring guide - Remove unused imports (os, re) from validate-scenario.py - Restore client/package-lock.json to match main branch Co-authored-by: GeekTrainer <6109729+GeekTrainer@users.noreply.github.com> --- client/package-lock.json | 26 ++++++++++---------------- docs/authoring/new-scenario-guide.md | 2 +- scripts/new-scenario.py | 4 ++-- scripts/validate-scenario.py | 16 ++++++++++------ 4 files changed, 23 insertions(+), 25 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index d4b233e8..6011fa3d 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1633,6 +1633,7 @@ "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-5.1.1.tgz", "integrity": "sha512-Y1Cs7hhTc+a5E9Va/xwKlAJoariQyHY+5zBgCZg4PFWNYQ1nMN9sjK1zhw1gK69DuqVP++sht/1GZg1aRwmAXQ==", "license": "MIT", + "peer": true, "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^4.0.1", "debug": "^4.4.1", @@ -2171,6 +2172,7 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2323,6 +2325,7 @@ "resolved": "https://registry.npmjs.org/astro/-/astro-5.15.5.tgz", "integrity": "sha512-A56u4H6gFHEb0yRHcGTOADBb7jmEwfDjQpkqVV/Z+ZWlu6mYuwCrIcOUtZjNno0chrRKmOeZWDofW23ql18y3w==", "license": "MIT", + "peer": true, "dependencies": { "@astrojs/compiler": "^2.12.2", "@astrojs/internal-helpers": "0.7.4", @@ -2558,6 +2561,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -5287,6 +5291,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -5664,6 +5669,7 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.1.tgz", "integrity": "sha512-33xGNBsDJAkzt0PvninskHlWnTIPgDtTwhg0U38CUoNP/7H6wI2Cz6dUeoNPbjdTdsYTGuiFFASuUOWovH0SyQ==", "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -5929,6 +5935,7 @@ "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.43.5.tgz", "integrity": "sha512-HQoZArIewxQVNedseDsgMgnRSC4XOXczxXLF9rOJaPIJkg58INOPUiL8aEtzqZIXNSZJyw8NmqObwg/voajiHQ==", "license": "MIT", + "peer": true, "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", @@ -6101,6 +6108,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -6489,6 +6497,7 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", @@ -6930,22 +6939,6 @@ "prettier": "2.8.7" } }, - "node_modules/yaml-language-server/node_modules/prettier": { - "version": "2.8.7", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz", - "integrity": "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==", - "license": "MIT", - "optional": true, - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, "node_modules/yaml-language-server/node_modules/request-light": { "version": "0.5.8", "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.5.8.tgz", @@ -7116,6 +7109,7 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/docs/authoring/new-scenario-guide.md b/docs/authoring/new-scenario-guide.md index 70cc7939..abe2db55 100644 --- a/docs/authoring/new-scenario-guide.md +++ b/docs/authoring/new-scenario-guide.md @@ -4,7 +4,7 @@ This guide walks you through every step needed to introduce a new sample applica ## Prerequisites -- Python 3.8 or later (for the scaffolding and validation scripts). +- Python 3.9 or later (for the scaffolding and validation scripts). - A sample application repository accessible on GitHub. If you don't have one yet, see [Using the AI prompt to create a sample app](#using-the-ai-prompt-to-create-a-sample-app). - Familiarity with the workshop structure — skim through `workshop-content/README.md` and a couple of the exercise files before you begin. diff --git a/scripts/new-scenario.py b/scripts/new-scenario.py index 13e591c6..419012c5 100644 --- a/scripts/new-scenario.py +++ b/scripts/new-scenario.py @@ -132,7 +132,7 @@ def repo_root() -> Path: # Scaffolding logic # --------------------------------------------------------------------------- -def title_case(scenario_id: str) -> str: +def scenario_id_to_display_name(scenario_id: str) -> str: """Convert 'my-scenario-id' to 'My Scenario Id' as a placeholder name.""" return " ".join(word.capitalize() for word in scenario_id.split("-")) @@ -149,7 +149,7 @@ def create_scenario(scenario_id: str) -> None: "Remove it first or choose a different scenario ID." ) - display_name = title_case(scenario_id) + display_name = scenario_id_to_display_name(scenario_id) # Create directories overrides_dir.mkdir(parents=True) diff --git a/scripts/validate-scenario.py b/scripts/validate-scenario.py index 731cc245..a9be4365 100644 --- a/scripts/validate-scenario.py +++ b/scripts/validate-scenario.py @@ -12,12 +12,16 @@ """ import sys -import os -import re from pathlib import Path from typing import Any +# Sentinel used by the minimal YAML parser to indicate that a field was +# detected in the file but its value is a block scalar or nested map that +# the parser did not fully deserialise (e.g. `description: >`). +_PRESENT_MARKER = "__present__" + + # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- @@ -112,7 +116,7 @@ def _parse_block( if rest in (">", "|"): # Block scalar: consume its content lines and mark as present i = _skip_to_next_indent(lines, i, base_indent) - result[key] = "__present__" + result[key] = _PRESENT_MARKER elif rest == "": # Could be a nested map or an empty value; peek ahead j = i @@ -131,9 +135,9 @@ def _parse_block( nested, i = _parse_block(lines, j, next_indent) result[key] = nested else: - result[key] = "__present__" + result[key] = _PRESENT_MARKER else: - result[key] = "__present__" + result[key] = _PRESENT_MARKER elif rest == "[]": result[key] = [] else: @@ -266,7 +270,7 @@ def _check_variables(self, config: dict[str, Any]) -> None: val = variables.get(var) if val is None: self.err(f"scenario.yml: variables.{var} is missing.") - elif isinstance(val, str) and (val.upper().startswith("TODO") or val == "__present__"): + elif isinstance(val, str) and (val.upper().startswith("TODO") or val == _PRESENT_MARKER): self.warn(f"scenario.yml: variables.{var} still contains a TODO placeholder.") # Check top-level fields for TODOs From 85eb7b8d507527d3be2b1a0c31991afac15ab818 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 8 May 2026 23:11:58 +0000 Subject: [PATCH 05/11] Initial plan: create .NET tailspin-toys scenario Co-authored-by: GeekTrainer <6109729+GeekTrainer@users.noreply.github.com> --- client/package-lock.json | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/client/package-lock.json b/client/package-lock.json index 6011fa3d..d4b233e8 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -1633,7 +1633,6 @@ "resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-5.1.1.tgz", "integrity": "sha512-Y1Cs7hhTc+a5E9Va/xwKlAJoariQyHY+5zBgCZg4PFWNYQ1nMN9sjK1zhw1gK69DuqVP++sht/1GZg1aRwmAXQ==", "license": "MIT", - "peer": true, "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^4.0.1", "debug": "^4.4.1", @@ -2172,7 +2171,6 @@ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2325,7 +2323,6 @@ "resolved": "https://registry.npmjs.org/astro/-/astro-5.15.5.tgz", "integrity": "sha512-A56u4H6gFHEb0yRHcGTOADBb7jmEwfDjQpkqVV/Z+ZWlu6mYuwCrIcOUtZjNno0chrRKmOeZWDofW23ql18y3w==", "license": "MIT", - "peer": true, "dependencies": { "@astrojs/compiler": "^2.12.2", "@astrojs/internal-helpers": "0.7.4", @@ -2561,7 +2558,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -5291,7 +5287,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -5669,7 +5664,6 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.1.tgz", "integrity": "sha512-33xGNBsDJAkzt0PvninskHlWnTIPgDtTwhg0U38CUoNP/7H6wI2Cz6dUeoNPbjdTdsYTGuiFFASuUOWovH0SyQ==", "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -5935,7 +5929,6 @@ "resolved": "https://registry.npmjs.org/svelte/-/svelte-5.43.5.tgz", "integrity": "sha512-HQoZArIewxQVNedseDsgMgnRSC4XOXczxXLF9rOJaPIJkg58INOPUiL8aEtzqZIXNSZJyw8NmqObwg/voajiHQ==", "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/remapping": "^2.3.4", "@jridgewell/sourcemap-codec": "^1.5.0", @@ -6108,7 +6101,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -6497,7 +6489,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.1.tgz", "integrity": "sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==", "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", @@ -6939,6 +6930,22 @@ "prettier": "2.8.7" } }, + "node_modules/yaml-language-server/node_modules/prettier": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.7.tgz", + "integrity": "sha512-yPngTo3aXUUmyuTjeTUT75txrf+aMh9FiD7q9ZE/i6r0bPb22g4FsE6Y338PQX1bmfy08i9QQCB7/rcUAVntfw==", + "license": "MIT", + "optional": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/yaml-language-server/node_modules/request-light": { "version": "0.5.8", "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.5.8.tgz", @@ -7109,7 +7116,6 @@ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } From cc7d9fa78f5b9b4fc0c37e919260c07a8308db99 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 8 May 2026 23:22:26 +0000 Subject: [PATCH 06/11] Add .NET tailspin-toys-dotnet scenario with ASP.NET Core backend Co-authored-by: GeekTrainer <6109729+GeekTrainer@users.noreply.github.com> --- scenarios/README.md | 1 + .../overrides/2-custom-instructions.md | 251 ++++++++++++++++++ .../overrides/3-copilot-agent-mode-vscode.md | 224 ++++++++++++++++ .../overrides/4-copilot-coding-agent.md | 201 ++++++++++++++ .../overrides/7-iterating-copilot-work.md | 173 ++++++++++++ .../tailspin-toys-dotnet/overrides/README.md | 24 ++ scenarios/tailspin-toys-dotnet/scenario.yml | 56 ++++ .../.devcontainer/devcontainer.json | 28 ++ .../.github/agents/accessibility.md | 184 +++++++++++++ .../instructions/astro.instructions.md | 67 +++++ .../instructions/csharp-api.instructions.md | 74 ++++++ .../instructions/csharp-tests.instructions.md | 83 ++++++ .../instructions/svelte.instructions.md | 68 +++++ .../instructions/tailwindcss.instructions.md | 54 ++++ .../.github/instructions/ui.instructions.md | 50 ++++ .../.github/workflows/copilot-setup-steps.yml | 37 +++ tailspin-toys-dotnet/README.md | 71 +++++ tailspin-toys-dotnet/client/astro.config.mjs | 21 ++ tailspin-toys-dotnet/client/package.json | 26 ++ .../client/src/components/EmptyState.svelte | 7 + .../client/src/components/ErrorMessage.svelte | 7 + .../client/src/components/GameCard.svelte | 50 ++++ .../client/src/components/GameDetails.svelte | 125 +++++++++ .../client/src/components/GameList.svelte | 57 ++++ .../client/src/components/Header.astro | 44 +++ .../src/components/LoadingSkeleton.svelte | 20 ++ .../client/src/layouts/Layout.astro | 49 ++++ tailspin-toys-dotnet/client/src/middleware.ts | 43 +++ .../client/src/pages/about.astro | 35 +++ .../client/src/pages/game/[id].astro | 24 ++ .../client/src/pages/index.astro | 16 ++ .../client/src/styles/global.css | 1 + tailspin-toys-dotnet/client/svelte.config.js | 5 + tailspin-toys-dotnet/client/tsconfig.json | 5 + .../scripts/run-server-tests.sh | 11 + tailspin-toys-dotnet/scripts/setup-env.sh | 19 ++ tailspin-toys-dotnet/scripts/start-app.sh | 66 +++++ .../Controllers/GamesController.cs | 59 ++++ .../Controllers/PublishersController.cs | 16 ++ .../server/TailspinToys.Api/DTOs/GameDtos.cs | 11 + .../TailspinToys.Api/Data/AppDbContext.cs | 13 + .../TailspinToys.Api/Data/DataSeeder.cs | 110 ++++++++ .../TailspinToys.Api/Models/Category.cs | 8 + .../server/TailspinToys.Api/Models/Game.cs | 13 + .../TailspinToys.Api/Models/Publisher.cs | 8 + .../server/TailspinToys.Api/Program.cs | 22 ++ .../TailspinToys.Api/TailspinToys.Api.csproj | 19 ++ .../appsettings.Development.json | 8 + .../server/TailspinToys.Api/appsettings.json | 13 + .../GamesControllerTests.cs | 173 ++++++++++++ .../TailspinToys.Tests.csproj | 25 ++ tailspin-toys-dotnet/server/TailspinToys.sln | 28 ++ 52 files changed, 2803 insertions(+) create mode 100644 scenarios/tailspin-toys-dotnet/overrides/2-custom-instructions.md create mode 100644 scenarios/tailspin-toys-dotnet/overrides/3-copilot-agent-mode-vscode.md create mode 100644 scenarios/tailspin-toys-dotnet/overrides/4-copilot-coding-agent.md create mode 100644 scenarios/tailspin-toys-dotnet/overrides/7-iterating-copilot-work.md create mode 100644 scenarios/tailspin-toys-dotnet/overrides/README.md create mode 100644 scenarios/tailspin-toys-dotnet/scenario.yml create mode 100644 tailspin-toys-dotnet/.devcontainer/devcontainer.json create mode 100644 tailspin-toys-dotnet/.github/agents/accessibility.md create mode 100644 tailspin-toys-dotnet/.github/instructions/astro.instructions.md create mode 100644 tailspin-toys-dotnet/.github/instructions/csharp-api.instructions.md create mode 100644 tailspin-toys-dotnet/.github/instructions/csharp-tests.instructions.md create mode 100644 tailspin-toys-dotnet/.github/instructions/svelte.instructions.md create mode 100644 tailspin-toys-dotnet/.github/instructions/tailwindcss.instructions.md create mode 100644 tailspin-toys-dotnet/.github/instructions/ui.instructions.md create mode 100644 tailspin-toys-dotnet/.github/workflows/copilot-setup-steps.yml create mode 100644 tailspin-toys-dotnet/README.md create mode 100644 tailspin-toys-dotnet/client/astro.config.mjs create mode 100644 tailspin-toys-dotnet/client/package.json create mode 100644 tailspin-toys-dotnet/client/src/components/EmptyState.svelte create mode 100644 tailspin-toys-dotnet/client/src/components/ErrorMessage.svelte create mode 100644 tailspin-toys-dotnet/client/src/components/GameCard.svelte create mode 100644 tailspin-toys-dotnet/client/src/components/GameDetails.svelte create mode 100644 tailspin-toys-dotnet/client/src/components/GameList.svelte create mode 100644 tailspin-toys-dotnet/client/src/components/Header.astro create mode 100644 tailspin-toys-dotnet/client/src/components/LoadingSkeleton.svelte create mode 100644 tailspin-toys-dotnet/client/src/layouts/Layout.astro create mode 100644 tailspin-toys-dotnet/client/src/middleware.ts create mode 100644 tailspin-toys-dotnet/client/src/pages/about.astro create mode 100644 tailspin-toys-dotnet/client/src/pages/game/[id].astro create mode 100644 tailspin-toys-dotnet/client/src/pages/index.astro create mode 100644 tailspin-toys-dotnet/client/src/styles/global.css create mode 100644 tailspin-toys-dotnet/client/svelte.config.js create mode 100644 tailspin-toys-dotnet/client/tsconfig.json create mode 100755 tailspin-toys-dotnet/scripts/run-server-tests.sh create mode 100755 tailspin-toys-dotnet/scripts/setup-env.sh create mode 100755 tailspin-toys-dotnet/scripts/start-app.sh create mode 100644 tailspin-toys-dotnet/server/TailspinToys.Api/Controllers/GamesController.cs create mode 100644 tailspin-toys-dotnet/server/TailspinToys.Api/Controllers/PublishersController.cs create mode 100644 tailspin-toys-dotnet/server/TailspinToys.Api/DTOs/GameDtos.cs create mode 100644 tailspin-toys-dotnet/server/TailspinToys.Api/Data/AppDbContext.cs create mode 100644 tailspin-toys-dotnet/server/TailspinToys.Api/Data/DataSeeder.cs create mode 100644 tailspin-toys-dotnet/server/TailspinToys.Api/Models/Category.cs create mode 100644 tailspin-toys-dotnet/server/TailspinToys.Api/Models/Game.cs create mode 100644 tailspin-toys-dotnet/server/TailspinToys.Api/Models/Publisher.cs create mode 100644 tailspin-toys-dotnet/server/TailspinToys.Api/Program.cs create mode 100644 tailspin-toys-dotnet/server/TailspinToys.Api/TailspinToys.Api.csproj create mode 100644 tailspin-toys-dotnet/server/TailspinToys.Api/appsettings.Development.json create mode 100644 tailspin-toys-dotnet/server/TailspinToys.Api/appsettings.json create mode 100644 tailspin-toys-dotnet/server/TailspinToys.Tests/GamesControllerTests.cs create mode 100644 tailspin-toys-dotnet/server/TailspinToys.Tests/TailspinToys.Tests.csproj create mode 100644 tailspin-toys-dotnet/server/TailspinToys.sln diff --git a/scenarios/README.md b/scenarios/README.md index bde21d5a..29db8fc1 100644 --- a/scenarios/README.md +++ b/scenarios/README.md @@ -39,6 +39,7 @@ See [docs/authoring/new-scenario-guide.md](../docs/authoring/new-scenario-guide. | Scenario ID | Application | Tech stack | Status | |---|---|---|---| | `tailspin-toys` | Tailspin Toys (crowdfunding for board games) | Python/Flask · Astro/Svelte | ✅ Default | +| `tailspin-toys-dotnet` | Tailspin Toys .NET (crowdfunding for board games) | ASP.NET Core · Astro/Svelte | ✅ Available | ## Adding a new scenario diff --git a/scenarios/tailspin-toys-dotnet/overrides/2-custom-instructions.md b/scenarios/tailspin-toys-dotnet/overrides/2-custom-instructions.md new file mode 100644 index 00000000..40229042 --- /dev/null +++ b/scenarios/tailspin-toys-dotnet/overrides/2-custom-instructions.md @@ -0,0 +1,251 @@ +# Exercise 2 - Providing context to Copilot with instruction files + +| [← Previous lesson: Model Context Protocol (MCP)][previous-lesson] | [Next lesson: Copilot agent mode →][next-lesson] | +|:--|--:| + +Context is key across many aspects of life, and when working with generative AI. If you're performing a task which needs to be completed a particular way, or if a piece of background information is important, you want to ensure Copilot has access to that information. You can use [instruction files][instruction-files] to provide guidance so that Copilot not only understands what you want it to do but also how you want it to be done. + +In this exercise, you will learn how to: + +- provide Copilot with project-specific context, coding guidelines and documentation standards using [repository custom instructions][repository-custom-instructions] **.github/copilot-instructions.md**. +- provide path instruction files to guide Copilot for repetitive or templated tasks on specific types of files. +- implement both repository-wide instructions and task-specific instructions. + +> [!IMPORTANT] +> Note that the code generated may diverge from some of the standards you set. AI tools like Copilot are non-deterministic, and may not always provide the same result. The other files in the codebase do not contain XML documentation comments or comment headers, which could lead Copilot in another direction. Consistency is key, so making sure that your code follows the established patterns is important. You can always follow-up in chat and ask Copilot to follow your coding standards, which will help guide it in the right direction. + +## Scenario + +As any good dev shop, Tailspin Toys has a set of guidelines and requirements for development practices. These include: + +- API always needs unit tests. +- UI should be in dark mode and have a modern feel. +- Documentation should be added to code in the form of XML documentation comments. +- A block of comments should be added to the head of each file describing what the file does. + +Through the use of instruction files you'll ensure Copilot has the right information to perform the tasks in alignment with the practices highlighted. + +## Before you begin + +You're going to be making some code changes, so you should follow the usual practice of creating a new branch to work in. This will allow you to make changes without affecting the main branch until you're ready. + +1. Return to your codespace from the previous exercise. +2. Open a new terminal window inside your codespace by selecting Ctl+\`. +3. Create and switch to a new branch by running the following command in the terminal: + + ```bash + git checkout -b add-filters + ``` + +## Custom instructions + +Custom instructions allow you to provide context and preferences to Copilot chat, so that it can better understand your coding style and requirements. This is a powerful feature that can help you steer Copilot to get more relevant suggestions and code snippets. You can specify your preferred coding conventions, libraries, and even the types of comments you like to include in your code. You can create instructions for your entire repository, or for specific types of files for task-level context. + +There are two types of instructions files: + +- **.github/copilot-instructions.md**, a single instruction file sent to Copilot for **every** chat prompt for the repository. This file should contain project-level information, context which is relevant for most chat requests sent to Copilot. This could include the tech stack being used, an overview of what's being built and best practices, and other global guidance for Copilot. +- **\*.instructions.md** files can be created for specific tasks or file types. You can use **\*.instructions.md** files to provide guidelines for particular languages (like C# or TypeScript), or for tasks like creating an ASP.NET Core controller or a new set of unit tests. + +> [!NOTE] +> When working in your IDE, instructions files are only used for code generation in Copilot Chat, and not used for code completions or next edit suggestions. +> +> Copilot coding agent will utilize both repository level and \*.instructions with `applyTo` header matter when generating code. + +## Best practices for managing instructions files + +A full conversation about creating instructions files is beyond the scope of the workshop. However, the examples provided in the sample project provide a representative example of how to approach their management. At a high level: + +- Keep instructions in **copilot-instructions.md** focused on project-level guidance, such as a description of what's being built, the structure of the project, and global coding standards. +- Use **\*.instructions.md** files to provide specific instructions for file types (unit tests, API controllers, frontend components), or for specific tasks. +- Use natural language in your instructions files. Keep guidance clear. Provide examples of how code should (and shouldn't) look. + +There isn't one specific way to create instructions files, just as there isn't one specific way to use AI. You will find through experimentation what works best for your project. The guidance provided here and the [resources](#resources) below should help you get started. + +> [!TIP] +> Every project using GitHub Copilot should have a robust collection of instructions files to provide context and best guide code generation. As you explore the instructions files in the project, you may notice there are ones for numerous types of files and tasks, including [UI updates](../.github/instructions/ui.instructions.md) and [Astro](../.github/instructions/astro.instructions.md). The investment made in instructions files will greatly enhance the quality of code suggestion from Copilot, ensuring it better matches the style and requirements your organization has. +> +> You can even have Copilot aid in generating instructions files by selecting the gear icon for **Configure Chat** in Copilot chat and selecting **Generate Agent Instructions**. +> +> ![Screenshot of option in GitHub Copilot chat with configure chat highlighted and generate agent instructions highlighted](./images/ex2-generate-instructions.png) + +## Use GitHub Copilot Chat before updating custom instructions + +To see the impact of custom instructions, you'll start by sending a prompt with the current version of the files. You'll then make some updates, send the same prompt again, and note the difference. + +1. Return to your codespace. +2. Close any open files in your codespace from the previous exercises. This will ensure Copilot has the context you want it to have. +3. Open `server/TailspinToys.Api/Controllers/PublishersController.cs`, an empty controller file. +4. If **Copilot chat** is not already open, open it by selecting the Copilot icon towards the top of your codespace. +5. Create a new chat session by typing `/clear` into the chat window and selecting Enter (or return on a Mac). +6. Select **Ask** from the modes dropdown. + + ![Chat mode selection dialog with Ask mode highlighted](./images/ex2-select-chat-mode.png) + +7. Send the following prompt to create a new endpoint to return all publishers: + + ```plaintext + Create a new endpoint to return a list of all publishers. It should return the name and id for all publishers. + ``` + +8. Copilot explores the project to learn how best to implement the code, and generates a list of suggestions, which may include code for `PublishersController.cs` and tests to ensure the new code runs correctly. +9. Explore the code, noticing the generated code uses C# types and nullable reference types because, as you'll see, the custom instructions includes the directive to include them. +10. Notice the generated code **is missing** XML documentation comments or a file header comment - or both! + +> [!IMPORTANT] +> As highlighted previously, GitHub Copilot and LLM tools are probabilistic, not deterministic. As a result, the exact code generated may vary, and there's even a chance it'll abide by your rules without you spelling it out! But to aid consistency in code you should always document anything you want to ensure Copilot should understand about how you want your code generated. + +## Add new repository standards to copilot-instructions.md + +As highlighted previously, `copilot-instructions.md` is designed to provide project-level information to Copilot. Let's ensure repository coding standards are documented to improve code suggestions from Copilot chat. + +1. Return to your codespace. +2. Open `.github/copilot-instructions.md`. +3. Explore the file, noting the brief description of the project and sections for **Code standards**, **Scripts** and **GitHub Actions Workflows**. These are applicable to any interactions you'd have with Copilot, are robust, and provide clear guidance on what you're doing and how you want to accomplish it. +4. Locate the **Code formatting requirements** section. Note how it contains a note to use C# nullable reference types and standard C# naming conventions. +5. Add the following lines of markdown right below the note about nullable reference types to instruct Copilot to add XML documentation comments and file headers: + + ```markdown + - Every public type, method, and property should have XML documentation comments (``, ``, ``). + - Before the namespace declaration, add a comment block to the file that explains its purpose. + ``` + +6. Close **copilot-instructions.md**. +7. Select **New Chat** in Copilot chat to clear the buffer and start a new conversation. +8. Return to **server/TailspinToys.Api/Controllers/PublishersController.cs** to ensure focus is set correctly. +9. Send the same prompt as before to create the endpoint. + + ```plaintext + Create a new endpoint to return a list of all publishers. It should return the name and id for all publishers. + ``` + +10. Notice how the newly generated code includes a file header comment at the top which resembles the following: + + ```csharp + // PublishersController.cs + // Provides API endpoints for retrieving publisher information + // for the Tailspin Toys crowdfunding platform. + ``` + +11. Notice how the newly generated code includes XML documentation comments inside the action method which resemble the following: + + ```csharp + /// + /// Returns a list of all publishers with their id and name. + /// + /// A JSON array of publisher objects containing id and name. + ``` + +12. Notice the generated code now includes XML documentation as well as a comment block at the top! +13. Also note how the existing code isn't updated, but of course you could ask Copilot to perform that operation if you so desired! +14. **Don't implement the suggested changes**, as you'll be doing that in the next section. + +> [!NOTE] +> If you accepted the changes, you can always select the **Undo** button towards the top right of the Copilot chat window. + +From this section, you explored how the custom instructions file has provided Copilot with the context it needs to generate code that follows the established guidelines. + +## Instructions for specific tasks or files + +Coding is often repetitive, with developers performing similar tasks on a regular basis. Copilot is wonderful for allowing you to offload the mundane, like adding endpoints, generating tests, or building a new component. But all code has a set of requirements, and often require a particular template or structure to be followed. **\*.instructions.md** files allow you to provide tailored guidance for these types of tasks and files. They can be added manually when using Copilot Chat, or can have an `applyTo:` tag added to the top of the file to have Copilot automatically use them for specific files. + +## Explore a task-specific custom instructions file + +You want to create a new endpoint to list all publishers, following the same pattern used for the existing [games controller][games-controller], and to create tests which follow the same pattern as the existing [games controller tests][games-tests]. An instruction file has already been created; let's explore it and see the difference in code it generates. + +1. Open `.github/instructions/csharp-tests.instructions.md`. +2. Note the `applyTo:` section at the top, which contains a filter for all test files in the `server/TailspinToys.Tests` directory ending in `Tests.cs`. Whenever Copilot Chat interacts with a file which matches this pattern it will automatically use the guidance provided in this file. +3. Note the file contains guidance about how xUnit tests should be created, and how to use the EF Core in-memory provider for testing database functionality. +4. Open `.github/instructions/csharp-api.instructions.md`. +5. Review the following entries inside the instruction file, which includes: + + - an overview of requirements, including that tests must be created, and controllers inherit from `ControllerBase` with `[ApiController]`. + - a link to the previously mentioned `csharp-tests.instructions.md` file. + - links to two existing files which follow the patterns you want - both the games controller and its tests. Notice how these are setup as normal markdown links, allowing an instruction file to incorporate additional files for context. + +6. Return to `server/TailspinToys.Api/Controllers/PublishersController.cs` to ensure focus is set correctly. +7. Return to Copilot Chat and select **New Chat** to start a new session. +8. Select **Edit** from the mode dropdown, which will allow Copilot to update multiple files. + + ![Copilot Chat mode selector with Edit chosen and highlighted](./images/ex2-select-edit-mode.png) + +> [!NOTE] +> If you have any issues running the tests in this part of the exercise, please undo your changes and retry from the above step using **Agent** mode instead. + +9. Select the **Add Context** button to open the context dialog +10. If prompted to allow the codespace to see text and images copied to the clipboard, select **Allow**. +11. Select **Instructions** from the dropdown at the top of your codespace. + +> [!TIP] +> If the list of options is long, you can type **instructions** to filter to the Instructions option then select **Instructions**. + +12. Select **csharp-api .github/instructions** to add the instruction file to the context. + + ![Screenshot showing the instruction file being added into Copilot Chat](images/ex2-add-instructions-file.png) + +13. Send the same prompt as before to generate the desired endpoint: + + ```plaintext + Create a new endpoint to return a list of all publishers. It should return the name and id for all publishers. + ``` + +> [!NOTE] +> While the up-arrow shortcut to resend a prior command is handy, it will reset any context you might add as well. If you added in the instructions file as context, then use the up arrow, it will remove the instructions file. For this particular step, make sure you copy/paste (or type) the command to avoid accidentally removing context. + +14. Note the **References** section and how it uses the **csharp-api.instructions.md** file to provide context. If you use instructions files with Copilot agent mode, you will notice that Copilot explores and reads the files referenced in the instructions file. + + ![Screenshot of the references section, showing the included instructions file](./images/ex2-copilot-instructions-references.png) + +15. Copilot generates the files. Notice how it generates updates across multiple files, like **PublishersController.cs** and **PublishersControllerTests.cs** + +> [!NOTE] +> Note that the code generated may diverge from some of the standards we set. AI tools like Copilot are non-deterministic, and may not always provide the same result. The other files in our codebase do not contain XML documentation comments or comment headers, which could lead Copilot in another direction. Consistency is key, so making sure that your code follows the established patterns is important. You can always follow-up in chat and ask Copilot to follow your coding standards, which will help guide it in the right direction. + +16. After reviewing the code, select **Keep** in Copilot Chat to accept the changes. +17. Open a terminal window by selecting Ctl+\`. +18. Run the tests by running the script with the following command: + + ```sh + ./scripts/run-server-tests.sh + ``` + +19. Once the code is correct, and all tests pass, open the **Source Control** panel on the left of the Codespace and review the changes made by Copilot. +20. Stage the changes by selecting the **+** icon in the **Source Control** panel. +21. Generate a commit message using the **Sparkle** button. + + ![Screenshot of the Source Control panel showing the changes made](images/ex2-source-control-changes.png) + +22. Commit the changes to your repository by selecting **Commit**. + +## Summary and next steps + +Congratulations! You explored how to ensure Copilot has the right context to generate code following the practices your organization has set forth. This can be done at a repository level with the **.github/copilot-instructions.md** file, or on a task basis with instruction files. You explored how to: + +- provide Copilot with project-specific context, coding guidelines and documentation standards using custom instructions (.github/copilot-instructions.md). +- use instruction files to guide Copilot for repetitive or templated tasks. +- implement both repository-wide instructions and task-specific instructions. + +Next we'll use [agent mode to add functionality to the site][next-lesson]. + +## Resources + +- [Instruction files for GitHub Copilot customization][instruction-files] +- [5 tips for writing better custom instructions for Copilot][copilot-instructions-five-tips] +- [Best practices for creating custom instructions][instructions-best-practices] +- [Personal custom instructions for GitHub Copilot][personal-instructions] +- [Awesome Copilot - a collection of instructions files and other resources][awesome-copilot] + +--- + +| [← Previous lesson: Model Context Protocol (MCP)][previous-lesson] | [Next lesson: Copilot agent mode →][next-lesson] | +|:--|--:| + +[previous-lesson]: ./1-mcp.md +[next-lesson]: ./3-copilot-agent-mode-vscode.md +[instruction-files]: https://code.visualstudio.com/docs/copilot/copilot-customization +[games-controller]: ../server/TailspinToys.Api/Controllers/GamesController.cs +[games-tests]: ../server/TailspinToys.Tests/GamesControllerTests.cs +[instructions-best-practices]: https://docs.github.com/enterprise-cloud@latest/copilot/using-github-copilot/coding-agent/best-practices-for-using-copilot-to-work-on-tasks#adding-custom-instructions-to-your-repository +[personal-instructions]: https://docs.github.com/copilot/customizing-copilot/adding-personal-custom-instructions-for-github-copilot +[copilot-instructions-five-tips]: https://github.blog/ai-and-ml/github-copilot/5-tips-for-writing-better-custom-instructions-for-copilot/ +[awesome-copilot]: https://github.com/github/awesome-copilot +[repository-custom-instructions]: https://docs.github.com/copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot diff --git a/scenarios/tailspin-toys-dotnet/overrides/3-copilot-agent-mode-vscode.md b/scenarios/tailspin-toys-dotnet/overrides/3-copilot-agent-mode-vscode.md new file mode 100644 index 00000000..36f99e45 --- /dev/null +++ b/scenarios/tailspin-toys-dotnet/overrides/3-copilot-agent-mode-vscode.md @@ -0,0 +1,224 @@ +# Exercise 3 - Adding new functionality with Copilot Agent Mode + +| [← Previous lesson: Custom instructions][previous-lesson] | [Next lesson: GitHub Copilot coding agent →][next-lesson] | +|:--|--:| + +Even the simplest of updates to an application typically require updates to multiple files and operations to be performed like running tests. As a developer your flow typically involves tracking down all the necessary files, making the changes, running the tests, debugging, figuring out which file was missed, making another update... The list goes on and on. + +This is where Copilot Agent Mode comes into play. + +Copilot Agent Mode is built to act more autonomously in your IDE. It behaves in a similar fashion to a developer, starting by exploring the existing project structure, performing the necessary updates, running tasks like tests, and automatically fixing any discovered mistakes. Let's explore how you can use Agent Mode to introduce new functionality to your site. + +> [!NOTE] +> While the names are similar, agent mode and coding agent are built for two different types of experiences. Agent mode performs its tasks in your IDE, allowing for quick feedback cycles and interaction. Coding agent is designed as a peer programmer, working asynchronously like a member of the team, interacting with you via issues and pull requests. + +In this exercise, you will learn how: + +- Copilot Agent Mode can explore your project, identify relevant files, and make coordinated changes. +- GitHub Copilot Agent Mode can implement new features across both backend and frontend codebases. +- to review changes and tests generated by Copilot Agent Mode before merging into your codebase. + +## Scenario + +As the list of games grows, you want to allow users to filter by category. This will require updating both the API and UI, and updating the tests for the API. With the help of Copilot Agent Mode you'll work with your AI pair programmer to add the new feature! + +## Running the Tailspin Toys website + +Before you make any changes, let's explore the Tailspin Toys website to understand its current functionality. + +The website is a crowdfunding platform for board games with a developer theme. It allows users to list games and display details about them. The website has two main components: the front-end (written in Svelte) and the backend (written in C#). + +### Starting the website + +To make running the website easier, a script has been provided that will start both the front-end and back-end servers. You can run this script in your GitHub Codespace to start the website with the following instructions: + +1. Return to your codespace. You'll continue working in your current branch. +2. Open a new terminal window inside your codespace by selecting Ctl + \`. +3. Run the following script to start the website: + + ```bash + scripts/start-app.sh + ``` + + Once the script is running, you should see output indicating that both the front-end and back-end servers are running, similar to the below: + + ```bash + Server (ASP.NET Core) running at: http://localhost:5100 + Client (Astro) server running at: http://localhost:4321 + ``` + +> [!NOTE] +> If a dialog box opens prompting you to open a browser window for `http://localhost:5100` close it by selecting the **x**. + +4. Open the website by using Ctrl-**Click** (or Cmd-**Click** on a Mac) on the client address `http://localhost:4321` in the terminal. + +> [!NOTE] +> When using a codespace, selecting a link for the localhost URL from the Codespace terminal will automatically redirect you to `https://-4321.app.github.dev/`. This is a private tunnel to your codespace, which is now hosting your web server! + +### Exploring the website + +Once the website is running, you can explore its functionality. The main features of the website include: + +- **Home Page**: Displays a list of board games with their titles, images, and descriptions. +- **Game Details Page**: When you select a game, you'll be brought to a details page with more information about the game, including its title, description, publisher and category. + +## Explore the backlog with Copilot + +The initial implementation of the website is functional, but we want to enhance it by adding new capabilities. Let's start off by reviewing the backlog. Ask GitHub Copilot to show you the backlog of items that we created in the previous exercise. + +1. Return to your codespace. +2. Open **Copilot Chat**. +3. Create a new chat session by selecting the **New Chat** button, which will remove any previous context. +4. Ensure **Agent** is selected from the list of modes. +5. Select **Claude Sonnet 4.5** from the list of available models. + +> [!IMPORTANT] +> The authors of this lab are not indicating a preference towards one model or another. When building this lab, we used Claude Sonnet 4.5, and as such are including that in the instructions. The hope is the code suggestions you receive will be relatively consistent to ensure a good experience. However, because LLMs are probabilistic, you may notice the suggestions received differ from what is indicated in the lab. This is perfectly normal and expected. + +6. Open the GitHub Copilot Chat, if it is not already open. +7. Switch to **Agent** mode, if you are not already in agent mode. +8. Select **Claude Sonnet 4.5** from the list of available models. + +> [!IMPORTANT] +> The authors of this lab are not indicating a preference towards one model or another. When building this lab, we used Claude Sonnet 4.5, and as such are including that in the instructions. The hope is the code suggestions you receive will be relatively consistent to ensure a good experience. However, because LLMs are probabilistic, you may notice the suggestions received differ from what is indicated in the lab. This is perfectly normal and expected. + +> [!NOTE] +> Because of the probabilistic nature of LLMs, Copilot may utilize a different MCP command, but should still be able to complete the task. + +8. Ask Copilot about the backlog of issues by sending the following prompt to Copilot: + + ```plaintext + Please show me the backlog of items from my GitHub repository. Help me prioritize them based on those which will be most useful to the user. + ``` +9. Select **Continue** to run the command to list all issues. +10. Review the generated list of issues. + +Notice how Copilot has even prioritized the items for you, based on the ones that it thinks will be most useful to the user. + +## Review instructions files + +Before kicking off the agent to generate the code, it's a good time to review the instructions file you'll use to provide Copilot context for its work. You're going to take advantage of the [user interface (UI)](../.github/instructions/ui.instructions.md) file, which contains context on how to approach adding functionality to the website. + +1. In your codespace, navigate to **.github/instructions/ui.instructions.md**. +2. Take note of the overall guidance on how to approach adding functionality. This includes: + - An overview of the architecture. + - Principles for component design, testability and accessibility. + - Links to specific instructions files for various file types, including: + - Astro + - Svelte + - Tailwind CSS + +> [!TIP] +> Instructions files allow you to reference both other instructions files and files in your project. The paths are relative to the location of the instructions file. This allows for reuse, breaking down complex instructions into smaller more manageable chunks, and providing examples and templates. + +## Implement the filtering functionality + +To implement filtering, no less than three separate updates will need to be made to the application: + +- A new endpoint added to the API +- A new set of tests for the new endpoint +- Updates to the UI to introduce the functionality + +In addition, the tests need to run (and pass) before you merge everything into your codebase. Copilot Agent Mode can perform these tasks for you! Let's add the functionality. + +1. You can continue in the current conversation with Copilot, or start a new one by selecting **New Chat**. +2. Select **Add Context**, **Instructions**, and **ui** as the instructions file. + + ![Screenshot showing an example of selecting the UI instructions file](images/ex3-select-instructions-file.png) + +3. Ensure **Agent** mode is still selected. + + ![Screenshot of Copilot Chat mode selection with Agent highlighted](./images/shared-agent-mode-dropdown.png) + +4. Ensure **Claude Sonnet 4.5** is still selected for the model. +5. Prompt Copilot to implement the functionality based on the issue you created earlier by using the following prompt: + + ```plaintext + Please update the site to include filtering by publisher and category based on the requirements from the related GitHub issue in the backlog. Ensure all tests are passing before completion. The server is already running, so you do not need to start it up. + ``` + +6. Watch as Copilot begins by exploring the project, locating the files associated with the desired functionality. You should see it finding both the API and UI definitions, as well as the tests. It then begins modifying the files and running the tests. + + ![Screenshot showing Copilot exploring the project files](images/ex3-agent-mode-explores.png) + +> [!NOTE] +> You will notice that Copilot will perform several tasks, like exploring the project, modifying files, and running tests. It may take a few minutes depending on the complexity of the task and the codebase. During that process, you may notice **Keep** and **Undo** buttons appear in the code editor. When Copilot is finished, you will have a **Keep** or **Undo** for all of the changes, so you do not need to select them while work is in progress. + +7. As prompted by Copilot, select **Continue** to run the tests. + + ![Screenshot showing a dialog in the Copilot Chat pane asking the user to confirm they are happy to run tests](images/ex3-agent-mode-run-tests.png) + +8. You may experience some pauses and even see some tests fail throughout the process. That's okay! Copilot works back and forth between code generation and tests until it completes the task and doesn't detect any errors. + + ![Screenshot showing a complete Chat session with Copilot Agent Mode](images/ex3-agent-mode-proposed-changes.png) + +9. Explore the generated code for any potential issues. + +> [!IMPORTANT] +> Remember, it's always important to review the code that Copilot or any AI tools generate. + +10. Return to the browser with the website running. Explore the new functionality! +11. Once you've confirmed everything works and reviewed the code, select **Keep** in the Copilot Chat window. + +## Publish the branch and create a pull request + +With your changes created locally you're ready to create a pull request (PR) to allow for your team to review your suggested changes and work through your DevOps process. The first step in that process is to publish the branch. Let's take care of that first. + +1. Navigate to the **Source Control** panel in the Codespace and review the changes made by Copilot. +2. Stage the changes by selecting the **+** icon. +3. Generate a commit message using the **Sparkle** button. + + ![Screenshot of the Source Control panel showing the changes made](images/ex3-source-control-changes.png) + +4. Select **Publish** to push the branch to your repository. + +## Create the pull request + +There are several ways to create a pull request, including through github.com and the GitHub command-line interface (CLI). But since you're already working with GitHub Copilot, let's let it create the PR for you! You can have it find the relevant issue and create the PR with an association to the located issue. + +1. Navigate to the Copilot Chat panel and select **New Chat** to start a new session. +2. Ask Copilot to create a PR for you: + + ```plaintext + Find the issue in the repo related to filtering by category and publisher. Create a new pull request for the current add-filters branch, and associate it with the correct issue. + ``` + +3. As needed, select **Continue** to allow Copilot to perform the tasks necessary to gather information and perform operations. +4. Notice how Copilot searches through the issues, finds the right one, and creates the PR. +5. Select the link generated by Copilot to review your pull request, but please **don't merge it yet**. + +## Summary and next steps + +Congratulations! In this exercise, we explored how to use GitHub Copilot Agent Mode to add new capabilities to the Tailspin Toys website. We learned how: + +- GitHub Copilot Agent Mode can implement new features across both backend and frontend codebases. +- Copilot Agent Mode can explore your project, identify relevant files, and make coordinated changes. +- to review changes and tests generated by Copilot Agent Mode before merging into your codebase. + +Now let's [return to our coding agent][next-lesson] to see how well it did with the issues we assigned to it. + +### Bonus exploration exercise – Implement paging + +As the list of games grows there will be a need for paging to be enabled. Using the skills you learned in this exercise, prompt Copilot to update the site to implement paging. Some considerations for the code include: + +- follow the existing best practices, including using the existing instructions files. +- consider how you want paging implemented, if you want to allow the user to select the page size or for it to be hard-coded. +- as you create the prompt ensure you provide Copilot with the necessary guidance to create the implementation as you desire. +- you may need to iterate with GitHub Copilot, asking for changes and providing context. This is the normal flow when working with Copilot! + +## Resources + +- [Coding agent 101][coding-agent-101] +- [Copilot ask, edit, and agent modes: What they do and when to use them][choose-mode] +- [Agent mode in VS Code][vs-code-agent-mode] + +--- + +| [← Previous lesson: Custom instructions][previous-lesson] | [Next lesson: GitHub Copilot coding agent →][next-lesson] | +|:--|--:| + +[previous-lesson]: ./2-custom-instructions.md +[next-lesson]: ./4-copilot-coding-agent.md +[coding-agent-101]: https://github.blog/ai-and-ml/github-copilot/agent-mode-101-all-about-github-copilots-powerful-mode/ +[choose-mode]: https://github.blog/ai-and-ml/github-copilot/copilot-ask-edit-and-agent-modes-what-they-do-and-when-to-use-them/ +[vs-code-agent-mode]: https://code.visualstudio.com/docs/copilot/chat/chat-agent-mode diff --git a/scenarios/tailspin-toys-dotnet/overrides/4-copilot-coding-agent.md b/scenarios/tailspin-toys-dotnet/overrides/4-copilot-coding-agent.md new file mode 100644 index 00000000..8d367116 --- /dev/null +++ b/scenarios/tailspin-toys-dotnet/overrides/4-copilot-coding-agent.md @@ -0,0 +1,201 @@ +# Exercise 4 - GitHub Copilot coding agent + +| [← Previous lesson: Copilot agent mode][previous-lesson] | [Next lesson: Reviewing coding agent →][next-lesson] | +|:--|--:| + +There are likely very few, if any, organizations who don't struggle with tech debt. This could be unresolved security issues, legacy code requiring updates, or feature requests which have languished on the backlog because there just wasn't the time to implement them. GitHub Copilot's coding agent is built to perform tasks such as updating code and adding functionality, all in an autonomous fashion. Once the agent completes its work, it generates a draft PR ready for a human developer to review. This allows offloading of tedious tasks and an acceleration of the development process, and frees developers to focus on larger picture items. + +You'll explore the following with Copilot coding agent: + +- customizing the environment for generating code. +- ensuring operations are performed securely. +- the importance of clearly scoped issues. +- assigning issues to Copilot. + +## Scenarios + +Tailspin Toys has some tech debt they'd like to address. The contractors initially hired to create the first version of the site left the documentation in an unideal state - and by that you'll notice it's completely lacking. As a first step, they'd like to see docstrings or the equivalent added to all functions in the application. + +Additionally, the design team is ready to get to work on building the UX for managing games. They don't need a full implementation yet, but they at least need some endpoints they can use for testing. Specifically, they need endpoints for the games API which will allow them to create, update and delete games. This is currently a blocker, but there are other issues which are of higher priority at the moment. + +These are both examples of tasks which can quickly find themselves deprioritized, and are great to assign to Copilot coding agent. Copilot coding agent can then work on them asynchronously, allowing the developer to focus on other tasks, then return to review Copilot's work and ensure everything is as expected. + +## Introducing GitHub Copilot coding agent + +[GitHub Copilot coding agent][coding-agent-overview] can perform tasks in the background, much in the same way a human developer would. And, just like with working with a human developer, this can be done in multiple ways, including [assigning a GitHub issue to Copilot][assign-issue]. Once assigned, Copilot will create a draft pull request to track its progress, setup an environment, and begin working on the task. You can dig into Copilot's session while it's still in flight or after its completed. Once its ready for you to review the proposed solution, it'll tag you in the pull request! + +## The importance of well-scoped instructions + +While it can often feel like it, there is no magic in GitHub Copilot. There are no magic solutions available, where you can with just a couple of sentences snap your fingers and let AI perform the entire task for you. In fact, even seemingly straight-forward operations can often have fair amount of complexity when you peel back the layers. + +As a result, you want to [be mindful about how you approach assigning tasks to Copilot coding agent][coding-agent-best-practices]. Working with Copilot as an AI pair programmer is typically the best approach. Approach tasks, big and small, following the same strategy you would without Copilot - work in stages, learn, experiment, and adapt accordingly. + +As always, the fundamentals of software development do not change with the addition of generative AI. + +## Setting up the dev environment for the Copilot coding agent + +Creating code, regardless of who's involved, typically requires a specific environment and some setup scripts to be run to ensure everything is in a good state. This holds true when assigning tasks to Copilot, which is performing tasks in a similar fashion to a SWE. + +Coding agent uses [GitHub Actions][github-actions] for its environment when doing its work. You can customize this environment by creating a [special setup workflow][setup-workflow], configured in the **.github/workflows/copilot-setup-steps.yml** file, to run before it gets to work. This enables it to have access to the required development tools and dependencies. This has been pre-configured ahead of the lab to help the lab flow and allow this learning opportunity. It makes sure that Copilot had access to .NET, Node.JS, and the required dependencies for the client and server: + +```yaml +name: "Copilot Setup Steps" + +# Allows you to test the setup steps from your repository's "Actions" tab +on: workflow_dispatch + +jobs: + copilot-setup-steps: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout code + uses: actions/checkout@v4 + + # Backend setup - .NET + - name: Set up .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: "8.0" + + - name: Restore .NET dependencies + run: dotnet restore server/TailspinToys.sln + + - name: Build .NET project + run: dotnet build server/TailspinToys.sln --no-restore + + # Frontend setup - Node.js + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: "22" + cache: "npm" + cache-dependency-path: "./client/package.json" + + - name: Install JavaScript dependencies + working-directory: ./client + run: npm ci +``` + +It looks like any other GitHub workflow file, but it has a few key points: + +- It contains a single job called **copilot-setup-steps**. This job is executed in GitHub Actions before Copilot starts working on the pull request. +- Notice the **workflow_dispatch** trigger, which allows you to run the workflow manually from the Actions tab of your repository. This is useful for testing that the workflow runs successfully instead of waiting for Copilot to run it. + +## Adding documentation + +While everyone understands the importance of documentation, most projects have either outdated information or lack it altogether. This is the type of tech debt which often goes unaddressed, slowing productivity and making it more difficult to maintain the codebase or bring new developers into the team. Fortunately, Copilot shines at creating documentation, and this is a perfect issue to assign to Copilot coding agent. It'll work in the background to generate the necessary documentation. In a future exercise you'll return to review its work. + +1. Navigate to your repository on github.com in a new browser tab. +2. Select the **Issues** tab. +3. Select **New issue** to open the new issue dialog. +4. Select **Blank issue** to create the new issue. +5. Set the **Title** to `Code lacks documentation`. +6. Set the **Description** to: + + ```plaintext + Our organization has a requirement that all functions have XML documentation comments (or the language equivalent). Unfortunately, recent updates haven't followed this standard. We need to update the existing code to ensure docstrings (or the equivalent) are included with every function or method. + ``` + +7. Select **Create** to create the issue. +8. On the right side, select **Assign to Copilot** to open the assignment dialog. + + ![Assigning Copilot to an issue](images/shared-assign-copilot.png) + +9. Select **Assign**. + + ![Copilot assignment details](images/ex4-assign-copilot-details.png) + +10. Select the **Pull Requests** tab. +11. Open the newly generated pull request (PR), which will be titled something similar to **[WIP]: Code lacks documentation**. If a new PR doesn't appear on the list, wait for a moment or two and refresh the browser window. +12. After a few minutes, you should see that Copilot has created a todo list. + +> [!NOTE] +> It make take several minutes for the todo list from Copilot to appear in the PR. Copilot is creating its environment (running the workflow highlighted previously), analyzing the project, and determining the best approach to tackling the problem. + +13. Review the list and the tasks it's going to complete. +14. Scroll down the pull request timeline, and you should see an update that Copilot has started working on the issue. +15. Select the **View session** button. + + ![Copilot session view](images/ex4-view-session.png) + +> [!IMPORTANT] +> You may need to refresh the window to see the updated indicator. + +16. Notice that you can scroll through the live session, and how Copilot is solving the problem. That includes exploring the code and understanding the state, how Copilot pauses to think and decide on the appropriate plan and also creating code. + +This will likely take several minutes. One of the primary goals of Copilot coding agent is to allow it to perform tasks asynchronously, freeing us to focus on other tasks. We're going to take advantage of that very feature by both assigning another task to Copilot coding agent, then turning our attention to writing some code to add features to our application. + +## Create new endpoints to modify games + +As has been highlighted, one of the great advantages of GitHub Copilot coding agent is the ability to divide work, where you can focus on one set of tasks while it focuses on another. While creating the endpoints for modifying games for the design team might not necessarily take a long time, it's still time which could be used for other tasks. Let's assign it to Copilot coding agent! + +1. Return to your repository on github.com. +2. Select the **Issues** tab. +3. Select **New issue** to open the new issue dialogue. +4. Select **Blank issue** to use the blank template. +5. Set the **Title** to: `Add endpoints to create and edit games` +6. Set the **Description** to: + + ```markdown + We're going to be creating functionality in the future to allow for the submission (and editing) of games. For now we just want the endpoints so we can explore how we want to create the UX and do some acceptance testing. Our requirements are: + + - Add new endpoints to the Games API to support creating, updating and deleting games + - There should be appropriate error handling for all new endpoints + - There should be unit tests created for all new endpoints + - Before creating the PR, ensure all tests pass + ``` + +7. Select **Create** to create the issue. +8. On the right side, select **Assign to Copilot** to open the assignment dialog. + + ![Assigning Copilot to an issue](images/shared-assign-copilot.png) + +9. Select **Assign**. + +Shortly after, you should see a set of 👀 on the first comment in the issue, indicating Copilot is on the job! + +![Copilot uses the eyes emoji to indicate it's working on the issue](images/ex4-issue-eyes-emoji.png) + +9. Select **Assign** to assign the issue to Copilot coding agent. + +Copilot is now diligently working on your second request! Copilot coding agent works in a similar fashion to a SWE, so you don't need to actively monitor it, but instead review once it's completed. Let's turn your attention to writing code and adding other features. + +## Summary and next steps + +With coding agent working diligently in the background, you can now turn your attention to your next lesson, [creating and using custom agents][next-lesson]. [Copilot coding agent can also use MCP servers][coding-agent-mcp], and has custom instructions available to it, which you explored in earlier modules. + +## Summary and next steps + +This lesson explored [GitHub Copilot coding agent][copilot-agents], your AI peer programmer. With coding agent you can assign issues to Copilot to perform asynchronously. You can use Copilot to address tech debt, create new features, or aid in migrating code from one framework to another. + +You explored these concepts: + +- customizing the environment for generating code. +- ensuring operations are performed securely. +- the importance of clearly scoped issues. +- assigning issues to Copilot. + +With coding agent working diligently in the background, we can now turn our attention to our next lesson, [creating and using custom agents][next-lesson]. [Copilot coding agent can also use MCP servers][coding-agent-mcp], and has custom instructions available to it, which we explored in earlier modules. + +## Resources + +- [About Copilot coding agent][copilot-agents] +- [Assigning GitHub issues to Copilot][assign-issue] +- [Copilot coding agent setup workflow best practices][coding-agent-best-practices] + +--- + +| [← Previous lesson: Copilot agent mode][previous-lesson] | [Next lesson: Custom agents →][next-lesson] | +|:--|--:| + +[coding-agent-overview]: https://docs.github.com/copilot/using-github-copilot/coding-agent/about-assigning-tasks-to-copilot#overview-of-copilot-coding-agent +[coding-agent-mcp]: https://docs.github.com/copilot/how-tos/agents/copilot-coding-agent/extending-copilot-coding-agent-with-mcp +[assign-issue]: https://docs.github.com/copilot/using-github-copilot/coding-agent/using-copilot-to-work-on-an-issue +[setup-workflow]: https://docs.github.com/copilot/using-github-copilot/coding-agent/best-practices-for-using-copilot-to-work-on-tasks#pre-installing-dependencies-in-github-copilots-environment +[copilot-agents]: https://docs.github.com/copilot/using-github-copilot/coding-agent/about-assigning-tasks-to-copilot +[coding-agent-best-practices]: https://docs.github.com/copilot/using-github-copilot/coding-agent/best-practices-for-using-copilot-to-work-on-tasks +[github-actions]: https://docs.github.com/actions +[next-lesson]: ./5-custom-agents.md +[previous-lesson]: ./3-copilot-agent-mode-vscode.md diff --git a/scenarios/tailspin-toys-dotnet/overrides/7-iterating-copilot-work.md b/scenarios/tailspin-toys-dotnet/overrides/7-iterating-copilot-work.md new file mode 100644 index 00000000..395d4ba0 --- /dev/null +++ b/scenarios/tailspin-toys-dotnet/overrides/7-iterating-copilot-work.md @@ -0,0 +1,173 @@ +# Exercise 7: Iterating on GitHub Copilot's work + +| [← Previous lesson: Mission control][previous-lesson] | +|:--| + +Throughout this lab you've assigned several issues to GitHub Copilot coding agent. You asked it to add documentation to your code, generate endpoints for the design team to iterate on, and implement accessibility features including high-contrast and light mode toggles. Let's explore the code changes it suggested and, if necessary, provide feedback to Copilot to improve its work. + +## Scenario + +As has been highlighted numerous times, the fundamentals of software design and DevOps do not change with the addition of generative AI. We always want to review the code generated, and work through our normal DevOps process. With that in mind, let's review the suggestions from GitHub Copilot for creating the documentation, new endpoints, and accessibility features before we turn on review for the rest of our team. + +## Security and GitHub Copilot coding agent + +Because Copilot coding agent performs its tasks asynchronously and without supervision, certain security constraints have been put in place to ensure everything remains safe. These include: + +- Copilot only has read access to your repository and write access **only** to the branch it will use for its code. +- Coding agent runs inside of GitHub Actions, where it will create a separate, ephemeral environment in which to work. +- Any GitHub Actions workflows require approval from a human before they can be run. +- [Access to external resources is limited by default][agent-firewall], including MCP servers. + +## Reviewing the generated documentation + +Let's start by exploring the first pull request (PR) generated by GitHub Copilot coding agent - adding documentation to your code. You'll perform this task by utilizing the standard PR interface in GitHub.com. + +> [!NOTE] +> When you explore the PR you may notice a warning about GitHub Copilot being blocked by a firewall. This **is expected**, as Copilot has limited access to external resources by default, including calls to external MCP servers. If you wish, you can [customize or disable the firewall for Copilot coding agent][agent-firewall]. + +1. Return to your repository on github.com. +2. Select **Pull Requests** to open the list of pull requests. +3. Open issue titled something similar to **Add missing documentation** or something more robust. + +> [!NOTE] +> If Copilot is still working on the task, the issue will contain the **[WIP]** flag. If so, wait for Copilot to complete the work. This may take a few minutes, so feel free to take a break, or reflect on everything you've learned so far. + +4. Once the pull request is ready, select the **Files changed** tab and review the changes. + + ![Files changed tab](images/shared-pr-files-changed.png) + +5. Explore the newly updated code, which includes the newly created docstrings and other documentation. The exact changes will vary. +6. Once you've reviewed the updates and everything looks good, navigate back to the **Conversation** tab and scroll down. +7. You should see an indicator that some workflows are waiting for approval. +8. Click on the **Approve and run workflows** button to allow the workflows to run. + + ![Approve and run workflows](images/shared-approve-workflows.png) + +9. You should see the workflows get queued in the checks section of the pull request. All being well, you should see that the workflows pass for both the backend and frontend. This may take a few minutes to complete. + +## Requesting changes from GitHub Copilot + +Working with Copilot on a pull request is not just a one-way street. You can also tag Copilot in comments - like you would other members of your team - in the pull request, or inline comments of the code. Copilot will see these comments, and trigger another session to address them. Due to the non-deterministic results, we can't give prescriptive text of what to ask for. Some ideas of what to ask Copilot to update include: + +- Add comment headers to the top of each code file with a brief description of what they do. +- Add docstrings to TypeScript and Svelte files. +- Create a README in both the server and client folders with descriptions of the codebase of each. + +1. Add a comment requesting a change to the generated documentation, tagging **@copilot** like you would any user. Use one of the ideas above, or another suggestion for Copilot around documentation you'd like to see in the codebase. +2. Select **View Session** to watch Copilot perform its work. Notice how Copilot starts a new session to make the updates. +3. You can select **Back to pull request** to return to the pull request. + + ![Back to pull request](images/ex7-back-to-pr.png) + +4. Once Copilot has completed the changes, you should see a new commit in the pull request. +5. Select the **Files changed** tab to review the changes. + +Feel free to continue iterating until you are happy. Once happy, you can convert the PR to ready from a draft, and merge it into the main branch. + +![Convert PR to ready](images/ex7-ready-for-review.png) + +## Review the new endpoints + +Let's return to the PR Copilot generated for resolving our issue about adding endpoints to the games API for creating, updating and deleting games. + +1. Return to your repository in GitHub.com. +2. Select the **Pull Requests** tab. +3. Select the PR which has a title similar to **Add CRUD endpoints for games API** or something more robust. +4. Select the **Files changed** tab to review the code it generated. +5. Once you've reviewed the updates and everything looks good, navigate back to the **Conversation** tab and scroll down. +6. You should see an indicator that some workflows are waiting for approval. +7. Click on the **Approve and run workflows** button to allow the workflows to run. + + ![Approve and run workflows](images/shared-approve-workflows.png) + +8. You should see the workflows get queued in the checks section of the pull request. All being well, you should see that the workflows pass for both the backend and frontend. This may take a few minutes to complete. +9. **Optional:** You could even switch to this branch in your Codespace to perform a manual test of the new endpoints. Navigate to your Codespace, open the terminal, and run the following commands (replace **** with the name of the branch Copilot created, e.g. **copilot/fix-8**.): + + ```bash + git fetch origin + git checkout + ``` + +Copilot has created the new endpoints! Just as before, you can work iteratively with Copilot coding agent to request updates. For example, you might want to request Copilot centralizes the error handling to reduce duplication, or ensuring comments and docstrings are added (remember - this was assigned **before** you made the updates to your custom instructions!) Just like before, you can make these requests by adding a new comment on the **Conversation** tab, which Copilot will see and kickoff a new session. + +## Review the accessibility features + +Finally, let's review the accessibility features that were implemented using the custom accessibility agent. This PR should include both the high-contrast mode you assigned in Exercise 5, and the light mode that was requested in mission control in Exercise 6. + +1. Return to your repository in GitHub.com. +2. Select the **Pull Requests** tab. +3. Select the PR which has a title similar to **Add high contrast mode to website** or something more robust. + +> [!NOTE] +> If Copilot is still working on the task, the issue will contain the **[WIP]** flag. If so, wait for Copilot to complete the work. This may take a few minutes. + +4. Select the **Files changed** tab to review the code it generated. +5. Review the implementation, paying particular attention to: + - The toggle UI components for switching between modes + - The use of local storage to persist user preferences + - The CSS or styling changes for high-contrast and light modes + - The accessibility attributes (ARIA labels, keyboard navigation, etc.) + - Any JavaScript/TypeScript code that manages the mode switching + +6. Once you've reviewed the updates and everything looks good, navigate back to the **Conversation** tab and scroll down. +7. You should see an indicator that some workflows are waiting for approval. +8. Click on the **Approve and run workflows** button to allow the workflows to run. + + ![Approve and run workflows](images/shared-approve-workflows.png) + +9. You should see the workflows get queued in the checks section of the pull request. All being well, you should see that the workflows pass for both the backend and frontend. This may take a few minutes to complete. +10. **Optional:** You could switch to this branch in your Codespace to manually test the accessibility features. Navigate to your Codespace, open the terminal, and run the following commands (replace **** with the name of the branch Copilot created): + + ```bash + git fetch origin + git checkout + ``` + + Then start the application and test the high-contrast and light mode toggles in your browser to ensure they work as expected and persist across page reloads. + +Notice how the custom accessibility agent helped guide Copilot to implement these features following accessibility best practices. If you see any accessibility concerns or improvements, you can tag **@copilot** in a comment to request updates, just like you did with the previous PRs. + +## Optional Exercise - Explore the agent's capabilities with more issues + +Having access to a peer programmer who is able to explore our codebase and make changes asynchronously is powerful, allowing us to reach a first iteration across tasks quickly, allowing us to review and guide, or take over and continue coding in the editor. + +You have made great progress through the lab, and you're approaching the end. However, you're encouraged to create some additional issues in your GitHub repository and use Copilot to solve those. Some ideas include: + +- Create a backer interest form on the game details page +- Implement pagination on the game listing endpoint +- Add input validation and error handling to the ASP.NET Core API +- Something else? What else might you consider? + +## Summary + +Congratulations! You completed the lab! You worked through several features available to you with GitHub Copilot, from the IDE to the repository. In particular you: + +- **Learned how to use GitHub Copilot and the Model Context Protocol (MCP) to streamline software development**. You set up the GitHub MCP server to enable Copilot to interact with your repository, created a detailed backlog using Copilot Agent Mode. +- **Explored how custom instructions and prompt files can guide Copilot to follow your project's coding standards.** You created a custom instructions file to provide context for Copilot, ensuring it generates code that adheres to your project's guidelines and used prompt files to provide guidance for repetitive tasks and established practices. +- **Used Copilot Agent Mode to implement new features, coordinate changes across backend and frontend code, and automate repetitive tasks.** You used GitHub Copilot to implement a new category and publisher filter for the game listing page, making changes across the client, backend, and the resulting tests. +- **Experienced Copilot as a peer programmer, being assigned issues and working collaboratively on pull requests.** You assigned Copilot to issues in your backlog, allowing it to create a pull request, build a plan, implement changes, and iterate further as you provided feedback. + +This is just the beginning, and we can't wait to see how you use Copilot to help you with your own projects. We hope you enjoyed the lab, and we look forward to seeing you in the next one! Happy coding! + +## Resources + +- [GitHub Copilot][github-copilot] +- [About Copilot agents][copilot-agents] +- [Assigning GitHub issues to Copilot][assign-issue] +- [Copilot coding agent setup workflow best practices][coding-agent-best-practices] +- [Configuring Copilot coding agent firewall][agent-firewall] + +--- + +| [← Previous lesson: Managing agents][previous-lesson] | +|:--| + +[github-copilot]: https://github.com/features/copilot +[coding-agent-overview]: https://docs.github.com/copilot/using-github-copilot/coding-agent/about-assigning-tasks-to-copilot#overview-of-copilot-coding-agent +[assign-issue]: https://docs.github.com/copilot/using-github-copilot/coding-agent/using-copilot-to-work-on-an-issue +[setup-workflow]: https://docs.github.com/copilot/using-github-copilot/coding-agent/best-practices-for-using-copilot-to-work-on-tasks#pre-installing-dependencies-in-github-copilots-environment +[copilot-agents]: https://docs.github.com/copilot/using-github-copilot/coding-agent/about-assigning-tasks-to-copilot +[coding-agent-best-practices]: https://docs.github.com/copilot/using-github-copilot/coding-agent/best-practices-for-using-copilot-to-work-on-tasks +[agent-firewall]: https://docs.github.com/copilot/customizing-copilot/customizing-or-disabling-the-firewall-for-copilot-coding-agent + +[previous-lesson]: ./6-managing-agents.md diff --git a/scenarios/tailspin-toys-dotnet/overrides/README.md b/scenarios/tailspin-toys-dotnet/overrides/README.md new file mode 100644 index 00000000..77128f44 --- /dev/null +++ b/scenarios/tailspin-toys-dotnet/overrides/README.md @@ -0,0 +1,24 @@ +# Tailspin Toys Dotnet — Exercise Overrides + +This directory contains exercise-specific content overrides for the **Tailspin Toys Dotnet** scenario. + +## How overrides work + +Place a Markdown file here with the **same filename** as an exercise in `workshop-content/` to replace +its contents for this scenario. Typically you only need to rewrite the `## Scenario` section of each +exercise to match your application — copy the original exercise file and replace every Tailspin-specific +reference with your application's equivalent. + +``` +overrides/ + 1-mcp.md ← replaces Scenario section for exercise 1 + 3-copilot-agent-mode-vscode.md ← replaces Scenario section for exercise 3 +``` + +Overrides must NOT be created for exercises listed in the `skip` field of `scenario.yml`. + +See [docs/authoring/new-scenario-guide.md](../../../docs/authoring/new-scenario-guide.md) for full instructions. + +## Files in this directory + +*(None yet — add override files as needed.)* diff --git a/scenarios/tailspin-toys-dotnet/scenario.yml b/scenarios/tailspin-toys-dotnet/scenario.yml new file mode 100644 index 00000000..7dcad02a --- /dev/null +++ b/scenarios/tailspin-toys-dotnet/scenario.yml @@ -0,0 +1,56 @@ +# tailspin-toys-dotnet — scenario configuration +# See docs/authoring/new-scenario-guide.md for full documentation. + +# Human-readable name displayed in the workshop +name: Tailspin Toys (.NET) + +# Short description of the application used in this scenario +description: > + A crowdfunding platform for board games with a developer theme. + The app uses an ASP.NET Core Web API, a SQLite database, and an + Astro/Svelte frontend with Tailwind CSS. + +# GitHub repository for the sample application (org/repo) +# The app is stored as a subfolder in this repository. +repository: github-samples/agents-in-sdlc + +# Technology stack — used when generating scenario-specific content +tech_stack: + frontend: + name: Astro + Svelte + language: TypeScript / JavaScript + notes: > + Astro handles routing and static rendering. + Svelte components (with runes-based reactivity) handle interactivity. + The frontend proxies all /api/* requests to the ASP.NET Core backend. + backend: + name: ASP.NET Core Web API + language: C# + notes: > + RESTful API using ASP.NET Core controllers and Entity Framework Core ORM. + C# nullable reference types are enabled on all files. + XML documentation comments are required on all public types and members. + database: SQLite (via Entity Framework Core) + +# Content variables — substituted into workshop content and prompts +variables: + company_name: Tailspin Toys + company_description: a crowdfunding platform for board games with a developer theme + product_type: crowdfunding platform + primary_entities: games, publishers, categories + filter_feature: filter games by category and publisher + new_endpoints_feature: endpoints to create, update, and delete games + accessibility_feature: high-contrast mode toggle + documentation_feature: XML documentation comments for all public types and methods + +# Exercises to skip for this scenario. +# List exercise filenames (without the .md extension) from workshop-content/ +# that are NOT applicable to this scenario. +skip: [] + +# Notes for workshop authors or maintainers (not shown to participants) +author_notes: > + This scenario uses an ASP.NET Core Web API backend instead of Flask/Python. + The frontend (Astro + Svelte) is identical to the canonical tailspin-toys scenario. + The app lives in the tailspin-toys-dotnet/ subfolder of this repository. + Exercises 2, 3, 4, and 7 require overrides due to .NET-specific references. diff --git a/tailspin-toys-dotnet/.devcontainer/devcontainer.json b/tailspin-toys-dotnet/.devcontainer/devcontainer.json new file mode 100644 index 00000000..47ba13f7 --- /dev/null +++ b/tailspin-toys-dotnet/.devcontainer/devcontainer.json @@ -0,0 +1,28 @@ +{ + "name": "Tailspin Toys .NET", + "image": "mcr.microsoft.com/devcontainers/base:bookworm", + "features": { + "ghcr.io/devcontainers/features/dotnet:2": { + "version": "8.0" + }, + "ghcr.io/devcontainers/features/node:1": {}, + "ghcr.io/warrenbuckley/codespace-features/sqlite:1": {} + }, + "postCreateCommand": "./scripts/setup-env.sh", + "forwardPorts": [ + 4321, + 5100 + ], + "customizations": { + "vscode": { + "extensions": [ + "github.copilot", + "github.copilot-chat", + "ms-dotnettools.csdevkit", + "ms-dotnettools.csharp", + "svelte.svelte-vscode", + "astro-build.astro-vscode" + ] + } + } +} diff --git a/tailspin-toys-dotnet/.github/agents/accessibility.md b/tailspin-toys-dotnet/.github/agents/accessibility.md new file mode 100644 index 00000000..3de98a2e --- /dev/null +++ b/tailspin-toys-dotnet/.github/agents/accessibility.md @@ -0,0 +1,184 @@ +--- +name: Accessibility agent +description: Designed to generate accessible websites +--- + +# Accessibility Specialist Agent + +You are an expert accessibility specialist focused on creating inclusive web experiences that comply with WCAG 2.1 Level AA standards. + +## Core Responsibilities + +- Ensure POUR principles: Perceivable, Operable, Understandable, Robust +- Identify and fix accessibility violations in HTML, CSS, and JavaScript +- Validate semantic HTML, ARIA attributes, keyboard navigation, and screen reader compatibility +- Verify color contrast ratios and ensure forms are accessible + +## WCAG 2.1 Level AA Requirements + +### Perceivable +- **Text Alternatives**: All images need `alt` attributes; decorative images use `alt=""` or `role="presentation"` +- **Color Contrast**: Normal text 4.5:1, large text 3:1; don't rely on color alone +- **Semantic Structure**: Use `