Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
124 changes: 54 additions & 70 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -1,70 +1,54 @@
# Copilot Instructions

These instructions guide GitHub Copilot when working in this repository.

## Project Context

This is a **beginner-friendly educational course** teaching GitHub Copilot CLI. The repo contains Markdown chapters (00–07), Python/C#/JavaScript sample apps, and supporting assets (images, demo GIFs, glossary). It is **not** a software product — it is technical courseware.

## Writing Conventions

- **Audience**: Beginners with no AI/ML experience. Explain every technical term on first use.
- **Tone**: Friendly, encouraging, practical. Avoid jargon without explanation.
- **Examples**: All code blocks and `copilot` commands must be copy-paste ready. Test them mentally before including.
- **Naming**: Use kebab-case for session names, file names, and identifiers (e.g., `book-app-review`, not `book app review`).
- **Command syntax**: Standardize flag format — use `--flag=value` consistently when a value is required, `--flag` when boolean.
- **Precision**: Don't over-specify tool behavior that may vary across shells or OS. Describe what the user will see, not implementation details.
- **Fallbacks**: When referencing tool version requirements (e.g., `gh` CLI version), always include upgrade instructions or a manual alternative.

## Content Conventions (from PR review patterns)

These patterns were mined from actual PR review feedback and represent recurring maintainer expectations:

- When showing multi-step workflows, ensure all prerequisite steps are included (e.g., `git add` before `git diff --staged`).
- When introducing a concept with an example, use consistent naming throughout the section — don't mix kebab-case and quoted names.
- When describing a command's behavior, match the level of specificity in the official release notes — don't state behavior that may differ across environments.
- If a feature requires a minimum tool version, mention the version AND provide a fallback path for users who can't upgrade yet.

## Sample Code Conventions

- **Primary sample**: Always use `samples/book-app-project/` (Python) for examples in chapters.
- **Test framework**: pytest — test files go in `samples/book-app-project/tests/` and follow `test_*.py` naming.
- **Python version**: 3.10+ (per `samples/book-app-project/pyproject.toml`).
- **Intentional bugs**: Files in `samples/book-app-buggy/` and `samples/buggy-code/` contain **deliberate bugs** for exercises. Never fix them.

## Chapter Structure

Every chapter (00–07) follows the same pattern in its `README.md`:

1. Real-World Analogy
2. Core Concepts
3. Hands-On Examples
4. Assignment
5. What's Next

Do not deviate from this structure when editing or adding chapter content.

## Markdown Formatting

- Use standard GitHub-Flavored Markdown.
- Images go in the repo-root `images/` directory.
- Use relative links for cross-chapter references (e.g., `../03-development-workflows/README.md`).
- Emoji usage is encouraged for section headers (matching existing style).

## Maintenance Matrix

| Change Made | Files to Update |
|---|---|
| New chapter added | `README.md` (course table), `AGENTS.md` (structure table), `images/learning-path.png` |
| Chapter content updated | The chapter's `README.md`, verify cross-references in adjacent chapters |
| New sample app variant added | `AGENTS.md` (structure table), `samples/` directory, relevant chapter references |
| Sample app code changed | `samples/book-app-project/tests/` (update/add tests), chapters referencing that code |
| Bug intentionally added to buggy samples | `samples/book-app-buggy/` or `samples/buggy-code/` only — do NOT update tests |
| New skill added | `.github/skills/{skill-name}/SKILL.md`, `samples/skills/` (example copy), Chapter 05 |
| New agent template added | `samples/agents/`, Chapter 04 |
| New MCP config added | `samples/mcp-configs/`, Chapter 06 |
| Glossary term introduced | `GLOSSARY.md` — add definition in alphabetical order |
| npm scripts changed | `package.json`, `AGENTS.md` (build section) |
| Devcontainer updated | `.devcontainer/devcontainer.json`, Chapter 00 (setup instructions) |
| Image or banner changed | `images/` directory, any README referencing the image |
| Copilot CLI version requirements change | Chapter 00, Chapter 01, `.devcontainer/devcontainer.json` |
# Copilot Instructions (project-specific)

Purpose
- Short guide for GitHub Copilot CLI sessions working on this repository (courseware, not a production product).

Build, test, and lint commands
- Python (primary sample: samples/book-app-project):
- Run all tests: python -m pytest samples/book-app-project -q
- Run a single test: python -m pytest samples/book-app-project/tests/test_books.py::test_name -q
- Install deps: follow pyproject.toml (e.g., `poetry install`), or `python -m pip install -r samples/book-app-project/requirements.txt` if present
- Lint (if available): python -m flake8 samples/book-app-project

- JavaScript (samples/book-app-project-js):
- Install & test: cd samples/book-app-project-js && npm install && npm test
- Run a single test (Jest): npm test -- -t "test name" (or use npx jest <path>)
- Lint: npm run lint (if script exists)

- C# (samples/book-app-project-cs):
- Run tests: dotnet test samples/book-app-project-cs
- Run a single test: dotnet test samples/book-app-project-cs --filter "FullyQualifiedName~TestName"

High-level architecture
- Course chapters live in top-level folders 00-07; each chapter is self-contained with a README following the same structure.
- Primary sample: samples/book-app-project (Python). JS and C# variants exist under samples/book-app-project-js and samples/book-app-project-cs.
- Agents and skills:
- .github/agents and .github/skills store agent & skill templates used by course demos
- samples/agents and samples/skills contain example implementations
- CI / automation: .github/workflows and .github/scripts contain helper scripts for building demo assets and translations.
- MCP examples: samples/mcp-configs/ and chapter 06 contain MCP server configs and docs.

Key conventions (repo-specific)
- Primary-sample-first: Use samples/book-app-project for Python-centric examples in chapters.
- Test file naming: pytest tests are under samples/book-app-project/tests/ and use test_*.py naming.
- Python version: 3.10+ (see samples/book-app-project/pyproject.toml).
- Intentional-bugs: Do NOT fix files in samples/book-app-buggy/ or samples/buggy-code/ — they are exercises.
- Chapter README format: Every chapter README must include: Real-World Analogy; Core Concepts; Hands-On Examples; Assignment; What's Next.
- Filenames & identifiers: prefer kebab-case for session names and examples used in text and commands.
- Command flag style: use `--flag=value` when a value is required and `--flag` for booleans in examples.
- Docs-first edits: When changing sample behavior, update corresponding chapter README and tests.

Files & places to check for AI sessions
- Primary sample code: samples/book-app-project/
- Tests: samples/book-app-project/tests/
- Agent templates: .github/agents/ and samples/agents/
- Skill templates: .github/skills/ and samples/skills/
- MCP server examples: samples/mcp-configs/ and 06-mcp-servers/

MCP servers
- This repo includes MCP examples (samples/mcp-configs/) and a chapter (06-mcp-servers). Would you like assistance configuring an MCP server for local testing? (Ask to configure: Playwright or other demo servers.)

Summary
- This file adds actionable build/test/lint commands, a concise architecture overview, and repo-specific conventions to help future Copilot sessions behave usefully and avoid common mistakes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
24 changes: 19 additions & 5 deletions samples/book-app-project/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,20 +27,34 @@ It can add, remove, and list books. Also mark them as read.

## Running the App

Available commands (run from samples/book-app-project/):

```bash
python book_app.py list
python book_app.py add
python book_app.py find
python book_app.py remove
python book_app.py list # Show all books
python book_app.py add # Interactive add
python book_app.py find # Find books by author (exact match)
python book_app.py remove # Remove a book by title
python book_app.py search # Interactive search prompt
python book_app.py search "dune" title # Search titles containing "dune"
python book_app.py search "gibson" author # Search authors containing "gibson"
python book_app.py help
```

## Running Tests

Run the full test suite for the sample:

```bash
python -m pytest tests/
python -m pytest samples/book-app-project -q
```

Run a single test by node (example):

```bash
python -m pytest samples/book-app-project/tests/test_books.py::test_search_by_title_exact -q
```


---

## Notes
Expand Down
59 changes: 41 additions & 18 deletions samples/book-app-project/book_app.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import sys
from books import BookCollection
from typing import List, Any


# Global collection instance
collection = BookCollection()


def show_books(books):
def show_books(books: List[Any]) -> None:
"""Display books in a user-friendly format."""
if not books:
print("No books found.")
Expand All @@ -21,12 +22,12 @@ def show_books(books):
print()


def handle_list():
def handle_list() -> None:
books = collection.list_books()
show_books(books)


def handle_add():
def handle_add() -> None:
print("\nAdd a New Book\n")

title = input("Title: ").strip()
Expand All @@ -41,7 +42,7 @@ def handle_add():
print(f"\nError: {e}\n")


def handle_remove():
def handle_remove() -> None:
print("\nRemove a Book\n")

title = input("Enter the title of the book to remove: ").strip()
Expand All @@ -50,7 +51,7 @@ def handle_remove():
print("\nBook removed if it existed.\n")


def handle_find():
def handle_find() -> None:
print("\nFind Books by Author\n")

author = input("Author name: ").strip()
Expand All @@ -59,36 +60,58 @@ def handle_find():
show_books(books)


def show_help():
def handle_search() -> None:
"""Search books by title or author.

Usage: book_app.py search <query> [title|author]
If query is omitted, user is prompted.
"""
# prefer CLI args when provided
if len(sys.argv) >= 3:
query = sys.argv[2]
else:
query = input("Query: ").strip()

field = 'title'
if len(sys.argv) >= 4:
field = sys.argv[3].lower()
books = collection.search_books(query, field)
show_books(books)


def show_help() -> None:
print("""
Book Collection Helper

Commands:
list - Show all books
add - Add a new book
remove - Remove a book by title
find - Find books by author
find - Find books by author (exact match)
search - Search books by title or author (partial, case-insensitive). Usage: search <query> [title|author]
help - Show this help message
""")


def main():
def main() -> None:
if len(sys.argv) < 2:
show_help()
return

command = sys.argv[1].lower()

if command == "list":
handle_list()
elif command == "add":
handle_add()
elif command == "remove":
handle_remove()
elif command == "find":
handle_find()
elif command == "help":
show_help()
COMMANDS = {
"list": handle_list,
"add": handle_add,
"remove": handle_remove,
"find": handle_find,
"search": handle_search,
"help": show_help,
}

handler = COMMANDS.get(command)
if handler:
handler()
else:
print("Unknown command.\n")
show_help()
Expand Down
17 changes: 16 additions & 1 deletion samples/book-app-project/books.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,5 +68,20 @@ def remove_book(self, title: str) -> bool:
return False

def find_by_author(self, author: str) -> List[Book]:
"""Find all books by a given author."""
"""Find all books by a given author (exact match, case-insensitive)."""
return [b for b in self.books if b.author.lower() == author.lower()]

def search_books(self, query: str, field: str = 'title') -> List[Book]:
"""Search books by title or author.

Args:
query: substring to search for (case-insensitive).
field: 'title' or 'author'. Defaults to 'title'.
Returns:
List[Book]: matching books (partial, case-insensitive).
"""
q = query.lower()
if field == 'author':
return [b for b in self.books if q in b.author.lower()]
# default to title
return [b for b in self.books if q in b.title.lower()]
30 changes: 30 additions & 0 deletions samples/book-app-project/tests/test_books.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,3 +51,33 @@ def test_remove_book_invalid():
collection = BookCollection()
result = collection.remove_book("Nonexistent Book")
assert result is False


def test_search_by_title_exact():
collection = BookCollection()
collection.add_book("Dune", "Frank Herbert", 1965)
collection.add_book("Dune Messiah", "Frank Herbert", 1969)
results = collection.search_books("Dune", field='title')
assert any(b.title == "Dune" for b in results)


def test_search_by_title_partial_case_insensitive():
collection = BookCollection()
collection.add_book("The Hobbit", "J.R.R. Tolkien", 1937)
results = collection.search_books("hob", field='title')
assert any("hob" in b.title.lower() for b in results)


def test_search_by_author_partial_case_insensitive():
collection = BookCollection()
collection.add_book("Neuromancer", "William Gibson", 1984)
collection.add_book("Count Zero", "William Gibson", 1986)
results = collection.search_books("gibson", field='author')
assert len(results) == 2


def test_search_no_results():
collection = BookCollection()
collection.add_book("Frankenstein", "Mary Shelley", 1818)
results = collection.search_books("notfound", field='title')
assert results == []
10 changes: 10 additions & 0 deletions samples/mcp-configs/playwright-mcp.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"mcpServers": {
"playwright-demo": {
"type": "local",
"command": "npx",
"args": ["-y", "http-server", "samples/src", "-p", "3000"],
"tools": ["browser"]
}
}
}
Loading