From 9c985c5dce5ffba0945b6d9236095037de9e2b24 Mon Sep 17 00:00:00 2001 From: Caleb Wilson Date: Tue, 9 Jun 2026 12:41:54 +0100 Subject: [PATCH 1/5] feat: add Lesson 24 (Remote Control) for CLI remote access Add new Lesson 24 covering the remote control feature introduced in v1.0.25-1.0.32. This teaches users how to monitor and control CLI sessions from GitHub.com or GitHub Mobile. Changes: - Added tutorial/lessons/24-remote-control.md as optional capstone lesson - Positioned after Lesson 23 (Graduation) before specialist tracks - Covers /remote enable, --remote flag, and --connect flag - Exercise: finish tutorial from mobile/browser (optional) - Shifted QA track from lessons 24-28 to 25-29 - Shifted Design track from lessons 29-34 to 30-35 Decision: - Made this a standalone lesson (not a section in Session Management) as it represents a distinct workflow and deserves full coverage - Positioned as last core lesson to let users optionally finish on mobile - Kept content focused on practical use cases (long-running tasks, mobile check-ins, cross-device workflows) - No slide deck updates needed (tutorial-only feature) Closes #66 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- tutorial/lessons/24-remote-control.md | 88 +++++++++++++++++++ ...4-test-planning.md => 25-test-planning.md} | 2 +- ...-case-design.md => 26-test-case-design.md} | 2 +- ...ion.md => 27-automated-test-generation.md} | 2 +- ...wright.md => 28-e2e-testing-playwright.md} | 2 +- ...raduation.md => 29-qa-track-graduation.md} | 2 +- ...g-mockups.md => 30-translating-mockups.md} | 2 +- ...y-review.md => 31-accessibility-review.md} | 2 +- ...ns.md => 32-design-system-instructions.md} | 2 +- ...ff.md => 33-designer-developer-handoff.md} | 2 +- ...prototyping.md => 34-rapid-prototyping.md} | 2 +- ...ation.md => 35-design-track-graduation.md} | 2 +- 12 files changed, 99 insertions(+), 11 deletions(-) create mode 100644 tutorial/lessons/24-remote-control.md rename tutorial/lessons/{24-test-planning.md => 25-test-planning.md} (98%) rename tutorial/lessons/{25-test-case-design.md => 26-test-case-design.md} (98%) rename tutorial/lessons/{26-automated-test-generation.md => 27-automated-test-generation.md} (98%) rename tutorial/lessons/{27-e2e-testing-playwright.md => 28-e2e-testing-playwright.md} (98%) rename tutorial/lessons/{28-qa-track-graduation.md => 29-qa-track-graduation.md} (98%) rename tutorial/lessons/{29-translating-mockups.md => 30-translating-mockups.md} (98%) rename tutorial/lessons/{30-accessibility-review.md => 31-accessibility-review.md} (98%) rename tutorial/lessons/{31-design-system-instructions.md => 32-design-system-instructions.md} (98%) rename tutorial/lessons/{32-designer-developer-handoff.md => 33-designer-developer-handoff.md} (98%) rename tutorial/lessons/{33-rapid-prototyping.md => 34-rapid-prototyping.md} (99%) rename tutorial/lessons/{34-design-track-graduation.md => 35-design-track-graduation.md} (99%) diff --git a/tutorial/lessons/24-remote-control.md b/tutorial/lessons/24-remote-control.md new file mode 100644 index 0000000..bbf4df8 --- /dev/null +++ b/tutorial/lessons/24-remote-control.md @@ -0,0 +1,88 @@ +# Lesson 24: Remote Control + +## What You'll Learn + +How to monitor and control a running Copilot CLI session from GitHub.com or GitHub Mobile. Perfect for long-running tasks, checking progress away from your desk, or finishing the tutorial on your phone. + +## Concepts + +--- + +### What is remote control? + +Remote control lets you access a running CLI session from anywhere — GitHub.com in your browser, or the GitHub Mobile app on your phone. You can: + +- Monitor the session in real-time as Copilot works +- Respond to permission requests remotely +- Provide input or answer questions +- Review changes and approve tool calls + +This is valuable when you kick off a complex task and need to step away. Instead of waiting at your desk, you can check in from anywhere and keep the work moving. + +--- + +### Starting a remote session + +**From an interactive session**, enable remote control with: + +``` +/remote enable +``` + +This generates a link to view the session on GitHub.com. The link appears in your terminal — click it or copy it to your browser or GitHub Mobile app. + +**When starting a new session**, use the `--remote` flag: + +``` +copilot --remote +``` + +The CLI displays the remote access link immediately. Open it on any device. + +### Viewing from GitHub.com or Mobile + +Once you open the remote link: + +- **On GitHub.com**: You see the conversation in real-time. Copilot's responses appear as they're generated. Permission requests show up as interactive prompts you can approve or deny. + +- **On GitHub Mobile**: The mobile app provides the same interface, optimized for touch. You can read, respond, and approve from your phone. + +The remote view is **live** — it updates as the session progresses. If Copilot asks a question or requests permission, you'll see it immediately. + +--- + +### Use cases + +Remote control shines in these scenarios: + +- **Long-running tasks**: Start a large refactoring or test generation task, enable remote control, and step away. Check in periodically from your phone. +- **Collaborative debugging**: Share the remote link with a teammate so they can follow along or provide input. +- **Mobile check-ins**: Quickly approve a permission request or answer a question from GitHub Mobile while you're away from your desk. +- **Cross-device workflows**: Start a session on your workstation, continue it on your laptop, finish it on your phone. + +--- + +## Exercise + +**Finish this tutorial from GitHub.com or GitHub Mobile.** + +This is an optional capstone experience — you can complete this lesson from your terminal as usual, or try the remote control feature and finish the tutorial from a different device. + +**If you want to try remote control:** + +1. Enable remote control for this session: + ``` + /remote enable + ``` + +2. Open github another device: + - **GitHub.com in a browser** + - **GitHub Mobile app** on your phone + +3. Navigate to the agents tab and find your remote session. + +4. Prompt to finish the tutorial from there + +**If you prefer to stay in the terminal:** + +That's completely fine — remote control is an optional advanced feature. Just say **"next"** when you're ready to continue. \ No newline at end of file diff --git a/tutorial/lessons/24-test-planning.md b/tutorial/lessons/25-test-planning.md similarity index 98% rename from tutorial/lessons/24-test-planning.md rename to tutorial/lessons/25-test-planning.md index be30e87..19a7be6 100644 --- a/tutorial/lessons/24-test-planning.md +++ b/tutorial/lessons/25-test-planning.md @@ -1,4 +1,4 @@ -# Lesson 24: Test Planning from Requirements +# Lesson 25: Test Planning from Requirements ## What You'll Learn diff --git a/tutorial/lessons/25-test-case-design.md b/tutorial/lessons/26-test-case-design.md similarity index 98% rename from tutorial/lessons/25-test-case-design.md rename to tutorial/lessons/26-test-case-design.md index d113197..5222cbb 100644 --- a/tutorial/lessons/25-test-case-design.md +++ b/tutorial/lessons/26-test-case-design.md @@ -1,4 +1,4 @@ -# Lesson 25: Test Case Design & Coverage +# Lesson 26: Test Case Design & Coverage ## What You'll Learn diff --git a/tutorial/lessons/26-automated-test-generation.md b/tutorial/lessons/27-automated-test-generation.md similarity index 98% rename from tutorial/lessons/26-automated-test-generation.md rename to tutorial/lessons/27-automated-test-generation.md index 77a417d..df1d8ce 100644 --- a/tutorial/lessons/26-automated-test-generation.md +++ b/tutorial/lessons/27-automated-test-generation.md @@ -1,4 +1,4 @@ -# Lesson 26: Automated Test Generation +# Lesson 27: Automated Test Generation ## What You'll Learn diff --git a/tutorial/lessons/27-e2e-testing-playwright.md b/tutorial/lessons/28-e2e-testing-playwright.md similarity index 98% rename from tutorial/lessons/27-e2e-testing-playwright.md rename to tutorial/lessons/28-e2e-testing-playwright.md index bfb296f..ccb3990 100644 --- a/tutorial/lessons/27-e2e-testing-playwright.md +++ b/tutorial/lessons/28-e2e-testing-playwright.md @@ -1,4 +1,4 @@ -# Lesson 27: E2E Testing with Playwright +# Lesson 28: E2E Testing with Playwright ## What You'll Learn diff --git a/tutorial/lessons/28-qa-track-graduation.md b/tutorial/lessons/29-qa-track-graduation.md similarity index 98% rename from tutorial/lessons/28-qa-track-graduation.md rename to tutorial/lessons/29-qa-track-graduation.md index 04502ef..ab1699e 100644 --- a/tutorial/lessons/28-qa-track-graduation.md +++ b/tutorial/lessons/29-qa-track-graduation.md @@ -1,4 +1,4 @@ -# Lesson 28: QA Track Graduation +# Lesson 29: QA Track Graduation ## You Did It diff --git a/tutorial/lessons/29-translating-mockups.md b/tutorial/lessons/30-translating-mockups.md similarity index 98% rename from tutorial/lessons/29-translating-mockups.md rename to tutorial/lessons/30-translating-mockups.md index ad5f962..1a48fd6 100644 --- a/tutorial/lessons/29-translating-mockups.md +++ b/tutorial/lessons/30-translating-mockups.md @@ -1,4 +1,4 @@ -# Lesson 29: Translating Mockups to Specs +# Lesson 30: Translating Mockups to Specs ## What You'll Learn diff --git a/tutorial/lessons/30-accessibility-review.md b/tutorial/lessons/31-accessibility-review.md similarity index 98% rename from tutorial/lessons/30-accessibility-review.md rename to tutorial/lessons/31-accessibility-review.md index 695c102..b6b710e 100644 --- a/tutorial/lessons/30-accessibility-review.md +++ b/tutorial/lessons/31-accessibility-review.md @@ -1,4 +1,4 @@ -# Lesson 30: Accessibility & Design Review +# Lesson 31: Accessibility & Design Review ## What You'll Learn diff --git a/tutorial/lessons/31-design-system-instructions.md b/tutorial/lessons/32-design-system-instructions.md similarity index 98% rename from tutorial/lessons/31-design-system-instructions.md rename to tutorial/lessons/32-design-system-instructions.md index 42bd131..4c8d41c 100644 --- a/tutorial/lessons/31-design-system-instructions.md +++ b/tutorial/lessons/32-design-system-instructions.md @@ -1,4 +1,4 @@ -# Lesson 31: Design System Instructions +# Lesson 32: Design System Instructions ## What You'll Learn diff --git a/tutorial/lessons/32-designer-developer-handoff.md b/tutorial/lessons/33-designer-developer-handoff.md similarity index 98% rename from tutorial/lessons/32-designer-developer-handoff.md rename to tutorial/lessons/33-designer-developer-handoff.md index 3a9db31..a920edc 100644 --- a/tutorial/lessons/32-designer-developer-handoff.md +++ b/tutorial/lessons/33-designer-developer-handoff.md @@ -1,4 +1,4 @@ -# Lesson 32: Designer ↔ Developer Handoff +# Lesson 33: Designer ↔ Developer Handoff ## What You'll Learn diff --git a/tutorial/lessons/33-rapid-prototyping.md b/tutorial/lessons/34-rapid-prototyping.md similarity index 99% rename from tutorial/lessons/33-rapid-prototyping.md rename to tutorial/lessons/34-rapid-prototyping.md index 541e063..b028ba1 100644 --- a/tutorial/lessons/33-rapid-prototyping.md +++ b/tutorial/lessons/34-rapid-prototyping.md @@ -1,4 +1,4 @@ -# Lesson 33: Rapid Prototyping +# Lesson 34: Rapid Prototyping ## What You'll Learn diff --git a/tutorial/lessons/34-design-track-graduation.md b/tutorial/lessons/35-design-track-graduation.md similarity index 99% rename from tutorial/lessons/34-design-track-graduation.md rename to tutorial/lessons/35-design-track-graduation.md index 0f824a6..469ca7a 100644 --- a/tutorial/lessons/34-design-track-graduation.md +++ b/tutorial/lessons/35-design-track-graduation.md @@ -1,4 +1,4 @@ -# Lesson 34: Design Track Graduation +# Lesson 35: Design Track Graduation ## You Did It From c5a506d9a0c3bf359d6649758ecdbabdf4ea51b9 Mon Sep 17 00:00:00 2001 From: Caleb Wilson Date: Tue, 9 Jun 2026 12:41:54 +0100 Subject: [PATCH 2/5] feat: add Lesson 23 (Remote Control) before graduation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add new Lesson 23 covering the remote control feature introduced in v1.0.25-1.0.32. Positioned before Lesson 24 (Graduation) as an optional advanced feature users can try before completing the core track. Changes: - Added tutorial/lessons/23-remote-control.md (remote control lesson) - Renamed 23-graduation.md → 24-graduation.md - Covers /remote enable, --remote flag, viewing from GitHub.com/Mobile - Exercise: optionally finish tutorial from mobile/browser - Updated core track: now 0-24 (was 0-23) - Updated QA track: now 25-29 (was 24-28) - Updated Design track: now 30-35 (was 29-34) - Updated all agent files with correct track ranges - Updated README.md and copilot-instructions.md with new ranges Track configuration updates: - .github/agents/next.agent.md: core cap 24, qa cap 29, design cap 35 - .github/agents/previous.agent.md: qa min 25, design min 30 - .github/agents/skip.agent.md: core cap 24, qa cap 29, design cap 35 - .github/agents/progress.agent.md: core shows 25 lessons total - .github/agents/qa.agent.md: starts at lesson 25 - .github/agents/design.agent.md: starts at lesson 30 Decision: - Remote control comes BEFORE graduation (not after) as suggested - Makes graduation the true capstone (lesson 24) - Remote control is optional but available before final lesson - Kept content focused on practical use cases - No slide deck updates needed (tutorial-only feature) Closes #66 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .claude/skills/playwright-cli/SKILL.md | 388 ++++++++++++++++++ .../references/element-attributes.md | 23 ++ .../references/playwright-tests.md | 39 ++ .../references/request-mocking.md | 87 ++++ .../playwright-cli/references/running-code.md | 241 +++++++++++ .../references/session-management.md | 225 ++++++++++ .../references/spec-driven-testing.md | 305 ++++++++++++++ .../references/storage-state.md | 275 +++++++++++++ .../references/test-generation.md | 134 ++++++ .../playwright-cli/references/tracing.md | 139 +++++++ .../references/video-recording.md | 143 +++++++ .github/agents/design.agent.md | 6 +- .github/agents/next.agent.md | 2 +- .github/agents/previous.agent.md | 2 +- .github/agents/progress.agent.md | 2 +- .github/agents/qa.agent.md | 6 +- .github/agents/skip.agent.md | 2 +- .github/copilot-instructions.md | 36 +- .../page-2026-06-08T12-12-09-434Z.yml | 192 +++++++++ .../page-2026-06-08T12-13-09-706Z.yml | 230 +++++++++++ README.md | 8 +- exercises/sample-app/src/exporters/csv.ts | 23 ++ exercises/sample-app/src/exporters/json.ts | 5 + .../sample-app/src/exporters/markdown.ts | 7 + exercises/sample-app/tests/csv.test.ts | 58 +++ exercises/sample-app/tests/json.test.ts | 56 +++ exercises/sample-app/tests/markdown.test.ts | 45 ++ tutorial/lessons/23-remote-control.md | 88 ++++ .../{23-graduation.md => 24-graduation.md} | 2 +- ...4-test-planning.md => 25-test-planning.md} | 2 +- ...-case-design.md => 26-test-case-design.md} | 2 +- ...ion.md => 27-automated-test-generation.md} | 2 +- ...wright.md => 28-e2e-testing-playwright.md} | 2 +- ...raduation.md => 29-qa-track-graduation.md} | 2 +- ...g-mockups.md => 30-translating-mockups.md} | 2 +- ...y-review.md => 31-accessibility-review.md} | 2 +- ...ns.md => 32-design-system-instructions.md} | 2 +- ...ff.md => 33-designer-developer-handoff.md} | 2 +- ...prototyping.md => 34-rapid-prototyping.md} | 2 +- ...ation.md => 35-design-track-graduation.md} | 2 +- tutorial/state.json | 4 +- 41 files changed, 2749 insertions(+), 46 deletions(-) create mode 100644 .claude/skills/playwright-cli/SKILL.md create mode 100644 .claude/skills/playwright-cli/references/element-attributes.md create mode 100644 .claude/skills/playwright-cli/references/playwright-tests.md create mode 100644 .claude/skills/playwright-cli/references/request-mocking.md create mode 100644 .claude/skills/playwright-cli/references/running-code.md create mode 100644 .claude/skills/playwright-cli/references/session-management.md create mode 100644 .claude/skills/playwright-cli/references/spec-driven-testing.md create mode 100644 .claude/skills/playwright-cli/references/storage-state.md create mode 100644 .claude/skills/playwright-cli/references/test-generation.md create mode 100644 .claude/skills/playwright-cli/references/tracing.md create mode 100644 .claude/skills/playwright-cli/references/video-recording.md create mode 100644 .playwright-cli/page-2026-06-08T12-12-09-434Z.yml create mode 100644 .playwright-cli/page-2026-06-08T12-13-09-706Z.yml create mode 100644 exercises/sample-app/src/exporters/csv.ts create mode 100644 exercises/sample-app/src/exporters/json.ts create mode 100644 exercises/sample-app/src/exporters/markdown.ts create mode 100644 exercises/sample-app/tests/csv.test.ts create mode 100644 exercises/sample-app/tests/json.test.ts create mode 100644 exercises/sample-app/tests/markdown.test.ts create mode 100644 tutorial/lessons/23-remote-control.md rename tutorial/lessons/{23-graduation.md => 24-graduation.md} (99%) rename tutorial/lessons/{24-test-planning.md => 25-test-planning.md} (98%) rename tutorial/lessons/{25-test-case-design.md => 26-test-case-design.md} (98%) rename tutorial/lessons/{26-automated-test-generation.md => 27-automated-test-generation.md} (98%) rename tutorial/lessons/{27-e2e-testing-playwright.md => 28-e2e-testing-playwright.md} (98%) rename tutorial/lessons/{28-qa-track-graduation.md => 29-qa-track-graduation.md} (98%) rename tutorial/lessons/{29-translating-mockups.md => 30-translating-mockups.md} (98%) rename tutorial/lessons/{30-accessibility-review.md => 31-accessibility-review.md} (98%) rename tutorial/lessons/{31-design-system-instructions.md => 32-design-system-instructions.md} (98%) rename tutorial/lessons/{32-designer-developer-handoff.md => 33-designer-developer-handoff.md} (98%) rename tutorial/lessons/{33-rapid-prototyping.md => 34-rapid-prototyping.md} (99%) rename tutorial/lessons/{34-design-track-graduation.md => 35-design-track-graduation.md} (99%) diff --git a/.claude/skills/playwright-cli/SKILL.md b/.claude/skills/playwright-cli/SKILL.md new file mode 100644 index 0000000..f034c32 --- /dev/null +++ b/.claude/skills/playwright-cli/SKILL.md @@ -0,0 +1,388 @@ +--- +name: playwright-cli +description: Automate browser interactions, test web pages and work with Playwright tests. +allowed-tools: Bash(playwright-cli:*) Bash(npx:*) Bash(npm:*) +--- + +# Browser Automation with playwright-cli + +## Quick start + +```bash +# open new browser +playwright-cli open +# navigate to a page +playwright-cli goto https://playwright.dev +# interact with the page using refs from the snapshot +playwright-cli click e15 +playwright-cli type "page.click" +playwright-cli press Enter +# take a screenshot (rarely used, as snapshot is more common) +playwright-cli screenshot +# close the browser +playwright-cli close +``` + +## Commands + +### Core + +```bash +playwright-cli open +# open and navigate right away +playwright-cli open https://example.com/ +playwright-cli goto https://playwright.dev +playwright-cli type "search query" +playwright-cli click e3 +playwright-cli dblclick e7 +# --submit presses Enter after filling the element +playwright-cli fill e5 "user@example.com" --submit +playwright-cli drag e2 e8 +# drop files or data onto an element (from outside the page) +playwright-cli drop e4 --path=./image.png +playwright-cli drop e4 --data="text/plain=hello world" +playwright-cli hover e4 +playwright-cli select e9 "option-value" +playwright-cli upload ./document.pdf +playwright-cli check e12 +playwright-cli uncheck e12 +playwright-cli snapshot +playwright-cli eval "document.title" +playwright-cli eval "el => el.textContent" e5 +# get element id, class, or any attribute not visible in the snapshot +playwright-cli eval "el => el.id" e5 +playwright-cli eval "el => el.getAttribute('data-testid')" e5 +playwright-cli dialog-accept +playwright-cli dialog-accept "confirmation text" +playwright-cli dialog-dismiss +playwright-cli resize 1920 1080 +playwright-cli close +``` + +### Navigation + +```bash +playwright-cli go-back +playwright-cli go-forward +playwright-cli reload +``` + +### Keyboard + +```bash +playwright-cli press Enter +playwright-cli press ArrowDown +playwright-cli keydown Shift +playwright-cli keyup Shift +``` + +### Mouse + +```bash +playwright-cli mousemove 150 300 +playwright-cli mousedown +playwright-cli mousedown right +playwright-cli mouseup +playwright-cli mouseup right +playwright-cli mousewheel 0 100 +``` + +### Save as + +```bash +playwright-cli screenshot +playwright-cli screenshot e5 +playwright-cli screenshot --filename=page.png +playwright-cli pdf --filename=page.pdf +``` + +### Tabs + +```bash +playwright-cli tab-list +playwright-cli tab-new +playwright-cli tab-new https://example.com/page +playwright-cli tab-close +playwright-cli tab-close 2 +playwright-cli tab-select 0 +``` + +### Storage + +```bash +playwright-cli state-save +playwright-cli state-save auth.json +playwright-cli state-load auth.json + +# Cookies +playwright-cli cookie-list +playwright-cli cookie-list --domain=example.com +playwright-cli cookie-get session_id +playwright-cli cookie-set session_id abc123 +playwright-cli cookie-set session_id abc123 --domain=example.com --httpOnly --secure +playwright-cli cookie-delete session_id +playwright-cli cookie-clear + +# LocalStorage +playwright-cli localstorage-list +playwright-cli localstorage-get theme +playwright-cli localstorage-set theme dark +playwright-cli localstorage-delete theme +playwright-cli localstorage-clear + +# SessionStorage +playwright-cli sessionstorage-list +playwright-cli sessionstorage-get step +playwright-cli sessionstorage-set step 3 +playwright-cli sessionstorage-delete step +playwright-cli sessionstorage-clear +``` + +### Network + +```bash +playwright-cli route "**/*.jpg" --status=404 +playwright-cli route "https://api.example.com/**" --body='{"mock": true}' +playwright-cli route-list +playwright-cli unroute "**/*.jpg" +playwright-cli unroute +``` + +### DevTools + +```bash +playwright-cli console +playwright-cli console warning +playwright-cli requests +playwright-cli request 5 +playwright-cli run-code "async page => await page.context().grantPermissions(['geolocation'])" +playwright-cli run-code --filename=script.js +playwright-cli tracing-start +playwright-cli tracing-stop +playwright-cli video-start video.webm +playwright-cli video-chapter "Chapter Title" --description="Details" --duration=2000 +playwright-cli video-stop + +# launch the dashboard for UI review / design feedback — user annotates the page, you receive the annotated screenshot, snapshot, and notes +playwright-cli show --annotate + +# generate a Playwright locator for an element from its ref or selector +playwright-cli generate-locator e5 --raw + +# show a persistent highlight overlay for an element, optionally with a custom style +playwright-cli highlight e5 +playwright-cli highlight e5 --style="outline: 3px dashed red" +# hide a single element highlight, or all page highlights when no target is given +playwright-cli highlight e5 --hide +playwright-cli highlight --hide +``` + +## Raw output + +The global `--raw` option strips page status, generated code, and snapshot sections from the output, returning only the result value. Use it to pipe command output into other tools. Commands that don't produce output return nothing. + +```bash +playwright-cli --raw eval "JSON.stringify(performance.timing)" | jq '.loadEventEnd - .navigationStart' +playwright-cli --raw eval "JSON.stringify([...document.querySelectorAll('a')].map(a => a.href))" > links.json +playwright-cli --raw snapshot > before.yml +playwright-cli click e5 +playwright-cli --raw snapshot > after.yml +diff before.yml after.yml +TOKEN=$(playwright-cli --raw cookie-get session_id) +playwright-cli --raw localstorage-get theme +``` + +For structured output wrapping every reply as JSON, pass --json +```bash +playwright-cli list --json +``` + +## Open parameters +```bash +# Use specific browser when creating session +playwright-cli open --browser=chrome +playwright-cli open --browser=firefox +playwright-cli open --browser=webkit +playwright-cli open --browser=msedge + +# Use persistent profile (by default profile is in-memory) +playwright-cli open --persistent +# Use persistent profile with custom directory +playwright-cli open --profile=/path/to/profile + +# Connect to browser via Playwright Extension +playwright-cli attach --extension=chrome + +# Connect to a running Chrome or Edge by channel name +playwright-cli attach --cdp=chrome +playwright-cli attach --cdp=msedge + +# Connect to a running browser via CDP endpoint +playwright-cli attach --cdp=http://localhost:9222 + +# Start with config file +playwright-cli open --config=my-config.json + +# Close the browser +playwright-cli close +# Detach from an attached browser (leaves the external browser running) +playwright-cli -s=msedge detach +# Delete user data for the default session +playwright-cli delete-data +``` + +## Snapshots + +After each command, playwright-cli provides a snapshot of the current browser state. + +```bash +> playwright-cli goto https://example.com +### Page +- Page URL: https://example.com/ +- Page Title: Example Domain +### Snapshot +[Snapshot](.playwright-cli/page-2026-02-14T19-22-42-679Z.yml) +``` + +You can also take a snapshot on demand using `playwright-cli snapshot` command. All the options below can be combined as needed. + +```bash +# default - save to a file with timestamp-based name +playwright-cli snapshot + +# save to file, use when snapshot is a part of the workflow result +playwright-cli snapshot --filename=after-click.yaml + +# snapshot an element instead of the whole page +playwright-cli snapshot "#main" + +# limit snapshot depth for efficiency, take a partial snapshot afterwards +playwright-cli snapshot --depth=4 +playwright-cli snapshot e34 + +# include each element's bounding box as [box=x,y,width,height] +playwright-cli snapshot --boxes +``` + +## Targeting elements + +By default, use refs from the snapshot to interact with page elements. + +```bash +# get snapshot with refs +playwright-cli snapshot + +# interact using a ref +playwright-cli click e15 +``` + +You can also use css selectors or Playwright locators. + +```bash +# css selector +playwright-cli click "#main > button.submit" + +# role locator +playwright-cli click "getByRole('button', { name: 'Submit' })" + +# test id +playwright-cli click "getByTestId('submit-button')" +``` + +## Browser Sessions + +```bash +# create new browser session named "mysession" with persistent profile +playwright-cli -s=mysession open example.com --persistent +# same with manually specified profile directory (use when requested explicitly) +playwright-cli -s=mysession open example.com --profile=/path/to/profile +playwright-cli -s=mysession click e6 +playwright-cli -s=mysession close # stop a named browser +playwright-cli -s=mysession delete-data # delete user data for persistent session + +playwright-cli list +# Close all browsers +playwright-cli close-all +# Forcefully kill all browser processes +playwright-cli kill-all +``` + +## Installation + +If global `playwright-cli` command is not available, try a local version via `npx playwright-cli`: + +```bash +npx --no-install playwright-cli --version +``` + +When local version is available, use `npx playwright-cli` in all commands. Otherwise, install `playwright-cli` as a global command: + +```bash +npm install -g @playwright/cli@latest +``` + +## Example: Form submission + +```bash +playwright-cli open https://example.com/form +playwright-cli snapshot + +playwright-cli fill e1 "user@example.com" +playwright-cli fill e2 "password123" +playwright-cli click e3 +playwright-cli snapshot +playwright-cli close +``` + +## Example: Multi-tab workflow + +```bash +playwright-cli open https://example.com +playwright-cli tab-new https://example.com/other +playwright-cli tab-list +playwright-cli tab-select 0 +playwright-cli snapshot +playwright-cli close +``` + +## Example: Debugging with DevTools + +```bash +playwright-cli open https://example.com +playwright-cli click e4 +playwright-cli fill e7 "test" +playwright-cli console +playwright-cli requests +playwright-cli close +``` + +```bash +playwright-cli open https://example.com +playwright-cli tracing-start +playwright-cli click e4 +playwright-cli fill e7 "test" +playwright-cli tracing-stop +playwright-cli close +``` + +## Example: Interactive session + +Ask the user for UI review or design feedback. The user draws boxes on the live page and types comments; you receive the annotated screenshot, the snapshot of the marked region, and the user's notes. Use this whenever the user asks for "UI review", "design feedback", or to "ask the user what they think / want / mean": + +```bash +playwright-cli open https://example.com +playwright-cli show --annotate +``` + +## Specific tasks + +* **Running and Debugging Playwright tests** [references/playwright-tests.md](references/playwright-tests.md) +* **Request mocking** [references/request-mocking.md](references/request-mocking.md) +* **Running Playwright code** [references/running-code.md](references/running-code.md) +* **Browser session management** [references/session-management.md](references/session-management.md) +* **Spec-driven testing (plan / generate / heal)** [references/spec-driven-testing.md](references/spec-driven-testing.md) +* **Storage state (cookies, localStorage)** [references/storage-state.md](references/storage-state.md) +* **Test generation** [references/test-generation.md](references/test-generation.md) +* **Tracing** [references/tracing.md](references/tracing.md) +* **Video recording** [references/video-recording.md](references/video-recording.md) +* **Inspecting element attributes** [references/element-attributes.md](references/element-attributes.md) diff --git a/.claude/skills/playwright-cli/references/element-attributes.md b/.claude/skills/playwright-cli/references/element-attributes.md new file mode 100644 index 0000000..4e9fa6b --- /dev/null +++ b/.claude/skills/playwright-cli/references/element-attributes.md @@ -0,0 +1,23 @@ +# Inspecting Element Attributes + +When the snapshot doesn't show an element's `id`, `class`, `data-*` attributes, or other DOM properties, use `eval` to inspect them. + +## Examples + +```bash +playwright-cli snapshot +# snapshot shows a button as e7 but doesn't reveal its id or data attributes + +# get the element's id +playwright-cli eval "el => el.id" e7 + +# get all CSS classes +playwright-cli eval "el => el.className" e7 + +# get a specific attribute +playwright-cli eval "el => el.getAttribute('data-testid')" e7 +playwright-cli eval "el => el.getAttribute('aria-label')" e7 + +# get a computed style property +playwright-cli eval "el => getComputedStyle(el).display" e7 +``` diff --git a/.claude/skills/playwright-cli/references/playwright-tests.md b/.claude/skills/playwright-cli/references/playwright-tests.md new file mode 100644 index 0000000..bec2ec9 --- /dev/null +++ b/.claude/skills/playwright-cli/references/playwright-tests.md @@ -0,0 +1,39 @@ +# Running Playwright Tests + +To run Playwright tests, use the `npx playwright test` command, or a package manager script. To avoid opening the interactive html report, use `PLAYWRIGHT_HTML_OPEN=never` environment variable. + +```bash +# Run all tests +PLAYWRIGHT_HTML_OPEN=never npx playwright test + +# Run all tests through a custom npm script +PLAYWRIGHT_HTML_OPEN=never npm run special-test-command +``` + +# Debugging Playwright Tests + +To debug a failing Playwright test, run it with `--debug=cli` option. This command will pause the test at the start and print the debugging instructions. + +**IMPORTANT**: run the command in the background and check the output until "Debugging Instructions" is printed. Make sure to stop the command after you have finished. + +Once instructions containing a session name are printed, use `playwright-cli` to attach the session and explore the page. + +```bash +# Run the test +PLAYWRIGHT_HTML_OPEN=never npx playwright test --debug=cli +# ... +# ... debugging instructions for "tw-abcdef" session ... +# ... + +# Attach to the test +playwright-cli attach tw-abcdef +``` + +Keep the test running in the background while you explore and look for a fix. +The test is paused at the start, so you should step over or pause at a particular location +where the problem is most likely to be. + +Every action you perform with `playwright-cli` generates corresponding Playwright TypeScript code. +This code appears in the output and can be copied directly into the test. Most of the time, a specific locator or an expectation should be updated, but it could also be a bug in the app. Use your judgement. + +After fixing the test, stop the background test run. Rerun to check that test passes. diff --git a/.claude/skills/playwright-cli/references/request-mocking.md b/.claude/skills/playwright-cli/references/request-mocking.md new file mode 100644 index 0000000..9005fda --- /dev/null +++ b/.claude/skills/playwright-cli/references/request-mocking.md @@ -0,0 +1,87 @@ +# Request Mocking + +Intercept, mock, modify, and block network requests. + +## CLI Route Commands + +```bash +# Mock with custom status +playwright-cli route "**/*.jpg" --status=404 + +# Mock with JSON body +playwright-cli route "**/api/users" --body='[{"id":1,"name":"Alice"}]' --content-type=application/json + +# Mock with custom headers +playwright-cli route "**/api/data" --body='{"ok":true}' --header="X-Custom: value" + +# Remove headers from requests +playwright-cli route "**/*" --remove-header=cookie,authorization + +# List active routes +playwright-cli route-list + +# Remove a route or all routes +playwright-cli unroute "**/*.jpg" +playwright-cli unroute +``` + +## URL Patterns + +``` +**/api/users - Exact path match +**/api/*/details - Wildcard in path +**/*.{png,jpg,jpeg} - Match file extensions +**/search?q=* - Match query parameters +``` + +## Advanced Mocking with run-code + +For conditional responses, request body inspection, response modification, or delays: + +### Conditional Response Based on Request + +```bash +playwright-cli run-code "async page => { + await page.route('**/api/login', route => { + const body = route.request().postDataJSON(); + if (body.username === 'admin') { + route.fulfill({ body: JSON.stringify({ token: 'mock-token' }) }); + } else { + route.fulfill({ status: 401, body: JSON.stringify({ error: 'Invalid' }) }); + } + }); +}" +``` + +### Modify Real Response + +```bash +playwright-cli run-code "async page => { + await page.route('**/api/user', async route => { + const response = await route.fetch(); + const json = await response.json(); + json.isPremium = true; + await route.fulfill({ response, json }); + }); +}" +``` + +### Simulate Network Failures + +```bash +playwright-cli run-code "async page => { + await page.route('**/api/offline', route => route.abort('internetdisconnected')); +}" +# Options: connectionrefused, timedout, connectionreset, internetdisconnected +``` + +### Delayed Response + +```bash +playwright-cli run-code "async page => { + await page.route('**/api/slow', async route => { + await new Promise(r => setTimeout(r, 3000)); + route.fulfill({ body: JSON.stringify({ data: 'loaded' }) }); + }); +}" +``` diff --git a/.claude/skills/playwright-cli/references/running-code.md b/.claude/skills/playwright-cli/references/running-code.md new file mode 100644 index 0000000..98b541f --- /dev/null +++ b/.claude/skills/playwright-cli/references/running-code.md @@ -0,0 +1,241 @@ +# Running Custom Playwright Code + +Use `run-code` to execute arbitrary Playwright code for advanced scenarios not covered by CLI commands. + +## Syntax + +```bash +playwright-cli run-code "async page => { + // Your Playwright code here + // Access page.context() for browser context operations +}" +``` + +You can also load the function from a file: + +```bash +playwright-cli run-code --filename=./my-script.js +``` + + +The code must be a single function expression, it is wrapped in `(...)` and evaluated. +import/export/require syntax is not supported. + +## Geolocation + +```bash +# Grant geolocation permission and set location +playwright-cli run-code "async page => { + await page.context().grantPermissions(['geolocation']); + await page.context().setGeolocation({ latitude: 37.7749, longitude: -122.4194 }); +}" + +# Set location to London +playwright-cli run-code "async page => { + await page.context().grantPermissions(['geolocation']); + await page.context().setGeolocation({ latitude: 51.5074, longitude: -0.1278 }); +}" + +# Clear geolocation override +playwright-cli run-code "async page => { + await page.context().clearPermissions(); +}" +``` + +## Permissions + +```bash +# Grant multiple permissions +playwright-cli run-code "async page => { + await page.context().grantPermissions([ + 'geolocation', + 'notifications', + 'camera', + 'microphone' + ]); +}" + +# Grant permissions for specific origin +playwright-cli run-code "async page => { + await page.context().grantPermissions(['clipboard-read'], { + origin: 'https://example.com' + }); +}" +``` + +## Media Emulation + +```bash +# Emulate dark color scheme +playwright-cli run-code "async page => { + await page.emulateMedia({ colorScheme: 'dark' }); +}" + +# Emulate light color scheme +playwright-cli run-code "async page => { + await page.emulateMedia({ colorScheme: 'light' }); +}" + +# Emulate reduced motion +playwright-cli run-code "async page => { + await page.emulateMedia({ reducedMotion: 'reduce' }); +}" + +# Emulate print media +playwright-cli run-code "async page => { + await page.emulateMedia({ media: 'print' }); +}" +``` + +## Wait Strategies + +```bash +# Wait for network idle +playwright-cli run-code "async page => { + await page.waitForLoadState('networkidle'); +}" + +# Wait for specific element +playwright-cli run-code "async page => { + await page.locator('.loading').waitFor({ state: 'hidden' }); +}" + +# Wait for function to return true +playwright-cli run-code "async page => { + await page.waitForFunction(() => window.appReady === true); +}" + +# Wait with timeout +playwright-cli run-code "async page => { + await page.locator('.result').waitFor({ timeout: 10000 }); +}" +``` + +## Frames and Iframes + +```bash +# Work with iframe +playwright-cli run-code "async page => { + const frame = page.locator('iframe#my-iframe').contentFrame(); + await frame.locator('button').click(); +}" + +# Get all frames +playwright-cli run-code "async page => { + const frames = page.frames(); + return frames.map(f => f.url()); +}" +``` + +## File Downloads + +```bash +# Handle file download +playwright-cli run-code "async page => { + const downloadPromise = page.waitForEvent('download'); + await page.getByRole('link', { name: 'Download' }).click(); + const download = await downloadPromise; + await download.saveAs('./downloaded-file.pdf'); + return download.suggestedFilename(); +}" +``` + +## Clipboard + +```bash +# Read clipboard (requires permission) +playwright-cli run-code "async page => { + await page.context().grantPermissions(['clipboard-read']); + return await page.evaluate(() => navigator.clipboard.readText()); +}" + +# Write to clipboard +playwright-cli run-code "async page => { + await page.evaluate(text => navigator.clipboard.writeText(text), 'Hello clipboard!'); +}" +``` + +## Page Information + +```bash +# Get page title +playwright-cli run-code "async page => { + return await page.title(); +}" + +# Get current URL +playwright-cli run-code "async page => { + return page.url(); +}" + +# Get page content +playwright-cli run-code "async page => { + return await page.content(); +}" + +# Get viewport size +playwright-cli run-code "async page => { + return page.viewportSize(); +}" +``` + +## JavaScript Execution + +```bash +# Execute JavaScript and return result +playwright-cli run-code "async page => { + return await page.evaluate(() => { + return { + userAgent: navigator.userAgent, + language: navigator.language, + cookiesEnabled: navigator.cookieEnabled + }; + }); +}" + +# Pass arguments to evaluate +playwright-cli run-code "async page => { + const multiplier = 5; + return await page.evaluate(m => document.querySelectorAll('li').length * m, multiplier); +}" +``` + +## Error Handling + +```bash +# Try-catch in run-code +playwright-cli run-code "async page => { + try { + await page.getByRole('button', { name: 'Submit' }).click({ timeout: 1000 }); + return 'clicked'; + } catch (e) { + return 'element not found'; + } +}" +``` + +## Complex Workflows + +```bash +# Login and save state +playwright-cli run-code "async page => { + await page.goto('https://example.com/login'); + await page.getByRole('textbox', { name: 'Email' }).fill('user@example.com'); + await page.getByRole('textbox', { name: 'Password' }).fill('secret'); + await page.getByRole('button', { name: 'Sign in' }).click(); + await page.waitForURL('**/dashboard'); + await page.context().storageState({ path: 'auth.json' }); + return 'Login successful'; +}" + +# Scrape data from multiple pages +playwright-cli run-code "async page => { + const results = []; + for (let i = 1; i <= 3; i++) { + await page.goto(\`https://example.com/page/\${i}\`); + const items = await page.locator('.item').allTextContents(); + results.push(...items); + } + return results; +}" +``` diff --git a/.claude/skills/playwright-cli/references/session-management.md b/.claude/skills/playwright-cli/references/session-management.md new file mode 100644 index 0000000..bf39acd --- /dev/null +++ b/.claude/skills/playwright-cli/references/session-management.md @@ -0,0 +1,225 @@ +# Browser Session Management + +Run multiple isolated browser sessions concurrently with state persistence. + +## Named Browser Sessions + +Use `-s` flag to isolate browser contexts: + +```bash +# Browser 1: Authentication flow +playwright-cli -s=auth open https://app.example.com/login + +# Browser 2: Public browsing (separate cookies, storage) +playwright-cli -s=public open https://example.com + +# Commands are isolated by browser session +playwright-cli -s=auth fill e1 "user@example.com" +playwright-cli -s=public snapshot +``` + +## Browser Session Isolation Properties + +Each browser session has independent: +- Cookies +- LocalStorage / SessionStorage +- IndexedDB +- Cache +- Browsing history +- Open tabs + +## Browser Session Commands + +```bash +# List all browser sessions +playwright-cli list + +# Stop a browser session (close the browser) +playwright-cli close # stop the default browser +playwright-cli -s=mysession close # stop a named browser + +# Stop all browser sessions +playwright-cli close-all + +# Forcefully kill all daemon processes (for stale/zombie processes) +playwright-cli kill-all + +# Delete browser session user data (profile directory) +playwright-cli delete-data # delete default browser data +playwright-cli -s=mysession delete-data # delete named browser data +``` + +## Environment Variable + +Set a default browser session name via environment variable: + +```bash +export PLAYWRIGHT_CLI_SESSION="mysession" +playwright-cli open example.com # Uses "mysession" automatically +``` + +## Common Patterns + +### Concurrent Scraping + +```bash +#!/bin/bash +# Scrape multiple sites concurrently + +# Start all browsers +playwright-cli -s=site1 open https://site1.com & +playwright-cli -s=site2 open https://site2.com & +playwright-cli -s=site3 open https://site3.com & +wait + +# Take snapshots from each +playwright-cli -s=site1 snapshot +playwright-cli -s=site2 snapshot +playwright-cli -s=site3 snapshot + +# Cleanup +playwright-cli close-all +``` + +### A/B Testing Sessions + +```bash +# Test different user experiences +playwright-cli -s=variant-a open "https://app.com?variant=a" +playwright-cli -s=variant-b open "https://app.com?variant=b" + +# Compare +playwright-cli -s=variant-a screenshot +playwright-cli -s=variant-b screenshot +``` + +### Persistent Profile + +By default, browser profile is kept in memory only. Use `--persistent` flag on `open` to persist the browser profile to disk: + +```bash +# Use persistent profile (auto-generated location) +playwright-cli open https://example.com --persistent + +# Use persistent profile with custom directory +playwright-cli open https://example.com --profile=/path/to/profile +``` + +## Attaching to a Running Browser + +Use `attach` to connect to a browser that is already running, instead of launching a new one. + +### Attach by channel name + +Connect to a running Chrome or Edge instance by its channel name. The browser must have remote debugging enabled — navigate to `chrome://inspect/#remote-debugging` in the target browser and check "Allow remote debugging for this browser instance". + +```bash +# Attach to Chrome +playwright-cli attach --cdp=chrome + +# Attach to Chrome Canary +playwright-cli attach --cdp=chrome-canary + +# Attach to Microsoft Edge +playwright-cli attach --cdp=msedge + +# Attach to Edge Dev +playwright-cli attach --cdp=msedge-dev +``` + +Supported channels: `chrome`, `chrome-beta`, `chrome-dev`, `chrome-canary`, `msedge`, `msedge-beta`, `msedge-dev`, `msedge-canary`. + +When `--session` is not provided, the session is named after the channel (e.g. `--cdp=msedge` creates a session called `msedge`), so parallel attaches to Chrome and Edge don't collide on `default`. Pass `--session=` to override. + +### Attach via CDP endpoint + +Connect to a browser that exposes a Chrome DevTools Protocol endpoint: + +```bash +playwright-cli attach --cdp=http://localhost:9222 +``` + +### Attach via browser extension + +Connect to a browser with the Playwright extension installed: + +```bash +playwright-cli attach --extension +``` + +### Detach + +Tear down an attached session without affecting the external browser: + +```bash +# Detach the default attached session +playwright-cli detach + +# Detach a specific attached session +playwright-cli -s=msedge detach +``` + +`detach` only works on sessions created via `attach`. For sessions created via `open`, use `close`. + +## Default Browser Session + +When `-s` is omitted, commands use the default browser session: + +```bash +# These use the same default browser session +playwright-cli open https://example.com +playwright-cli snapshot +playwright-cli close # Stops default browser +``` + +## Browser Session Configuration + +Configure a browser session with specific settings when opening: + +```bash +# Open with config file +playwright-cli open https://example.com --config=.playwright/my-cli.json + +# Open with specific browser +playwright-cli open https://example.com --browser=firefox + +# Open in headed mode +playwright-cli open https://example.com --headed + +# Open with persistent profile +playwright-cli open https://example.com --persistent +``` + +## Best Practices + +### 1. Name Browser Sessions Semantically + +```bash +# GOOD: Clear purpose +playwright-cli -s=github-auth open https://github.com +playwright-cli -s=docs-scrape open https://docs.example.com + +# AVOID: Generic names +playwright-cli -s=s1 open https://github.com +``` + +### 2. Always Clean Up + +```bash +# Stop browsers when done +playwright-cli -s=auth close +playwright-cli -s=scrape close + +# Or stop all at once +playwright-cli close-all + +# If browsers become unresponsive or zombie processes remain +playwright-cli kill-all +``` + +### 3. Delete Stale Browser Data + +```bash +# Remove old browser data to free disk space +playwright-cli -s=oldsession delete-data +``` diff --git a/.claude/skills/playwright-cli/references/spec-driven-testing.md b/.claude/skills/playwright-cli/references/spec-driven-testing.md new file mode 100644 index 0000000..f37bdb4 --- /dev/null +++ b/.claude/skills/playwright-cli/references/spec-driven-testing.md @@ -0,0 +1,305 @@ +# Spec-driven testing (plan → generate → heal) + +End-to-end workflow for authoring and maintaining Playwright tests using `playwright-cli`. The three sections below can be used independently: + +- **Planning** — explore the app, produce a spec file describing what to test. +- **Generate** — turn a spec into Playwright test files. Update the spec if it's vague or stale. +- **Heal** — diagnose failing tests, fix the code, reconcile the spec with reality. + +All three lean on the same mechanic: run `npx playwright test --debug=cli` in the background, then `playwright-cli attach tw-XXXX` to drive the paused page interactively. See [playwright-tests.md](playwright-tests.md) for the debug/attach mechanics and [test-generation.md](test-generation.md) for how every `playwright-cli` action emits Playwright TypeScript. + +--- + +## 1. Planning + +Goal: produce a spec file (e.g. `specs/.plan.md`) that enumerates the scenarios to test. **Always** write the spec to a file. + +### 1.1 Prerequisite: workspace + +Check the workspace has Playwright installed before anything else: + +```bash +# Either of these confirms a workspace: +test -f playwright.config.ts || test -f playwright.config.js +npx --no-install playwright --version +``` + +If there is no Playwright install, bootstrap one and let the user pick the defaults: + +```bash +npm init playwright@latest +``` + +### 1.2 Prerequisite: seed test + +A **seed test** is a minimal test that lands the page in the state every scenario starts from: navigation to the app, any required login, feature flags, etc. Scenarios assume a fresh start *after* the seed. `--debug=cli` pauses *inside* this test, so the seed is where every planning and generation session begins. + +Minimum viable seed: + +```ts +// tests/seed.spec.ts +import { test } from '@playwright/test'; + +test('seed', async ({ page }) => { + await page.goto('https://example.com/'); +}); +``` + +Preferred — push navigation into a fixture so scenario tests reuse it: + +```ts +// tests/fixtures.ts +import { test as baseTest } from '@playwright/test'; +export { expect } from '@playwright/test'; + +export const test = baseTest.extend({ + page: async ({ page }, use) => { + await page.goto('https://example.com/'); + await use(page); + }, +}); +``` + +```ts +// tests/seed.spec.ts +import { test } from './fixtures'; + +test('seed', async ({ page }) => { + // Fixture already navigates. This empty body tells agents where to start. +}); +``` + +If no seed exists, create one that at least navigates to the app. + +### 1.3 Explore the app + +Launch the app via the seed in the background and attach: + +```bash +PLAYWRIGHT_HTML_OPEN=never npx playwright test tests/seed.spec.ts --debug=cli +# wait for "Debugging Instructions" and the session name tw-XXXX +playwright-cli attach tw-XXXX +``` + +Resume so the seed runs, then probe the app: + +```bash +playwright-cli resume # resume so that seed test runs fully +playwright-cli snapshot # inventory of interactive elements +playwright-cli click e5 # follow a flow +playwright-cli eval "location.href" # read URL / state +playwright-cli show --annotate # ask the user to point at something +``` + +Map out: + +- Interactive surfaces (forms, buttons, lists, filters, modals). +- Primary user journeys end-to-end. +- Edge cases: empty states, validation errors, very long input, boundary values. +- Persistence: reload, local/session storage, URL fragments. +- Navigation: which controls change the URL, back/forward behaviour. + +**Important**: Do not just open the app url with playwright-cli, always go through the test to capture any custom setup done there. +**Important**: Stop the background test when done exploring. + +### 1.4 Write the spec file + +Save under `specs/.plan.md`. Use this structure: + +```markdown +# Test Plan + +## Application Overview + + + +## Test Scenarios + +### 1. + +**Seed:** `tests/seed.spec.ts` + +#### 1.1. + +**File:** `tests//.spec.ts` + +**Steps:** + 1. + - expect: + - expect: + 2. + - expect: + +#### 1.2. +... + +### 2. + +**Seed:** `tests/seed.spec.ts` +... +``` + +Guidelines: + +- Each scenario is independent and starts from the seed's fresh state — never chain scenarios. +- Scenario names are kebab-case and match the test file name (`should-add-single-todo` → `should-add-single-todo.spec.ts`). +- Cover happy path, edge cases, validation, negative flows, persistence. +- Write steps at the user level ("Type 'Buy milk' into the input"), not the API level ("call `fill`"). +- Put observable outcomes in `- expect:` bullets; each becomes an assertion during generation. + +--- + +## 2. Generate + +Goal: take a spec file and produce Playwright test files. Optionally update the spec if it has drifted. + +### 2.1 Inputs + +- **Spec file**, e.g. `specs/basic-operations.plan.md`. +- **Target**: either a single scenario (e.g. `1.2`), a whole group (`1`), or all. +- **Seed file**, read from the `**Seed:**` line of the scenario's group. + +### 2.2 Generate one scenario + +For each target scenario, in sequence (never in parallel — scenarios share the seed session): + +```bash +PLAYWRIGHT_HTML_OPEN=never npx playwright test --debug=cli # background +playwright-cli attach tw-XXXX +# resume +``` + +**Do not** just open the app url with playwright-cli, always go through the test to capture any custom setup done there. + +Walk the scenario's `Steps:` one by one with `playwright-cli`, treating the spec as the plan and the live app as the source of truth. If a step is vague ("click the button" — which button?), references an element that no longer exists, or contradicts the app's actual behaviour, use your judgement: update the spec to match what the app really does, then keep going. Editing the spec mid-generation is expected. + +Every action prints the equivalent Playwright TypeScript (see [test-generation.md](test-generation.md)): + +```bash +playwright-cli snapshot # find refs +playwright-cli fill e3 "John Doe" # -> page.getByRole('textbox', {...}).fill(...) +playwright-cli press Enter +playwright-cli click e7 +``` + +For each `- expect:` bullet, add an explicit assertion. See [test-generation.md](test-generation.md) for details. + +Collect the generated code and write the test file at the path given in the spec: + +```ts +// spec: specs/basic-operations.plan.md +// seed: tests/seed.spec.ts +import { test, expect } from './fixtures'; // or '@playwright/test' if no fixtures file + +test.describe('Singing in and out', () => { + test('should sign in', async ({ page }) => { + // 1. Navigate to the application + // (handled by the seed fixture) + + // 2. Type 'John Doe' into the username field + await page.getByRole('textbox', { name: 'username' }).fill('John Doe'); + + // 3. Type password + await page.getByRole('textbox', { name: 'password' }).fill('TestPassword'); + + // 4. Press Enter to submit + await page.getByRole('textbox', { name: 'password' }).press('Enter'); + + await expect(page.getByRole('heading')).toContainText('Welcome, John Doe!'); + }); +}); +``` + +Rules: + +- **One test per file.** File path, describe name, and test name come verbatim from the spec (minus the ordinal). +- Prefix each numbered step with a `// N. ` comment before its actions. +- Use the describe group name verbatim from the spec (no `1.` ordinal). +- Import from `./fixtures` if the project has one; otherwise `@playwright/test`. +- **Important**: close the CLI session and stop the background test before moving to the next scenario. + +### 2.3 Generate multiple scenarios + +Loop 2.2 over the targeted scenarios one at a time, restarting the seed between each so every test starts from a clean page. This is safe to parallelise due to unique generated session names - just make sure each test run is stopped. + +### 2.4 Run generated tests + +After generation, run the new tests once: + +```bash +PLAYWRIGHT_HTML_OPEN=never npx playwright test tests//.spec.ts +``` + +Any failure goes to Section 3. + +--- + +## 3. Heal + +Goal: fix failing tests, and update the spec if the app's intended behaviour changed. + +### 3.1 Find failing tests + +```bash +PLAYWRIGHT_HTML_OPEN=never npx playwright test +``` + +Record the list of failing `:` entries and process them one at a time. Do not attempt parallel fixes — shared state and the single CLI session make that fragile. + +### 3.2 Debug one failure + +Run the single failing test in debug mode in the background, then attach: + +```bash +PLAYWRIGHT_HTML_OPEN=never npx playwright test tests//.spec.ts: --debug=cli +# wait for "Debugging Instructions" and the tw-XXXX session name +playwright-cli attach tw-XXXX +``` + +The test is paused at the start. Step forward or run to until just before the failing action or assertion, then diagnose: + +```bash +playwright-cli snapshot # did the element change / move / rename? +playwright-cli console # app-side errors? +playwright-cli network # failed request? wrong payload? +playwright-cli show --annotate # ask the user to point somewhere +``` + +Common causes: selector drift, new wrapper element, label/ARIA rename, timing (transition, async load), assertion text updated in the app, test data leaking between runs. + +Rehearse the corrected interaction with `playwright-cli` — the generated code in the output is what you paste back into the test. + +### 3.3 Apply the fix + +Edit the test file: update the locator, assertion, step order, or inputs to match the corrected behaviour. Stop the background debug run. Rerun the single test to confirm green. + +Never skip hooks or add sleeps as a fix. Never use `networkidle`. + +### 3.4 Reconcile with the spec + +Open the spec referenced by the `// spec:` header in the test file and locate the scenario that matches the test. + +- **Fix was purely technical** (locator drift, better assertion shape) and the spec's user-level behaviour still matches the app → leave the spec alone. +- **Fix changed user-visible steps, inputs, order, or expected outcomes** that the spec describes → update the spec to match reality. Keep the scenario id and file path stable; only the step / expect lines change. +- **Unclear whether the app change is intentional** (spec is stale) **or a regression** (test was right, app is wrong) → **stop and ask the user**. Provide: + - the scenario id (e.g. `2.3`), + - the spec lines that no longer match, + - the observed app behaviour (quote a snapshot excerpt or a concrete outcome). + +Only after the user answers, either update the spec (intentional change) or file/flag the test as covering a bug (regression). + +### 3.5 Iteration and giving up + +- Fix failures one at a time; rerun after each. +- If after thorough investigation you are confident the test is correct but the app is wrong *and* the user has confirmed it's a bug: mark the test `test.fixme(...)` with a comment pointing at the user's decision or issue link. Never silently skip. + +--- + +## Cross-references + +| For... | See | +|---|---| +| `--debug=cli` / attach mechanics | [playwright-tests.md](playwright-tests.md) | +| How `playwright-cli` actions become TS | [test-generation.md](test-generation.md) | +| Mocking requests during exploration/generation | [request-mocking.md](request-mocking.md) | +| Managing the CLI browser session | [session-management.md](session-management.md) | diff --git a/.claude/skills/playwright-cli/references/storage-state.md b/.claude/skills/playwright-cli/references/storage-state.md new file mode 100644 index 0000000..c856db5 --- /dev/null +++ b/.claude/skills/playwright-cli/references/storage-state.md @@ -0,0 +1,275 @@ +# Storage Management + +Manage cookies, localStorage, sessionStorage, and browser storage state. + +## Storage State + +Save and restore complete browser state including cookies and storage. + +### Save Storage State + +```bash +# Save to auto-generated filename (storage-state-{timestamp}.json) +playwright-cli state-save + +# Save to specific filename +playwright-cli state-save my-auth-state.json +``` + +### Restore Storage State + +```bash +# Load storage state from file +playwright-cli state-load my-auth-state.json + +# Reload page to apply cookies +playwright-cli open https://example.com +``` + +### Storage State File Format + +The saved file contains: + +```json +{ + "cookies": [ + { + "name": "session_id", + "value": "abc123", + "domain": "example.com", + "path": "/", + "expires": 1735689600, + "httpOnly": true, + "secure": true, + "sameSite": "Lax" + } + ], + "origins": [ + { + "origin": "https://example.com", + "localStorage": [ + { "name": "theme", "value": "dark" }, + { "name": "user_id", "value": "12345" } + ] + } + ] +} +``` + +## Cookies + +### List All Cookies + +```bash +playwright-cli cookie-list +``` + +### Filter Cookies by Domain + +```bash +playwright-cli cookie-list --domain=example.com +``` + +### Filter Cookies by Path + +```bash +playwright-cli cookie-list --path=/api +``` + +### Get Specific Cookie + +```bash +playwright-cli cookie-get session_id +``` + +### Set a Cookie + +```bash +# Basic cookie +playwright-cli cookie-set session abc123 + +# Cookie with options +playwright-cli cookie-set session abc123 --domain=example.com --path=/ --httpOnly --secure --sameSite=Lax + +# Cookie with expiration (Unix timestamp) +playwright-cli cookie-set remember_me token123 --expires=1735689600 +``` + +### Delete a Cookie + +```bash +playwright-cli cookie-delete session_id +``` + +### Clear All Cookies + +```bash +playwright-cli cookie-clear +``` + +### Advanced: Multiple Cookies or Custom Options + +For complex scenarios like adding multiple cookies at once, use `run-code`: + +```bash +playwright-cli run-code "async page => { + await page.context().addCookies([ + { name: 'session_id', value: 'sess_abc123', domain: 'example.com', path: '/', httpOnly: true }, + { name: 'preferences', value: JSON.stringify({ theme: 'dark' }), domain: 'example.com', path: '/' } + ]); +}" +``` + +## Local Storage + +### List All localStorage Items + +```bash +playwright-cli localstorage-list +``` + +### Get Single Value + +```bash +playwright-cli localstorage-get token +``` + +### Set Value + +```bash +playwright-cli localstorage-set theme dark +``` + +### Set JSON Value + +```bash +playwright-cli localstorage-set user_settings '{"theme":"dark","language":"en"}' +``` + +### Delete Single Item + +```bash +playwright-cli localstorage-delete token +``` + +### Clear All localStorage + +```bash +playwright-cli localstorage-clear +``` + +### Advanced: Multiple Operations + +For complex scenarios like setting multiple values at once, use `run-code`: + +```bash +playwright-cli run-code "async page => { + await page.evaluate(() => { + localStorage.setItem('token', 'jwt_abc123'); + localStorage.setItem('user_id', '12345'); + localStorage.setItem('expires_at', Date.now() + 3600000); + }); +}" +``` + +## Session Storage + +### List All sessionStorage Items + +```bash +playwright-cli sessionstorage-list +``` + +### Get Single Value + +```bash +playwright-cli sessionstorage-get form_data +``` + +### Set Value + +```bash +playwright-cli sessionstorage-set step 3 +``` + +### Delete Single Item + +```bash +playwright-cli sessionstorage-delete step +``` + +### Clear sessionStorage + +```bash +playwright-cli sessionstorage-clear +``` + +## IndexedDB + +### List Databases + +```bash +playwright-cli run-code "async page => { + return await page.evaluate(async () => { + const databases = await indexedDB.databases(); + return databases; + }); +}" +``` + +### Delete Database + +```bash +playwright-cli run-code "async page => { + await page.evaluate(() => { + indexedDB.deleteDatabase('myDatabase'); + }); +}" +``` + +## Common Patterns + +### Authentication State Reuse + +```bash +# Step 1: Login and save state +playwright-cli open https://app.example.com/login +playwright-cli snapshot +playwright-cli fill e1 "user@example.com" +playwright-cli fill e2 "password123" +playwright-cli click e3 + +# Save the authenticated state +playwright-cli state-save auth.json + +# Step 2: Later, restore state and skip login +playwright-cli state-load auth.json +playwright-cli open https://app.example.com/dashboard +# Already logged in! +``` + +### Save and Restore Roundtrip + +```bash +# Set up authentication state +playwright-cli open https://example.com +playwright-cli eval "() => { document.cookie = 'session=abc123'; localStorage.setItem('user', 'john'); }" + +# Save state to file +playwright-cli state-save my-session.json + +# ... later, in a new session ... + +# Restore state +playwright-cli state-load my-session.json +playwright-cli open https://example.com +# Cookies and localStorage are restored! +``` + +## Security Notes + +- Never commit storage state files containing auth tokens +- Add `*.auth-state.json` to `.gitignore` +- Delete state files after automation completes +- Use environment variables for sensitive data +- By default, sessions run in-memory mode which is safer for sensitive operations diff --git a/.claude/skills/playwright-cli/references/test-generation.md b/.claude/skills/playwright-cli/references/test-generation.md new file mode 100644 index 0000000..a045c55 --- /dev/null +++ b/.claude/skills/playwright-cli/references/test-generation.md @@ -0,0 +1,134 @@ +# Test Generation + +Generate Playwright test code automatically as you interact with the browser. + +## How It Works + +Every action you perform with `playwright-cli` generates corresponding Playwright TypeScript code. +This code appears in the output and can be copied directly into your test files. + +## Example Workflow + +```bash +# Start a session +playwright-cli open https://example.com/login + +# Take a snapshot to see elements +playwright-cli snapshot +# Output shows: e1 [textbox "Email"], e2 [textbox "Password"], e3 [button "Sign In"] + +# Fill form fields - generates code automatically +playwright-cli fill e1 "user@example.com" +# Ran Playwright code: +# await page.getByRole('textbox', { name: 'Email' }).fill('user@example.com'); + +playwright-cli fill e2 "password123" +# Ran Playwright code: +# await page.getByRole('textbox', { name: 'Password' }).fill('password123'); + +playwright-cli click e3 +# Ran Playwright code: +# await page.getByRole('button', { name: 'Sign In' }).click(); +``` + +## Building a Test File + +Collect the generated code into a Playwright test: + +```typescript +import { test, expect } from '@playwright/test'; + +test('login flow', async ({ page }) => { + // Generated code from playwright-cli session: + await page.goto('https://example.com/login'); + await page.getByRole('textbox', { name: 'Email' }).fill('user@example.com'); + await page.getByRole('textbox', { name: 'Password' }).fill('password123'); + await page.getByRole('button', { name: 'Sign In' }).click(); + + // Add assertions + await expect(page).toHaveURL(/.*dashboard/); +}); +``` + +## Best Practices + +### 1. Use Semantic Locators + +The generated code uses role-based locators when possible, which are more resilient: + +```typescript +// Generated (good - semantic) +await page.getByRole('button', { name: 'Submit' }).click(); + +// Avoid (fragile - CSS selectors) +await page.locator('#submit-btn').click(); +``` + +### 2. Explore Before Recording + +Take snapshots to understand the page structure before recording actions: + +```bash +playwright-cli open https://example.com +playwright-cli snapshot +# Review the element structure +playwright-cli click e5 +``` + +### 3. Add Assertions Manually + +Generated code captures actions but not assertions. Add expectations in your test using one of the recommended matchers: + +- `toBeVisible()` — element is rendered and visible +- `toHaveText(text)` — element text content matches +- `toHaveValue(value) / toBeEmpty()` — input/select value matches +- `toBeChecked() / toBeUnchecked()` — checkbox state matches +- `toMatchAriaSnapshot(snapshot)` — page (or locator) matches a partial accessibility snapshot + +Use `playwright-cli generate-locator ` to produce the locator expression for the assertion, and the snapshot/eval commands to capture the expected value. + +When asserting text content, make sure that generated locator does not contain text from the element itself. `getByTestId()` or `getByLabel()` usually work well with asserting text. When locator is text-based, prefer `toBeVisible()` instead. + +Snapshot to be matched does not have to contain all the information - only capture what's necessary for the assertion. You can use regular expressions for unstable values. + +```bash +# Get a stable locator for an element ref to use in the assertion +playwright-cli --raw generate-locator e5 +# getByRole('button', { name: 'Submit' }) + +# Capture expected text content for toHaveText +playwright-cli --raw eval "el => el.textContent" e5 + +# Capture expected input value for toHaveValue/toBeEmpty +playwright-cli --raw eval "el => el.value" e5 + +# Capture expected aria snapshot for toMatchAriaSnapshot/toBeChecked +# (whole page, or use a ref to scope to a region) +playwright-cli --raw snapshot +playwright-cli --raw snapshot e5 +``` + +```typescript +// Generated action +await page.getByRole('button', { name: 'Submit' }).click(); + +// Manual assertions using the outputs above: +await expect(page.getByRole('alert', { name: 'Success' })).toBeVisible(); +await expect(page.getByTestId('main-header')).toHaveText('Welcome, user'); +await expect(page.getByRole('textbox', { name: 'Email' })).toHaveValue('user@example.com'); +await expect(page.getByRole('checkbox', { name: 'Enable notifications' })).toBeChecked(); + +// toMatchAriaSnapshot on the whole page, finds a matching region +await expect(page).toMatchAriaSnapshot(` + - heading "Welcome, user" + - link /\\d+ new messages?/ + - button "Sign out" +`); + +// toMatchAriaSnapshot scoped to a region +await expect(page.getByRole('navigation')).toMatchAriaSnapshot(` + - link "Home" + - link /\\d+ new messages?/ + - link "Profile" +`); +``` diff --git a/.claude/skills/playwright-cli/references/tracing.md b/.claude/skills/playwright-cli/references/tracing.md new file mode 100644 index 0000000..7ce7bab --- /dev/null +++ b/.claude/skills/playwright-cli/references/tracing.md @@ -0,0 +1,139 @@ +# Tracing + +Capture detailed execution traces for debugging and analysis. Traces include DOM snapshots, screenshots, network activity, and console logs. + +## Basic Usage + +```bash +# Start trace recording +playwright-cli tracing-start + +# Perform actions +playwright-cli open https://example.com +playwright-cli click e1 +playwright-cli fill e2 "test" + +# Stop trace recording +playwright-cli tracing-stop +``` + +## Trace Output Files + +When you start tracing, Playwright creates a `traces/` directory with several files: + +### `trace-{timestamp}.trace` + +**Action log** - The main trace file containing: +- Every action performed (clicks, fills, navigations) +- DOM snapshots before and after each action +- Screenshots at each step +- Timing information +- Console messages +- Source locations + +### `trace-{timestamp}.network` + +**Network log** - Complete network activity: +- All HTTP requests and responses +- Request headers and bodies +- Response headers and bodies +- Timing (DNS, connect, TLS, TTFB, download) +- Resource sizes +- Failed requests and errors + +### `resources/` + +**Resources directory** - Cached resources: +- Images, fonts, stylesheets, scripts +- Response bodies for replay +- Assets needed to reconstruct page state + +## What Traces Capture + +| Category | Details | +|----------|---------| +| **Actions** | Clicks, fills, hovers, keyboard input, navigations | +| **DOM** | Full DOM snapshot before/after each action | +| **Screenshots** | Visual state at each step | +| **Network** | All requests, responses, headers, bodies, timing | +| **Console** | All console.log, warn, error messages | +| **Timing** | Precise timing for each operation | + +## Use Cases + +### Debugging Failed Actions + +```bash +playwright-cli tracing-start +playwright-cli open https://app.example.com + +# This click fails - why? +playwright-cli click e5 + +playwright-cli tracing-stop +# Open trace to see DOM state when click was attempted +``` + +### Analyzing Performance + +```bash +playwright-cli tracing-start +playwright-cli open https://slow-site.com +playwright-cli tracing-stop + +# View network waterfall to identify slow resources +``` + +### Capturing Evidence + +```bash +# Record a complete user flow for documentation +playwright-cli tracing-start + +playwright-cli open https://app.example.com/checkout +playwright-cli fill e1 "4111111111111111" +playwright-cli fill e2 "12/25" +playwright-cli fill e3 "123" +playwright-cli click e4 + +playwright-cli tracing-stop +# Trace shows exact sequence of events +``` + +## Trace vs Video vs Screenshot + +| Feature | Trace | Video | Screenshot | +|---------|-------|-------|------------| +| **Format** | .trace file | .webm video | .png/.jpeg image | +| **DOM inspection** | Yes | No | No | +| **Network details** | Yes | No | No | +| **Step-by-step replay** | Yes | Continuous | Single frame | +| **File size** | Medium | Large | Small | +| **Best for** | Debugging | Demos | Quick capture | + +## Best Practices + +### 1. Start Tracing Before the Problem + +```bash +# Trace the entire flow, not just the failing step +playwright-cli tracing-start +playwright-cli open https://example.com +# ... all steps leading to the issue ... +playwright-cli tracing-stop +``` + +### 2. Clean Up Old Traces + +Traces can consume significant disk space: + +```bash +# Remove traces older than 7 days +find .playwright-cli/traces -mtime +7 -delete +``` + +## Limitations + +- Traces add overhead to automation +- Large traces can consume significant disk space +- Some dynamic content may not replay perfectly diff --git a/.claude/skills/playwright-cli/references/video-recording.md b/.claude/skills/playwright-cli/references/video-recording.md new file mode 100644 index 0000000..ce9ad6a --- /dev/null +++ b/.claude/skills/playwright-cli/references/video-recording.md @@ -0,0 +1,143 @@ +# Video Recording + +Capture browser automation sessions as video for debugging, documentation, or verification. Produces WebM (VP8/VP9 codec). + +## Basic Recording + +```bash +# Open browser first +playwright-cli open + +# Start recording +playwright-cli video-start demo.webm + +# Add a chapter marker for section transitions +playwright-cli video-chapter "Getting Started" --description="Opening the homepage" --duration=2000 + +# Navigate and perform actions +playwright-cli goto https://example.com +playwright-cli snapshot +playwright-cli click e1 + +# Add another chapter +playwright-cli video-chapter "Filling Form" --description="Entering test data" --duration=2000 +playwright-cli fill e2 "test input" + +# Stop and save +playwright-cli video-stop +``` + +## Best Practices + +### 1. Use Descriptive Filenames + +```bash +# Include context in filename +playwright-cli video-start recordings/login-flow-2024-01-15.webm +playwright-cli video-start recordings/checkout-test-run-42.webm +``` + +### 2. Record entire hero scripts. + +When recording a video for the user or as a proof of work, it is best to create a code snippet and execute it with run-code. +It allows pulling appropriate pauses between the actions and annotating the video. There are new Playwright APIs for that. + +1) Perform scenario using CLI and take note of all locators and actions. You'll need those locators to request their bounding boxes for highlight. +2) Create a file with the intended script for video (below). Use pressSequentially w/ delay for nice typing, make reasonable pauses. +3) Use playwright-cli run-code --filename your-script.js + +**Important**: Overlays are `pointer-events: none` — they do not interfere with page interactions. You can safely keep sticky overlays visible while clicking, filling, or performing any actions on the page. + +```js +async page => { + await page.screencast.start({ path: 'video.webm', size: { width: 1280, height: 800 } }); + await page.goto('https://demo.playwright.dev/todomvc'); + + // Show a chapter card — blurs the page and shows a dialog. + // Blocks until duration expires, then auto-removes. + // Use this for simple use cases, but always feel free to hand-craft your own beautiful + // overlay via await page.screencast.showOverlay(). + await page.screencast.showChapter('Adding Todo Items', { + description: 'We will add several items to the todo list.', + duration: 2000, + }); + + // Perform action + await page.getByRole('textbox', { name: 'What needs to be done?' }).pressSequentially('Walk the dog', { delay: 60 }); + await page.getByRole('textbox', { name: 'What needs to be done?' }).press('Enter'); + await page.waitForTimeout(1000); + + // Show next chapter + await page.screencast.showChapter('Verifying Results', { + description: 'Checking the item appeared in the list.', + duration: 2000, + }); + + // Add a sticky annotation that stays while you perform actions. + // Overlays are pointer-events: none, so they won't block clicks. + const annotation = await page.screencast.showOverlay(` +
+ ✓ Item added successfully +
+ `); + + // Perform more actions while the annotation is visible + await page.getByRole('textbox', { name: 'What needs to be done?' }).pressSequentially('Buy groceries', { delay: 60 }); + await page.getByRole('textbox', { name: 'What needs to be done?' }).press('Enter'); + await page.waitForTimeout(1500); + + // Remove the annotation when done + await annotation.dispose(); + + // You can also highlight relevant locators and provide contextual annotations. + const bounds = await page.getByText('Walk the dog').boundingBox(); + await page.screencast.showOverlay(` +
+
+
Check it out, it is right above this text +
+ `, { duration: 2000 }); + + await page.screencast.stop(); +} +``` + +Embrace creativity, overlays are powerful. + +### Overlay API Summary + +| Method | Use Case | +|--------|----------| +| `page.screencast.showChapter(title, { description?, duration?, styleSheet? })` | Full-screen chapter card with blurred backdrop — ideal for section transitions | +| `page.screencast.showOverlay(html, { duration? })` | Custom HTML overlay — use for callouts, labels, highlights | +| `disposable.dispose()` | Remove a sticky overlay added without duration | +| `page.screencast.hideOverlays()` / `page.screencast.showOverlays()` | Temporarily hide/show all overlays | + +## Tracing vs Video + +| Feature | Video | Tracing | +|---------|-------|---------| +| Output | WebM file | Trace file (viewable in Trace Viewer) | +| Shows | Visual recording | DOM snapshots, network, console, actions | +| Use case | Demos, documentation | Debugging, analysis | +| Size | Larger | Smaller | + +## Limitations + +- Recording adds slight overhead to automation +- Large recordings can consume significant disk space diff --git a/.github/agents/design.agent.md b/.github/agents/design.agent.md index 2eb8cd0..9b6b45f 100644 --- a/.github/agents/design.agent.md +++ b/.github/agents/design.agent.md @@ -3,12 +3,12 @@ description: "Switch to the Design track" tools: ["read", "edit"] --- -Switch to the Design track (lessons 29-34). Do the following steps in order: +Switch to the Design track (lessons 30-35). Do the following steps in order: 1. Read the file `tutorial/state.json` 2. Set the `track` field to `"design"` -3. If `currentLesson` is less than 29, set `currentLesson` to 29 (otherwise keep it as-is) +3. If `currentLesson` is less than 30, set `currentLesson` to 30 (otherwise keep it as-is) 4. Write the updated JSON back to `tutorial/state.json` -5. Determine the lesson filename: look in `tutorial/lessons/` for the file whose name starts with the zero-padded lesson number (e.g., lesson 29 becomes "29-") +5. Determine the lesson filename: look in `tutorial/lessons/` for the file whose name starts with the zero-padded lesson number (e.g., lesson 30 becomes "30-") 6. Read that lesson file 7. Present its contents to the user. Mention that they've switched to the Design track. If the lesson has an Exercise section, make it visually distinct so the user knows exactly what to do next. diff --git a/.github/agents/next.agent.md b/.github/agents/next.agent.md index f64d1bb..faf8b7b 100644 --- a/.github/agents/next.agent.md +++ b/.github/agents/next.agent.md @@ -20,7 +20,7 @@ Advance the tutorial to the next lesson. Do the following steps in order: - **STOP here.** Do not increment `currentLesson` or present the next lesson. 5. Increment `currentLesson` by 1 -6. Read the `track` field from state (default `"core"` if missing). If `track` is `"qa"`, cap `currentLesson` at 28. If `track` is `"design"`, cap at 34. If `track` is `"core"`, cap at 23. (Set it back to the cap value if it exceeds it.) +6. Read the `track` field from state (default `"core"` if missing). If `track` is `"qa"`, cap `currentLesson` at 29. If `track` is `"design"`, cap at 35. If `track` is `"core"`, cap at 24. (Set it back to the cap value if it exceeds it.) 7. Write the updated JSON back to `tutorial/state.json` 8. Determine the next lesson filename: look in `tutorial/lessons/` for the file whose name starts with the zero-padded lesson number (e.g., lesson 3 becomes "03-", lesson 7 becomes "07-") 9. Read that lesson file diff --git a/.github/agents/previous.agent.md b/.github/agents/previous.agent.md index b625bf4..6c27419 100644 --- a/.github/agents/previous.agent.md +++ b/.github/agents/previous.agent.md @@ -9,7 +9,7 @@ Go back to the previous lesson. Do the following steps in order: 2. Get the current lesson number from the `currentLesson` field 3. Remove the current lesson number from the `completedLessons` array if it is present 4. Decrement `currentLesson` by 1 -5. Read the `track` field from state (default `"core"` if missing). If `track` is `"qa"`, cap `currentLesson` at a minimum of 24. If `track` is `"design"`, cap at a minimum of 29. If `track` is `"core"`, cap at a minimum of 0. (Set it back to the cap value if it goes below it.) +5. Read the `track` field from state (default `"core"` if missing). If `track` is `"qa"`, cap `currentLesson` at a minimum of 25. If `track` is `"design"`, cap at a minimum of 30. If `track` is `"core"`, cap at a minimum of 0. (Set it back to the cap value if it goes below it.) 6. Write the updated JSON back to `tutorial/state.json` 7. Determine the previous lesson filename: look in `tutorial/lessons/` for the file whose name starts with the zero-padded lesson number (e.g., lesson 3 becomes "03-", lesson 7 becomes "07-") 8. Read that lesson file diff --git a/.github/agents/progress.agent.md b/.github/agents/progress.agent.md index a2ffc9e..c6360d8 100644 --- a/.github/agents/progress.agent.md +++ b/.github/agents/progress.agent.md @@ -63,4 +63,4 @@ Show the user their tutorial progress. Do the following: - `[>]` if it is the `currentLesson` (the one they're on right now) - `[~]` if it is in the `skippedLessons` array - `[ ]` if it hasn't been reached yet -6. Show a summary line. If on the core track: "Progress: N of 24 lessons completed". If on the QA track: "Progress: N of 5 lessons completed". If on the design track: "Progress: N of 6 lessons completed". +6. Show a summary line. If on the core track: "Progress: N of 25 lessons completed". If on the QA track: "Progress: N of 5 lessons completed". If on the design track: "Progress: N of 6 lessons completed". diff --git a/.github/agents/qa.agent.md b/.github/agents/qa.agent.md index e850b7e..6c440b0 100644 --- a/.github/agents/qa.agent.md +++ b/.github/agents/qa.agent.md @@ -3,12 +3,12 @@ description: "Switch to the QA track" tools: ["read", "edit"] --- -Switch to the QA track (lessons 24-28). Do the following steps in order: +Switch to the QA track (lessons 25-29). Do the following steps in order: 1. Read the file `tutorial/state.json` 2. Set the `track` field to `"qa"` -3. If `currentLesson` is less than 24, set `currentLesson` to 24 (otherwise keep it as-is) +3. If `currentLesson` is less than 25, set `currentLesson` to 25 (otherwise keep it as-is) 4. Write the updated JSON back to `tutorial/state.json` -5. Determine the lesson filename: look in `tutorial/lessons/` for the file whose name starts with the zero-padded lesson number (e.g., lesson 24 becomes "24-") +5. Determine the lesson filename: look in `tutorial/lessons/` for the file whose name starts with the zero-padded lesson number (e.g., lesson 25 becomes "25-") 6. Read that lesson file 7. Present its contents to the user. Mention that they've switched to the QA track. If the lesson has an Exercise section, make it visually distinct so the user knows exactly what to do next. diff --git a/.github/agents/skip.agent.md b/.github/agents/skip.agent.md index 57b12b4..f87882d 100644 --- a/.github/agents/skip.agent.md +++ b/.github/agents/skip.agent.md @@ -18,7 +18,7 @@ Skip the current exercise and move to the next lesson. Do the following steps: - Then say: "Take a break here — wait until the instructor moves on to the next module. When you're ready to continue, just say **next**." - **STOP here.** Do not increment `currentLesson` or present the next lesson. -4. Increment `currentLesson` by 1. Read the `track` field (default `"core"` if missing). Cap at 34 if `track` is `"design"`, at 28 if `track` is `"qa"`, or at 23 if `track` is `"core"`. +4. Increment `currentLesson` by 1. Read the `track` field (default `"core"` if missing). Cap at 35 if `track` is `"design"`, at 29 if `track` is `"qa"`, or at 24 if `track` is `"core"`. 5. Write the updated state back to `tutorial/state.json` 6. Read the next lesson file from `tutorial/lessons/` (zero-padded prefix) 7. Present it to the user. Briefly acknowledge the skip in a neutral way — something like "Moving on!" — without making the user feel bad. Everyone learns at their own pace. diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 8a732e7..fd970ff 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -18,8 +18,8 @@ You are running as an interactive tutorial guide for GitHub Copilot CLI. Your jo | `hint` | Get a hint for the current exercise | | `skip` | Skip the current exercise | | `progress` | See tutorial progress | -| `qa` | Switch to the QA track (lessons 24-28) | -| `design` | Switch to the Design track (lessons 29-34) | +| `qa` | Switch to the QA track (lessons 25-29) | +| `design` | Switch to the Design track (lessons 30-35) | | `core` | Switch back to the core track | When suggesting navigation to the user, tell them to type `/agent` and select from the list, or say "next" / "next lesson" / "give me a hint" etc. — you should infer which agent to use from natural language. @@ -82,34 +82,34 @@ When suggesting navigation to the user, tell them to type `/agent` and select fr The tutorial includes optional specialist tracks for QA engineers and designers. It is controlled by the `track` field in `tutorial/state.json`: -- `"core"` (default) — Lessons 0-23, the standard developer-focused track -- `"qa"` — Lessons 24-28, focused on QA workflows -- `"design"` — Lessons 29-34, focused on design workflows +- `"core"` (default) — Lessons 0-24, the standard developer-focused track +- `"qa"` — Lessons 25-29, focused on QA workflows +- `"design"` — Lessons 30-35, focused on design workflows ### Track Navigation | Say this | Or type this | What it does | |----------|-------------|-------------| -| "switch to QA track" | `/agent qa` | Switch to the QA track (starts at lesson 24) | -| "switch to Design track" | `/agent design` | Switch to the Design track (starts at lesson 29) | +| "switch to QA track" | `/agent qa` | Switch to the QA track (starts at lesson 25) | +| "switch to Design track" | `/agent design` | Switch to the Design track (starts at lesson 30) | | "switch to core track" | `/agent core` | Switch back to the core track | ### QA Lessons -- **24: Test Planning from Requirements** — Analyse a feature request and create a structured test plan -- **25: Test Case Design & Coverage** — Identify coverage gaps and design test cases using QA techniques -- **26: Automated Test Generation** — Turn test cases into running Vitest tests -- **27: E2E Testing with Playwright** — Write Playwright tests for the web UI -- **28: QA Track Graduation** — Recap and bonus challenges +- **25: Test Planning from Requirements** — Analyse a feature request and create a structured test plan +- **26: Test Case Design & Coverage** — Identify coverage gaps and design test cases using QA techniques +- **27: Automated Test Generation** — Turn test cases into running Vitest tests +- **28: E2E Testing with Playwright** — Write Playwright tests for the web UI +- **29: QA Track Graduation** — Recap and bonus challenges ### Design Lessons -- **29: Translating Mockups to Specs** — Use Copilot to turn a visual design into a structured implementation spec -- **30: Accessibility & Design Review** — Audit a UI against WCAG 2.1 AA and create a reusable `a11y` agent -- **31: Design System Instructions** — Encode design system conventions in `copilot-instructions.md` so Copilot enforces them automatically -- **32: Designer ↔ Developer Handoff** — Use the "interview me" pattern to produce a developer-ready handoff spec -- **33: Rapid Prototyping** — Generate a working, interactive HTML prototype from the handoff spec in the same session -- **34: Design Track Graduation** — Recap and bonus challenges +- **30: Translating Mockups to Specs** — Use Copilot to turn a visual design into a structured implementation spec +- **31: Accessibility & Design Review** — Audit a UI against WCAG 2.1 AA and create a reusable `a11y` agent +- **32: Design System Instructions** — Encode design system conventions in `copilot-instructions.md` so Copilot enforces them automatically +- **33: Designer ↔ Developer Handoff** — Use the "interview me" pattern to produce a developer-ready handoff spec +- **34: Rapid Prototyping** — Generate a working, interactive HTML prototype from the handoff spec in the same session +- **35: Design Track Graduation** — Recap and bonus challenges The `next`, `skip`, and `progress` agents are track-aware — they respect the active track's lesson range. The hooks work with any lesson number automatically. diff --git a/.playwright-cli/page-2026-06-08T12-12-09-434Z.yml b/.playwright-cli/page-2026-06-08T12-12-09-434Z.yml new file mode 100644 index 0000000..c376948 --- /dev/null +++ b/.playwright-cli/page-2026-06-08T12-12-09-434Z.yml @@ -0,0 +1,192 @@ +- generic [active] [ref=e1]: + - region "We value your privacy" [ref=e2]: + - generic [ref=e4]: + - heading "We value your privacy" [level=2] [ref=e5] + - generic [ref=e6]: + - paragraph [ref=e8]: We use cookies to enhance your browsing experience, serve personalised ads or content, and analyse our traffic. By clicking "Accept All", you consent to our use of cookies. + - generic [ref=e9]: + - button "Customise" [ref=e10] [cursor=pointer] + - button "Reject All" [ref=e11] [cursor=pointer] + - button "Accept All" [ref=e12] [cursor=pointer] + - banner: + - navigation [ref=e13]: + - generic [ref=e14]: + - link [ref=e15] [cursor=pointer]: + - /url: https://instil.co/ + - img [ref=e16] + - list [ref=e19]: + - listitem [ref=e20]: + - link "What we do" [ref=e21] [cursor=pointer]: + - /url: https://instil.co/what-we-do + - listitem [ref=e22]: + - link "CASE STUDIES" [ref=e23] [cursor=pointer]: + - /url: https://instil.co/case-studies + - listitem [ref=e24]: + - link "INSIGHTS" [ref=e25] [cursor=pointer]: + - /url: https://instil.co/blog + - listitem [ref=e26]: + - link "TALK TO US" [ref=e27] [cursor=pointer]: + - /url: https://instil.co/contact + - main [ref=e28]: + - generic [ref=e29]: + - generic [ref=e38]: + - heading "Agentic Engineering" [level=1] [ref=e39]: + - generic [ref=e40]: Agentic + - generic [ref=e41]: Engineering + - paragraph [ref=e43]: AI has redefined how we build software. + - link "Talk to us" [ref=e44] [cursor=pointer]: + - /url: /contact + - generic [ref=e48]: + - img "Atlassian" [ref=e51] + - img "Blue light Card inline" [ref=e54] + - img "Bose inline" [ref=e57] + - img "Amex" [ref=e60] + - img "Deloitte" [ref=e63] + - img "Bmw inline" [ref=e66] + - img "Workday inline" [ref=e69] + - img "Mcafee inline" [ref=e72] + - generic [ref=e75]: + - heading "WHAT WE DO" [level=5] [ref=e76] + - generic [ref=e77]: + - paragraph [ref=e78]: Combining engineering expertise with agentic workflows, we move faster across the full lifecycle - from early product definition through to production, modernisation and scale. + - paragraph [ref=e79]: + - text: Working across + - link "agentic product engineering" [ref=e80] [cursor=pointer]: + - /url: https://instil.co/what-we-do/agentic-product-engineering + - text: "," + - link "cyber security" [ref=e81] [cursor=pointer]: + - /url: https://instil.co/what-we-do/cyber-security + - text: and + - link "AI transformation" [ref=e82] [cursor=pointer]: + - /url: https://instil.co/what-we-do/ai-data + - text: ", we help teams build new products and modernise legacy platforms with AI-driven delivery." + - generic [ref=e85]: + - link "Agentic Product Engineering We combine product engineering expertise with an agentic workforce to build better, ship faster and solve harder problems." [ref=e87] [cursor=pointer]: + - /url: https://instil.co/what-we-do/agentic-product-engineering + - generic [ref=e88]: + - generic [ref=e91]: + - heading "Agentic Product Engineering" [level=4] [ref=e92] + - paragraph [ref=e93]: We combine product engineering expertise with an agentic workforce to build better, ship faster and solve harder problems. + - img [ref=e95] + - link "AI Transformation We get your data, people and products ready to move faster, build smarter and solve more complex problems with AI." [ref=e98] [cursor=pointer]: + - /url: https://instil.co/what-we-do/ai-data + - generic [ref=e99]: + - generic [ref=e102]: + - heading "AI Transformation" [level=4] [ref=e103] + - paragraph [ref=e104]: We get your data, people and products ready to move faster, build smarter and solve more complex problems with AI. + - img [ref=e106] + - link "Cyber Security We help organisations embed cyber security into their culture, keeping it human-centred, practical and proactive." [ref=e109] [cursor=pointer]: + - /url: https://instil.co/what-we-do/cyber-security + - generic [ref=e110]: + - generic [ref=e113]: + - heading "Cyber Security" [level=4] [ref=e114] + - paragraph [ref=e115]: We help organisations embed cyber security into their culture, keeping it human-centred, practical and proactive. + - img [ref=e117] + - generic [ref=e120]: + - generic [ref=e121]: + - 'img "From months to weeks: how agentic engineering transformed software delivery for a US market leader" [ref=e122]' + - generic [ref=e123]: + - 'heading "From months to weeks: how agentic engineering transformed software delivery for a US market leader" [level=2] [ref=e124]' + - link "Read More" [ref=e125] [cursor=pointer]: + - /url: https://instil.co/case-studies/from-months-to-weeks-how-agentic-engineering-transformed-software-delivery-for-a-us-market-leader + - blockquote [ref=e127]: + - paragraph [ref=e128]: "\"Instil really came through with this training for our organization. After soft adoption and disjointed attempts by engineers to get onboarded and productive, Instil was able to effectively get engineers at all levels familiar with the effective use of Claude Code. The hands-on approach was widely recognized by our engineers as especially effective and engaging.\"" + - text: Engineering Director + - generic [ref=e130]: + - heading "Featured content" [level=3] [ref=e131] + - generic [ref=e132]: + - link "Cra v7 cyber security Cyber Resilience Audit - the ultimate test for Critical National Infrastructure" [ref=e134] [cursor=pointer]: + - /url: https://instil.co/blog/cyber-resilience-audit-critical-national-infrastructure + - generic [ref=e135]: + - img "Cra v7" [ref=e137] + - heading "cyber security" [level=5] [ref=e138] + - heading "Cyber Resilience Audit - the ultimate test for Critical National Infrastructure" [level=4] [ref=e139] + - link "Wheres the experience1 product You’ve done security - now do experience" [ref=e141] [cursor=pointer]: + - /url: https://instil.co/blog/youve-done-security-now-do-experience + - generic [ref=e142]: + - img "Wheres the experience1" [ref=e144] + - heading "product" [level=5] [ref=e145] + - heading "You’ve done security - now do experience" [level=4] [ref=e146] + - link "Racetolean AI The race is on. Agentic engineering is about to get lean." [ref=e148] [cursor=pointer]: + - /url: https://instil.co/blog/the-race-is-on-ai-agentic-engineering-is-about-to-get-lean + - generic [ref=e149]: + - img "Racetolean" [ref=e151] + - heading "AI" [level=5] [ref=e152] + - heading "The race is on. Agentic engineering is about to get lean." [level=4] [ref=e153] + - contentinfo [ref=e154]: + - generic [ref=e158]: + - img + - heading "Interested in working with us?" [level=3] [ref=e159] + - link "Talk To Us" [ref=e160] [cursor=pointer]: + - /url: https://instil.co/enquiry + - generic [ref=e161]: + - generic [ref=e162]: + - generic [ref=e163]: + - heading "SERVICES" [level=5] [ref=e164] + - list [ref=e165]: + - listitem [ref=e166]: + - link "Product Development" [ref=e167] [cursor=pointer]: + - /url: https://instil.co/what-we-do/product-development + - listitem [ref=e168]: + - link "Cyber Security" [ref=e169] [cursor=pointer]: + - /url: https://instil.co/what-we-do/cyber-security + - listitem [ref=e170]: + - link "AI & Data" [ref=e171] [cursor=pointer]: + - /url: https://instil.co/what-we-do/ai-data + - listitem [ref=e172]: + - link "Training" [ref=e173] [cursor=pointer]: + - /url: https://instil.co/what-we-do/training + - generic [ref=e174]: + - heading "COMPANY" [level=5] [ref=e175] + - list [ref=e176]: + - listitem [ref=e177]: + - link "About" [ref=e178] [cursor=pointer]: + - /url: https://instil.co/about + - listitem [ref=e179]: + - link "Work" [ref=e180] [cursor=pointer]: + - /url: https://instil.co/case-studies + - listitem [ref=e181]: + - link "Contact" [ref=e182] [cursor=pointer]: + - /url: https://instil.co/enquiry + - listitem [ref=e183]: + - link "Customer Portal" [ref=e184] [cursor=pointer]: + - /url: https://customers.instil.co/ + - generic [ref=e185]: + - heading "LATEST" [level=5] [ref=e186] + - list [ref=e187]: + - listitem [ref=e188]: + - link "Insights" [ref=e189] [cursor=pointer]: + - /url: https://instil.co/blog + - listitem [ref=e190]: + - link "News" [ref=e191] [cursor=pointer]: + - /url: https://instil.co/news + - listitem [ref=e192]: + - link "Events" [ref=e193] [cursor=pointer]: + - /url: https://instil.co/events + - generic [ref=e194]: + - heading "CAREERS" [level=5] [ref=e195] + - list [ref=e196]: + - listitem [ref=e197]: + - link "Careers" [ref=e198] [cursor=pointer]: + - /url: https://careers.instil.co + - listitem [ref=e199]: + - link "Culture" [ref=e200] [cursor=pointer]: + - /url: https://instil.co/life + - separator [ref=e201] + - generic [ref=e202]: + - paragraph [ref=e204]: © 2026 Instil Software + - generic [ref=e205]: + - link "Privacy Policy" [ref=e206] [cursor=pointer]: + - /url: https://instil.co/privacy-policy + - link "Modern Slavery Act" [ref=e207] [cursor=pointer]: + - /url: https://instil.co/modern-slavery-act + - generic [ref=e208]: + - link "LinkedIn" [ref=e209] [cursor=pointer]: + - /url: https://www.linkedin.com/company/304661 + - img [ref=e210] + - link "Instagram" [ref=e212] [cursor=pointer]: + - /url: https://www.instagram.com/InstilHQ + - img [ref=e213] + - link "YouTube" [ref=e215] [cursor=pointer]: + - /url: https://www.youtube.com/user/instilsoftware + - img [ref=e216] \ No newline at end of file diff --git a/.playwright-cli/page-2026-06-08T12-13-09-706Z.yml b/.playwright-cli/page-2026-06-08T12-13-09-706Z.yml new file mode 100644 index 0000000..ce4c9c4 --- /dev/null +++ b/.playwright-cli/page-2026-06-08T12-13-09-706Z.yml @@ -0,0 +1,230 @@ +- generic [active] [ref=e1]: + - region "We value your privacy" [ref=e2]: + - generic [ref=e4]: + - heading "We value your privacy" [level=2] [ref=e5] + - generic [ref=e6]: + - paragraph [ref=e8]: We use cookies to enhance your browsing experience, serve personalised ads or content, and analyse our traffic. By clicking "Accept All", you consent to our use of cookies. + - generic [ref=e9]: + - button "Customise" [ref=e10] [cursor=pointer] + - button "Reject All" [ref=e11] [cursor=pointer] + - button "Accept All" [ref=e12] [cursor=pointer] + - banner: + - navigation [ref=e13]: + - generic [ref=e14]: + - link [ref=e15] [cursor=pointer]: + - /url: https://instil.co/ + - img [ref=e16] + - list [ref=e19]: + - listitem [ref=e20]: + - link "What we do" [ref=e21] [cursor=pointer]: + - /url: https://instil.co/what-we-do + - listitem [ref=e22]: + - link "CASE STUDIES" [ref=e23] [cursor=pointer]: + - /url: https://instil.co/case-studies + - listitem [ref=e24]: + - link "INSIGHTS" [ref=e25] [cursor=pointer]: + - /url: https://instil.co/blog + - listitem [ref=e26]: + - link "TALK TO US" [ref=e27] [cursor=pointer]: + - /url: https://instil.co/contact + - main [ref=e28]: + - generic [ref=e32]: + - heading "Insights" [level=1] [ref=e33] + - heading "Articles, tutorials, and the occasional opinion piece from the team" [level=3] [ref=e34] + - generic [ref=e38]: + - link "All" [ref=e39] [cursor=pointer]: + - /url: /blog + - link "insight" [ref=e40] [cursor=pointer]: + - /url: "?category=insight" + - link "AI" [ref=e41] [cursor=pointer]: + - /url: "?category=ai" + - link "cyber security" [ref=e42] [cursor=pointer]: + - /url: "?category=cyber-security" + - link "product" [ref=e43] [cursor=pointer]: + - /url: "?category=product" + - link "learning" [ref=e44] [cursor=pointer]: + - /url: "?category=learning" + - link "engineering" [ref=e45] [cursor=pointer]: + - /url: "?category=engineering" + - link "cloud" [ref=e46] [cursor=pointer]: + - /url: "?category=cloud" + - link "Racetolean The race is on. Agentic engineering is about to get lean. Unlimited AI usage is ending. As costs rise, agentic AI systems will need to route tasks across models instead of relying on a single frontier model. Read More >" [ref=e49] [cursor=pointer]: + - /url: https://instil.co/blog/the-race-is-on-ai-agentic-engineering-is-about-to-get-lean + - generic [ref=e51]: + - img "Racetolean" [ref=e53] + - generic [ref=e55]: + - heading "The race is on. Agentic engineering is about to get lean." [level=3] [ref=e56] + - paragraph [ref=e57]: Unlimited AI usage is ending. As costs rise, agentic AI systems will need to route tasks across models instead of relying on a single frontier model. + - paragraph [ref=e58]: Read More > + - generic [ref=e60]: + - generic [ref=e61]: + - link "Craftsman v3 insight Want real AI transformation? Stop tinkering at the edges. Fix the system" [ref=e63] [cursor=pointer]: + - /url: https://instil.co/blog/want-to-ai-transform-its-time-to-stop-tinkering-at-the-edges-and-fix-the-system + - generic [ref=e64]: + - img "Craftsman v3" [ref=e66] + - heading "insight" [level=5] [ref=e67] + - heading "Want real AI transformation? Stop tinkering at the edges. Fix the system" [level=4] [ref=e68] + - link "Urban Night Signboard AI The AI pricing honeymoon is over. How to tame your next token bill." [ref=e70] [cursor=pointer]: + - /url: https://instil.co/blog/taming-the-token-bill + - generic [ref=e71]: + - img "Urban Night Signboard" [ref=e73] + - heading "AI" [level=5] [ref=e74] + - heading "The AI pricing honeymoon is over. How to tame your next token bill." [level=4] [ref=e75] + - link "Glad you called that v2 AI Copilot has entered the usage-based era. What can engineers do to reduce costs?" [ref=e77] [cursor=pointer]: + - /url: https://instil.co/blog/github-copilot-enters-the-usage-based-era + - generic [ref=e78]: + - img "Glad you called that v2" [ref=e80] + - heading "AI" [level=5] [ref=e81] + - heading "Copilot has entered the usage-based era. What can engineers do to reduce costs?" [level=4] [ref=e82] + - link "B2 TF FINAL v4 AI Back to the future - preparing the ground for agentic engineering" [ref=e84] [cursor=pointer]: + - /url: https://instil.co/blog/back-to-the-future-preparing-the-ground-for-agentic-engineering + - generic [ref=e85]: + - img "B2 TF FINAL v4" [ref=e87] + - heading "AI" [level=5] [ref=e88] + - heading "Back to the future - preparing the ground for agentic engineering" [level=4] [ref=e89] + - link "Blurred lines AI Blurred lines - better outcomes, not just faster delivery" [ref=e91] [cursor=pointer]: + - /url: https://instil.co/blog/blurred-lines-better-outcomes-not-just-faster-delivery + - generic [ref=e92]: + - img "Blurred lines" [ref=e94] + - heading "AI" [level=5] [ref=e95] + - heading "Blurred lines - better outcomes, not just faster delivery" [level=4] [ref=e96] + - 'link "AI Post AI AI in 2025: The terminal becomes cool again" [ref=e98] [cursor=pointer]': + - /url: https://instil.co/blog/ai-predictions-2026 + - generic [ref=e99]: + - img "AI Post" [ref=e101] + - heading "AI" [level=5] [ref=e102] + - 'heading "AI in 2025: The terminal becomes cool again" [level=4] [ref=e103]' + - link "Rag flows product Enhancing user experience at the frontier of AI" [ref=e105] [cursor=pointer]: + - /url: https://instil.co/blog/enhancing-user-experience-at-the-frontier-of-ai + - generic [ref=e106]: + - img "Rag flows" [ref=e108] + - heading "product" [level=5] [ref=e109] + - heading "Enhancing user experience at the frontier of AI" [level=4] [ref=e110] + - link "A buyers guide insight A buyer's guide to selecting the right software delivery partner" [ref=e112] [cursor=pointer]: + - /url: https://instil.co/blog/a-buyers-guide-to-selecting-the-right-software-delivery-partner-2 + - generic [ref=e113]: + - img "A buyers guide" [ref=e115] + - heading "insight" [level=5] [ref=e116] + - heading "A buyer's guide to selecting the right software delivery partner" [level=4] [ref=e117] + - link "Stragetic leadership AI The future of strategic decision-making" [ref=e119] [cursor=pointer]: + - /url: https://instil.co/blog/the-future-of-strategic-decision-making + - generic [ref=e120]: + - img "Stragetic leadership" [ref=e122] + - heading "AI" [level=5] [ref=e123] + - heading "The future of strategic decision-making" [level=4] [ref=e124] + - link "People are your weakest link cyber security They say people are your weakest link... We think not" [ref=e126] [cursor=pointer]: + - /url: https://instil.co/blog/they-say-people-are-your-weakest-link-we-think-not + - generic [ref=e127]: + - img "People are your weakest link" [ref=e129] + - heading "cyber security" [level=5] [ref=e130] + - heading "They say people are your weakest link... We think not" [level=4] [ref=e131] + - link "Instil atlassian two v2 insight Atlassian DC has reached end-of-life, is it time to get off Jira?" [ref=e133] [cursor=pointer]: + - /url: https://instil.co/blog/atlassian-dc-has-reached-end-of-life-is-it-time-to-get-off-jira + - generic [ref=e134]: + - img "Instil atlassian two v2" [ref=e136] + - heading "insight" [level=5] [ref=e137] + - heading "Atlassian DC has reached end-of-life, is it time to get off Jira?" [level=4] [ref=e138] + - link "Wheres the experience1 product You’ve done security - now do experience" [ref=e140] [cursor=pointer]: + - /url: https://instil.co/blog/youve-done-security-now-do-experience + - generic [ref=e141]: + - img "Wheres the experience1" [ref=e143] + - heading "product" [level=5] [ref=e144] + - heading "You’ve done security - now do experience" [level=4] [ref=e145] + - link "Atlassian p1 insight Atlassian is retiring Data Center – what does it mean and what are your options?" [ref=e147] [cursor=pointer]: + - /url: https://instil.co/blog/atlassian-is-retiring-data-center-what-does-it-mean-and-what-are-your + - generic [ref=e148]: + - img "Atlassian p1" [ref=e150] + - heading "insight" [level=5] [ref=e151] + - heading "Atlassian is retiring Data Center – what does it mean and what are your options?" [level=4] [ref=e152] + - link "GPT5v2 AI Prompting has evolved - GPT-5 proves it" [ref=e154] [cursor=pointer]: + - /url: https://instil.co/blog/prompting-has-evolved-and-gpt-5-proves-it + - generic [ref=e155]: + - img "GPT5v2" [ref=e157] + - heading "AI" [level=5] [ref=e158] + - heading "Prompting has evolved - GPT-5 proves it" [level=4] [ref=e159] + - link "Cra v7 cyber security Cyber Resilience Audit - the ultimate test for Critical National Infrastructure" [ref=e161] [cursor=pointer]: + - /url: https://instil.co/blog/cyber-resilience-audit-critical-national-infrastructure + - generic [ref=e162]: + - img "Cra v7" [ref=e164] + - heading "cyber security" [level=5] [ref=e165] + - heading "Cyber Resilience Audit - the ultimate test for Critical National Infrastructure" [level=4] [ref=e166] + - navigation "Blog pagination" [ref=e167]: + - link "NEXT →" [ref=e169] [cursor=pointer]: + - /url: https://instil.co/blog/p2 + - contentinfo [ref=e170]: + - generic [ref=e174]: + - img + - heading "Interested in working with us?" [level=3] [ref=e175] + - link "Talk To Us" [ref=e176] [cursor=pointer]: + - /url: https://instil.co/enquiry + - generic [ref=e177]: + - generic [ref=e178]: + - generic [ref=e179]: + - heading "SERVICES" [level=5] [ref=e180] + - list [ref=e181]: + - listitem [ref=e182]: + - link "Product Development" [ref=e183] [cursor=pointer]: + - /url: https://instil.co/what-we-do/product-development + - listitem [ref=e184]: + - link "Cyber Security" [ref=e185] [cursor=pointer]: + - /url: https://instil.co/what-we-do/cyber-security + - listitem [ref=e186]: + - link "AI & Data" [ref=e187] [cursor=pointer]: + - /url: https://instil.co/what-we-do/ai-data + - listitem [ref=e188]: + - link "Training" [ref=e189] [cursor=pointer]: + - /url: https://instil.co/what-we-do/training + - generic [ref=e190]: + - heading "COMPANY" [level=5] [ref=e191] + - list [ref=e192]: + - listitem [ref=e193]: + - link "About" [ref=e194] [cursor=pointer]: + - /url: https://instil.co/about + - listitem [ref=e195]: + - link "Work" [ref=e196] [cursor=pointer]: + - /url: https://instil.co/case-studies + - listitem [ref=e197]: + - link "Contact" [ref=e198] [cursor=pointer]: + - /url: https://instil.co/enquiry + - listitem [ref=e199]: + - link "Customer Portal" [ref=e200] [cursor=pointer]: + - /url: https://customers.instil.co/ + - generic [ref=e201]: + - heading "LATEST" [level=5] [ref=e202] + - list [ref=e203]: + - listitem [ref=e204]: + - link "Insights" [ref=e205] [cursor=pointer]: + - /url: https://instil.co/blog + - listitem [ref=e206]: + - link "News" [ref=e207] [cursor=pointer]: + - /url: https://instil.co/news + - listitem [ref=e208]: + - link "Events" [ref=e209] [cursor=pointer]: + - /url: https://instil.co/events + - generic [ref=e210]: + - heading "CAREERS" [level=5] [ref=e211] + - list [ref=e212]: + - listitem [ref=e213]: + - link "Careers" [ref=e214] [cursor=pointer]: + - /url: https://careers.instil.co + - listitem [ref=e215]: + - link "Culture" [ref=e216] [cursor=pointer]: + - /url: https://instil.co/life + - separator [ref=e217] + - generic [ref=e218]: + - paragraph [ref=e220]: © 2026 Instil Software + - generic [ref=e221]: + - link "Privacy Policy" [ref=e222] [cursor=pointer]: + - /url: https://instil.co/privacy-policy + - link "Modern Slavery Act" [ref=e223] [cursor=pointer]: + - /url: https://instil.co/modern-slavery-act + - generic [ref=e224]: + - link "LinkedIn" [ref=e225] [cursor=pointer]: + - /url: https://www.linkedin.com/company/304661 + - img [ref=e226] + - link "Instagram" [ref=e228] [cursor=pointer]: + - /url: https://www.instagram.com/InstilHQ + - img [ref=e229] + - link "YouTube" [ref=e231] [cursor=pointer]: + - /url: https://www.youtube.com/user/instilsoftware + - img [ref=e232] \ No newline at end of file diff --git a/README.md b/README.md index 11f0364..35c381f 100644 --- a/README.md +++ b/README.md @@ -31,8 +31,8 @@ You can navigate the tutorial using natural language or the `/agent` command: | "hint" or "give me a hint" | `/agent hint` | Get a hint for the current exercise | | "skip" or "skip this" | `/agent skip` | Skip the current exercise | | "progress" or "show progress" | `/agent progress` | See your tutorial progress | -| "switch to QA track" | `/agent qa` | Switch to the QA track (lessons 24-28) | -| "switch to Design track" | `/agent design` | Switch to the Design track (lessons 29-34) | +| "switch to QA track" | `/agent qa` | Switch to the QA track (lessons 25-29) | +| "switch to Design track" | `/agent design` | Switch to the Design track (lessons 30-35) | | "switch to core track" | `/agent core` | Switch back to the core track | ## Specialist Tracks @@ -41,13 +41,13 @@ The tutorial includes two optional specialist tracks. ### QA Track -The QA track (lessons 24-28) is for QA engineers. It covers test planning, test case design, automated test generation, and E2E testing with Playwright. +The QA track (lessons 25-29) is for QA engineers. It covers test planning, test case design, automated test generation, and E2E testing with Playwright. To start the QA track, say "switch to QA track" or type `/agent qa`. ### Design Track -The Design track (lessons 29-34) is for designers and frontend developers. It covers translating mockups, accessibility reviews, design system instructions, designer-developer handoff, and rapid prototyping. +The Design track (lessons 30-35) is for designers and frontend developers. It covers translating mockups, accessibility reviews, design system instructions, designer-developer handoff, and rapid prototyping. To start the Design track, say "switch to Design track" or type `/agent design`. diff --git a/exercises/sample-app/src/exporters/csv.ts b/exercises/sample-app/src/exporters/csv.ts new file mode 100644 index 0000000..a010249 --- /dev/null +++ b/exercises/sample-app/src/exporters/csv.ts @@ -0,0 +1,23 @@ +import type { Task } from '../tasks.js'; + +const HEADER = 'id,title,completed'; + +export function toCSV(tasks: Task[]): string { + const rows = tasks.map(task => + [ + escapeCSVField(task.id), + escapeCSVField(task.title), + escapeCSVField(String(task.completed)), + ].join(','), + ); + + return [HEADER, ...rows].join('\n'); +} + +function escapeCSVField(value: string): string { + if (!/[",\n\r]/.test(value)) { + return value; + } + + return `"${value.replace(/"/g, '""')}"`; +} diff --git a/exercises/sample-app/src/exporters/json.ts b/exercises/sample-app/src/exporters/json.ts new file mode 100644 index 0000000..7cad470 --- /dev/null +++ b/exercises/sample-app/src/exporters/json.ts @@ -0,0 +1,5 @@ +import type { Task } from '../tasks.js'; + +export function toJSON(tasks: Task[]): string { + return JSON.stringify(tasks, null, 2); +} diff --git a/exercises/sample-app/src/exporters/markdown.ts b/exercises/sample-app/src/exporters/markdown.ts new file mode 100644 index 0000000..f1a0434 --- /dev/null +++ b/exercises/sample-app/src/exporters/markdown.ts @@ -0,0 +1,7 @@ +import type { Task } from '../tasks.js'; + +export function toMarkdown(tasks: Task[]): string { + return tasks + .map(task => `- [${task.completed ? 'x' : ' '}] ${task.title}`) + .join('\n'); +} diff --git a/exercises/sample-app/tests/csv.test.ts b/exercises/sample-app/tests/csv.test.ts new file mode 100644 index 0000000..5ee0994 --- /dev/null +++ b/exercises/sample-app/tests/csv.test.ts @@ -0,0 +1,58 @@ +import { describe, expect, it } from 'vitest'; +import { toCSV } from '../src/exporters/csv.js'; +import type { Task } from '../src/tasks.js'; + +describe('toCSV', () => { + it('exports multiple tasks with a header row', () => { + const tasks: Task[] = [ + { + id: '1', + title: 'Buy groceries', + priority: 'medium', + completed: false, + createdAt: new Date('2026-01-01T00:00:00.000Z'), + }, + { + id: '2', + title: 'Ship feature', + priority: 'high', + completed: true, + createdAt: new Date('2026-01-02T00:00:00.000Z'), + }, + ]; + + expect(toCSV(tasks)).toBe(['id,title,completed', '1,Buy groceries,false', '2,Ship feature,true'].join('\n')); + }); + + it('returns only the header row for an empty array', () => { + expect(toCSV([])).toBe('id,title,completed'); + }); + + it('escapes commas in titles', () => { + const tasks: Task[] = [ + { + id: '1', + title: 'Review, test, and ship', + priority: 'medium', + completed: false, + createdAt: new Date('2026-01-01T00:00:00.000Z'), + }, + ]; + + expect(toCSV(tasks)).toBe(['id,title,completed', '1,"Review, test, and ship",false'].join('\n')); + }); + + it('escapes quotes in titles', () => { + const tasks: Task[] = [ + { + id: '1', + title: 'Say "hello"', + priority: 'low', + completed: true, + createdAt: new Date('2026-01-01T00:00:00.000Z'), + }, + ]; + + expect(toCSV(tasks)).toBe(['id,title,completed', '1,"Say ""hello""",true'].join('\n')); + }); +}); diff --git a/exercises/sample-app/tests/json.test.ts b/exercises/sample-app/tests/json.test.ts new file mode 100644 index 0000000..68812f0 --- /dev/null +++ b/exercises/sample-app/tests/json.test.ts @@ -0,0 +1,56 @@ +import { describe, expect, it } from 'vitest'; + +import type { Task } from '../src/tasks.js'; +import { toJSON } from '../src/exporters/json.js'; + +describe('toJSON', () => { + it('exports multiple tasks as pretty-printed JSON', () => { + const tasks: Task[] = [ + { + id: 'task-1', + title: 'Write docs', + priority: 'low', + completed: false, + createdAt: new Date('2026-01-02T03:04:05.000Z'), + }, + { + id: 'task-2', + title: 'Ship feature', + priority: 'high', + completed: true, + createdAt: new Date('2026-02-03T04:05:06.000Z'), + }, + ]; + + expect(toJSON(tasks)).toBe(JSON.stringify(tasks, null, 2)); + }); + + it('returns formatted empty array JSON for no tasks', () => { + expect(toJSON([])).toBe('[]'); + }); + + it('escapes special characters and produces parseable JSON', () => { + const tasks: Task[] = [ + { + id: 'task-"quotes"\nnewline', + title: 'Handle unicode ✓ and emoji 🚀 with "quotes"\nand tabs\ttoo', + priority: 'medium', + completed: false, + createdAt: new Date('2026-03-04T05:06:07.000Z'), + }, + ]; + + const exported = toJSON(tasks); + const parsed = JSON.parse(exported); + + expect(parsed).toEqual([ + { + id: 'task-"quotes"\nnewline', + title: 'Handle unicode ✓ and emoji 🚀 with "quotes"\nand tabs\ttoo', + priority: 'medium', + completed: false, + createdAt: '2026-03-04T05:06:07.000Z', + }, + ]); + }); +}); diff --git a/exercises/sample-app/tests/markdown.test.ts b/exercises/sample-app/tests/markdown.test.ts new file mode 100644 index 0000000..84b3c13 --- /dev/null +++ b/exercises/sample-app/tests/markdown.test.ts @@ -0,0 +1,45 @@ +import { describe, expect, it } from 'vitest'; + +import type { Task } from '../src/tasks.js'; +import { toMarkdown } from '../src/exporters/markdown.js'; + +describe('toMarkdown', () => { + it('formats multiple tasks as a markdown checklist', () => { + const tasks: Task[] = [ + { + id: '1', + title: 'Write docs', + priority: 'medium', + completed: true, + createdAt: new Date('2026-01-01T00:00:00.000Z'), + }, + { + id: '2', + title: 'Ship feature', + priority: 'high', + completed: false, + createdAt: new Date('2026-01-02T00:00:00.000Z'), + }, + ]; + + expect(toMarkdown(tasks)).toBe('- [x] Write docs\n- [ ] Ship feature'); + }); + + it('returns an empty string for an empty task list', () => { + expect(toMarkdown([])).toBe(''); + }); + + it('preserves special markdown characters in task titles', () => { + const tasks: Task[] = [ + { + id: '3', + title: 'Review *important* [notes] (v2) #123', + priority: 'low', + completed: false, + createdAt: new Date('2026-01-03T00:00:00.000Z'), + }, + ]; + + expect(toMarkdown(tasks)).toBe('- [ ] Review *important* [notes] (v2) #123'); + }); +}); diff --git a/tutorial/lessons/23-remote-control.md b/tutorial/lessons/23-remote-control.md new file mode 100644 index 0000000..9f2399e --- /dev/null +++ b/tutorial/lessons/23-remote-control.md @@ -0,0 +1,88 @@ +# Lesson 23: Remote Control + +## What You'll Learn + +How to monitor and control a running Copilot CLI session from GitHub.com or GitHub Mobile. Perfect for long-running tasks, checking progress away from your desk, or finishing the tutorial on your phone. + +## Concepts + +--- + +### What is remote control? + +Remote control lets you access a running CLI session from anywhere — GitHub.com in your browser, or the GitHub Mobile app on your phone. You can: + +- Monitor the session in real-time as Copilot works +- Respond to permission requests remotely +- Provide input or answer questions +- Review changes and approve tool calls + +This is valuable when you kick off a complex task and need to step away. Instead of waiting at your desk, you can check in from anywhere and keep the work moving. + +--- + +### Starting a remote session + +**From an interactive session**, enable remote control with: + +``` +/remote enable +``` + +This generates a link to view the session on GitHub.com. The link appears in your terminal — click it or copy it to your browser or GitHub Mobile app. + +**When starting a new session**, use the `--remote` flag: + +``` +copilot --remote +``` + +The CLI displays the remote access link immediately. Open it on any device. + +### Viewing from GitHub.com or Mobile + +Once you open the remote link: + +- **On GitHub.com**: You see the conversation in real-time. Copilot's responses appear as they're generated. Permission requests show up as interactive prompts you can approve or deny. + +- **On GitHub Mobile**: The mobile app provides the same interface, optimized for touch. You can read, respond, and approve from your phone. + +The remote view is **live** — it updates as the session progresses. If Copilot asks a question or requests permission, you'll see it immediately. + +--- + +### Use cases + +Remote control shines in these scenarios: + +- **Long-running tasks**: Start a large refactoring or test generation task, enable remote control, and step away. Check in periodically from your phone. +- **Collaborative debugging**: Share the remote link with a teammate so they can follow along or provide input. +- **Mobile check-ins**: Quickly approve a permission request or answer a question from GitHub Mobile while you're away from your desk. +- **Cross-device workflows**: Start a session on your workstation, continue it on your laptop, finish it on your phone. + +--- + +## Exercise + +**Finish this tutorial from GitHub.com or GitHub Mobile.** + +This is an optional capstone experience — you can complete this lesson from your terminal as usual, or try the remote control feature and finish the tutorial from a different device. + +**If you want to try remote control:** + +1. Enable remote control for this session: + ``` + /remote enable + ``` + +2. Open github another device: + - **GitHub.com in a browser** + - **GitHub Mobile app** on your phone + +3. Navigate to the agents tab and find your remote session. + +4. Prompt to finish the tutorial from there + +**If you prefer to stay in the terminal:** + +That's completely fine — remote control is an optional advanced feature. Just say **"next"** when you're ready to continue. \ No newline at end of file diff --git a/tutorial/lessons/23-graduation.md b/tutorial/lessons/24-graduation.md similarity index 99% rename from tutorial/lessons/23-graduation.md rename to tutorial/lessons/24-graduation.md index 8b24003..06099c6 100644 --- a/tutorial/lessons/23-graduation.md +++ b/tutorial/lessons/24-graduation.md @@ -1,4 +1,4 @@ -# Lesson 23: Graduation +# Lesson 24: Graduation ## You Did It diff --git a/tutorial/lessons/24-test-planning.md b/tutorial/lessons/25-test-planning.md similarity index 98% rename from tutorial/lessons/24-test-planning.md rename to tutorial/lessons/25-test-planning.md index be30e87..19a7be6 100644 --- a/tutorial/lessons/24-test-planning.md +++ b/tutorial/lessons/25-test-planning.md @@ -1,4 +1,4 @@ -# Lesson 24: Test Planning from Requirements +# Lesson 25: Test Planning from Requirements ## What You'll Learn diff --git a/tutorial/lessons/25-test-case-design.md b/tutorial/lessons/26-test-case-design.md similarity index 98% rename from tutorial/lessons/25-test-case-design.md rename to tutorial/lessons/26-test-case-design.md index d113197..5222cbb 100644 --- a/tutorial/lessons/25-test-case-design.md +++ b/tutorial/lessons/26-test-case-design.md @@ -1,4 +1,4 @@ -# Lesson 25: Test Case Design & Coverage +# Lesson 26: Test Case Design & Coverage ## What You'll Learn diff --git a/tutorial/lessons/26-automated-test-generation.md b/tutorial/lessons/27-automated-test-generation.md similarity index 98% rename from tutorial/lessons/26-automated-test-generation.md rename to tutorial/lessons/27-automated-test-generation.md index 77a417d..df1d8ce 100644 --- a/tutorial/lessons/26-automated-test-generation.md +++ b/tutorial/lessons/27-automated-test-generation.md @@ -1,4 +1,4 @@ -# Lesson 26: Automated Test Generation +# Lesson 27: Automated Test Generation ## What You'll Learn diff --git a/tutorial/lessons/27-e2e-testing-playwright.md b/tutorial/lessons/28-e2e-testing-playwright.md similarity index 98% rename from tutorial/lessons/27-e2e-testing-playwright.md rename to tutorial/lessons/28-e2e-testing-playwright.md index bfb296f..ccb3990 100644 --- a/tutorial/lessons/27-e2e-testing-playwright.md +++ b/tutorial/lessons/28-e2e-testing-playwright.md @@ -1,4 +1,4 @@ -# Lesson 27: E2E Testing with Playwright +# Lesson 28: E2E Testing with Playwright ## What You'll Learn diff --git a/tutorial/lessons/28-qa-track-graduation.md b/tutorial/lessons/29-qa-track-graduation.md similarity index 98% rename from tutorial/lessons/28-qa-track-graduation.md rename to tutorial/lessons/29-qa-track-graduation.md index 04502ef..ab1699e 100644 --- a/tutorial/lessons/28-qa-track-graduation.md +++ b/tutorial/lessons/29-qa-track-graduation.md @@ -1,4 +1,4 @@ -# Lesson 28: QA Track Graduation +# Lesson 29: QA Track Graduation ## You Did It diff --git a/tutorial/lessons/29-translating-mockups.md b/tutorial/lessons/30-translating-mockups.md similarity index 98% rename from tutorial/lessons/29-translating-mockups.md rename to tutorial/lessons/30-translating-mockups.md index ad5f962..1a48fd6 100644 --- a/tutorial/lessons/29-translating-mockups.md +++ b/tutorial/lessons/30-translating-mockups.md @@ -1,4 +1,4 @@ -# Lesson 29: Translating Mockups to Specs +# Lesson 30: Translating Mockups to Specs ## What You'll Learn diff --git a/tutorial/lessons/30-accessibility-review.md b/tutorial/lessons/31-accessibility-review.md similarity index 98% rename from tutorial/lessons/30-accessibility-review.md rename to tutorial/lessons/31-accessibility-review.md index 695c102..b6b710e 100644 --- a/tutorial/lessons/30-accessibility-review.md +++ b/tutorial/lessons/31-accessibility-review.md @@ -1,4 +1,4 @@ -# Lesson 30: Accessibility & Design Review +# Lesson 31: Accessibility & Design Review ## What You'll Learn diff --git a/tutorial/lessons/31-design-system-instructions.md b/tutorial/lessons/32-design-system-instructions.md similarity index 98% rename from tutorial/lessons/31-design-system-instructions.md rename to tutorial/lessons/32-design-system-instructions.md index 42bd131..4c8d41c 100644 --- a/tutorial/lessons/31-design-system-instructions.md +++ b/tutorial/lessons/32-design-system-instructions.md @@ -1,4 +1,4 @@ -# Lesson 31: Design System Instructions +# Lesson 32: Design System Instructions ## What You'll Learn diff --git a/tutorial/lessons/32-designer-developer-handoff.md b/tutorial/lessons/33-designer-developer-handoff.md similarity index 98% rename from tutorial/lessons/32-designer-developer-handoff.md rename to tutorial/lessons/33-designer-developer-handoff.md index 3a9db31..a920edc 100644 --- a/tutorial/lessons/32-designer-developer-handoff.md +++ b/tutorial/lessons/33-designer-developer-handoff.md @@ -1,4 +1,4 @@ -# Lesson 32: Designer ↔ Developer Handoff +# Lesson 33: Designer ↔ Developer Handoff ## What You'll Learn diff --git a/tutorial/lessons/33-rapid-prototyping.md b/tutorial/lessons/34-rapid-prototyping.md similarity index 99% rename from tutorial/lessons/33-rapid-prototyping.md rename to tutorial/lessons/34-rapid-prototyping.md index 541e063..b028ba1 100644 --- a/tutorial/lessons/33-rapid-prototyping.md +++ b/tutorial/lessons/34-rapid-prototyping.md @@ -1,4 +1,4 @@ -# Lesson 33: Rapid Prototyping +# Lesson 34: Rapid Prototyping ## What You'll Learn diff --git a/tutorial/lessons/34-design-track-graduation.md b/tutorial/lessons/35-design-track-graduation.md similarity index 99% rename from tutorial/lessons/34-design-track-graduation.md rename to tutorial/lessons/35-design-track-graduation.md index 0f824a6..469ca7a 100644 --- a/tutorial/lessons/34-design-track-graduation.md +++ b/tutorial/lessons/35-design-track-graduation.md @@ -1,4 +1,4 @@ -# Lesson 34: Design Track Graduation +# Lesson 35: Design Track Graduation ## You Did It diff --git a/tutorial/state.json b/tutorial/state.json index 3ca71d3..de96654 100644 --- a/tutorial/state.json +++ b/tutorial/state.json @@ -1,7 +1,7 @@ { - "currentLesson": 0, + "currentLesson": 23, "completedLessons": [], "skippedLessons": [], - "startedAt": "", + "startedAt": "2026-06-09T11:46:21.333Z", "track": "core" } From 20b2703cc299b7db6d2f2eed467b12e34c5832ee Mon Sep 17 00:00:00 2001 From: Caleb Wilson Date: Wed, 10 Jun 2026 09:23:37 +0100 Subject: [PATCH 3/5] small lesson fixes --- tutorial/lessons/23-remote-control.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tutorial/lessons/23-remote-control.md b/tutorial/lessons/23-remote-control.md index 9f2399e..3aeedd9 100644 --- a/tutorial/lessons/23-remote-control.md +++ b/tutorial/lessons/23-remote-control.md @@ -26,7 +26,7 @@ This is valuable when you kick off a complex task and need to step away. Instead **From an interactive session**, enable remote control with: ``` -/remote enable +/remote on ``` This generates a link to view the session on GitHub.com. The link appears in your terminal — click it or copy it to your browser or GitHub Mobile app. @@ -72,7 +72,7 @@ This is an optional capstone experience — you can complete this lesson from yo 1. Enable remote control for this session: ``` - /remote enable + /remote on ``` 2. Open github another device: From 736599e0a3ea8658350c3f58441d8db2dcf3b831 Mon Sep 17 00:00:00 2001 From: Caleb Wilson Date: Mon, 29 Jun 2026 09:48:24 +0100 Subject: [PATCH 4/5] fix: address PR review comments for Remote Control lesson MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Rename 24-graduation.md → 23-graduation.md so graduation stays lesson 23 and remote control sits at 24 - Delete duplicate 23-remote-control.md (used wrong /remote on command) - Update progress.agent.md with correct QA (25-29) and Design (30-35) lesson ranges, add lesson 24 to core track list - Reset tutorial/state.json to default values (was committed with non-zero lesson and startedAt) - Remove .playwright-cli snapshot artifacts (generated files, not tutorial content) - Remove out-of-scope sample-app exporters and tests (csv, json, markdown) Co-Authored-By: Claude Sonnet 4.6 --- .../page-2026-06-08T12-12-09-434Z.yml | 192 --------------- .../page-2026-06-08T12-13-09-706Z.yml | 230 ------------------ exercises/sample-app/src/exporters/csv.ts | 23 -- exercises/sample-app/src/exporters/json.ts | 5 - .../sample-app/src/exporters/markdown.ts | 7 - exercises/sample-app/tests/csv.test.ts | 58 ----- exercises/sample-app/tests/json.test.ts | 56 ----- exercises/sample-app/tests/markdown.test.ts | 45 ---- .../{24-graduation.md => 23-graduation.md} | 0 tutorial/lessons/23-remote-control.md | 88 ------- 10 files changed, 704 deletions(-) delete mode 100644 .playwright-cli/page-2026-06-08T12-12-09-434Z.yml delete mode 100644 .playwright-cli/page-2026-06-08T12-13-09-706Z.yml delete mode 100644 exercises/sample-app/src/exporters/csv.ts delete mode 100644 exercises/sample-app/src/exporters/json.ts delete mode 100644 exercises/sample-app/src/exporters/markdown.ts delete mode 100644 exercises/sample-app/tests/csv.test.ts delete mode 100644 exercises/sample-app/tests/json.test.ts delete mode 100644 exercises/sample-app/tests/markdown.test.ts rename tutorial/lessons/{24-graduation.md => 23-graduation.md} (100%) delete mode 100644 tutorial/lessons/23-remote-control.md diff --git a/.playwright-cli/page-2026-06-08T12-12-09-434Z.yml b/.playwright-cli/page-2026-06-08T12-12-09-434Z.yml deleted file mode 100644 index c376948..0000000 --- a/.playwright-cli/page-2026-06-08T12-12-09-434Z.yml +++ /dev/null @@ -1,192 +0,0 @@ -- generic [active] [ref=e1]: - - region "We value your privacy" [ref=e2]: - - generic [ref=e4]: - - heading "We value your privacy" [level=2] [ref=e5] - - generic [ref=e6]: - - paragraph [ref=e8]: We use cookies to enhance your browsing experience, serve personalised ads or content, and analyse our traffic. By clicking "Accept All", you consent to our use of cookies. - - generic [ref=e9]: - - button "Customise" [ref=e10] [cursor=pointer] - - button "Reject All" [ref=e11] [cursor=pointer] - - button "Accept All" [ref=e12] [cursor=pointer] - - banner: - - navigation [ref=e13]: - - generic [ref=e14]: - - link [ref=e15] [cursor=pointer]: - - /url: https://instil.co/ - - img [ref=e16] - - list [ref=e19]: - - listitem [ref=e20]: - - link "What we do" [ref=e21] [cursor=pointer]: - - /url: https://instil.co/what-we-do - - listitem [ref=e22]: - - link "CASE STUDIES" [ref=e23] [cursor=pointer]: - - /url: https://instil.co/case-studies - - listitem [ref=e24]: - - link "INSIGHTS" [ref=e25] [cursor=pointer]: - - /url: https://instil.co/blog - - listitem [ref=e26]: - - link "TALK TO US" [ref=e27] [cursor=pointer]: - - /url: https://instil.co/contact - - main [ref=e28]: - - generic [ref=e29]: - - generic [ref=e38]: - - heading "Agentic Engineering" [level=1] [ref=e39]: - - generic [ref=e40]: Agentic - - generic [ref=e41]: Engineering - - paragraph [ref=e43]: AI has redefined how we build software. - - link "Talk to us" [ref=e44] [cursor=pointer]: - - /url: /contact - - generic [ref=e48]: - - img "Atlassian" [ref=e51] - - img "Blue light Card inline" [ref=e54] - - img "Bose inline" [ref=e57] - - img "Amex" [ref=e60] - - img "Deloitte" [ref=e63] - - img "Bmw inline" [ref=e66] - - img "Workday inline" [ref=e69] - - img "Mcafee inline" [ref=e72] - - generic [ref=e75]: - - heading "WHAT WE DO" [level=5] [ref=e76] - - generic [ref=e77]: - - paragraph [ref=e78]: Combining engineering expertise with agentic workflows, we move faster across the full lifecycle - from early product definition through to production, modernisation and scale. - - paragraph [ref=e79]: - - text: Working across - - link "agentic product engineering" [ref=e80] [cursor=pointer]: - - /url: https://instil.co/what-we-do/agentic-product-engineering - - text: "," - - link "cyber security" [ref=e81] [cursor=pointer]: - - /url: https://instil.co/what-we-do/cyber-security - - text: and - - link "AI transformation" [ref=e82] [cursor=pointer]: - - /url: https://instil.co/what-we-do/ai-data - - text: ", we help teams build new products and modernise legacy platforms with AI-driven delivery." - - generic [ref=e85]: - - link "Agentic Product Engineering We combine product engineering expertise with an agentic workforce to build better, ship faster and solve harder problems." [ref=e87] [cursor=pointer]: - - /url: https://instil.co/what-we-do/agentic-product-engineering - - generic [ref=e88]: - - generic [ref=e91]: - - heading "Agentic Product Engineering" [level=4] [ref=e92] - - paragraph [ref=e93]: We combine product engineering expertise with an agentic workforce to build better, ship faster and solve harder problems. - - img [ref=e95] - - link "AI Transformation We get your data, people and products ready to move faster, build smarter and solve more complex problems with AI." [ref=e98] [cursor=pointer]: - - /url: https://instil.co/what-we-do/ai-data - - generic [ref=e99]: - - generic [ref=e102]: - - heading "AI Transformation" [level=4] [ref=e103] - - paragraph [ref=e104]: We get your data, people and products ready to move faster, build smarter and solve more complex problems with AI. - - img [ref=e106] - - link "Cyber Security We help organisations embed cyber security into their culture, keeping it human-centred, practical and proactive." [ref=e109] [cursor=pointer]: - - /url: https://instil.co/what-we-do/cyber-security - - generic [ref=e110]: - - generic [ref=e113]: - - heading "Cyber Security" [level=4] [ref=e114] - - paragraph [ref=e115]: We help organisations embed cyber security into their culture, keeping it human-centred, practical and proactive. - - img [ref=e117] - - generic [ref=e120]: - - generic [ref=e121]: - - 'img "From months to weeks: how agentic engineering transformed software delivery for a US market leader" [ref=e122]' - - generic [ref=e123]: - - 'heading "From months to weeks: how agentic engineering transformed software delivery for a US market leader" [level=2] [ref=e124]' - - link "Read More" [ref=e125] [cursor=pointer]: - - /url: https://instil.co/case-studies/from-months-to-weeks-how-agentic-engineering-transformed-software-delivery-for-a-us-market-leader - - blockquote [ref=e127]: - - paragraph [ref=e128]: "\"Instil really came through with this training for our organization. After soft adoption and disjointed attempts by engineers to get onboarded and productive, Instil was able to effectively get engineers at all levels familiar with the effective use of Claude Code. The hands-on approach was widely recognized by our engineers as especially effective and engaging.\"" - - text: Engineering Director - - generic [ref=e130]: - - heading "Featured content" [level=3] [ref=e131] - - generic [ref=e132]: - - link "Cra v7 cyber security Cyber Resilience Audit - the ultimate test for Critical National Infrastructure" [ref=e134] [cursor=pointer]: - - /url: https://instil.co/blog/cyber-resilience-audit-critical-national-infrastructure - - generic [ref=e135]: - - img "Cra v7" [ref=e137] - - heading "cyber security" [level=5] [ref=e138] - - heading "Cyber Resilience Audit - the ultimate test for Critical National Infrastructure" [level=4] [ref=e139] - - link "Wheres the experience1 product You’ve done security - now do experience" [ref=e141] [cursor=pointer]: - - /url: https://instil.co/blog/youve-done-security-now-do-experience - - generic [ref=e142]: - - img "Wheres the experience1" [ref=e144] - - heading "product" [level=5] [ref=e145] - - heading "You’ve done security - now do experience" [level=4] [ref=e146] - - link "Racetolean AI The race is on. Agentic engineering is about to get lean." [ref=e148] [cursor=pointer]: - - /url: https://instil.co/blog/the-race-is-on-ai-agentic-engineering-is-about-to-get-lean - - generic [ref=e149]: - - img "Racetolean" [ref=e151] - - heading "AI" [level=5] [ref=e152] - - heading "The race is on. Agentic engineering is about to get lean." [level=4] [ref=e153] - - contentinfo [ref=e154]: - - generic [ref=e158]: - - img - - heading "Interested in working with us?" [level=3] [ref=e159] - - link "Talk To Us" [ref=e160] [cursor=pointer]: - - /url: https://instil.co/enquiry - - generic [ref=e161]: - - generic [ref=e162]: - - generic [ref=e163]: - - heading "SERVICES" [level=5] [ref=e164] - - list [ref=e165]: - - listitem [ref=e166]: - - link "Product Development" [ref=e167] [cursor=pointer]: - - /url: https://instil.co/what-we-do/product-development - - listitem [ref=e168]: - - link "Cyber Security" [ref=e169] [cursor=pointer]: - - /url: https://instil.co/what-we-do/cyber-security - - listitem [ref=e170]: - - link "AI & Data" [ref=e171] [cursor=pointer]: - - /url: https://instil.co/what-we-do/ai-data - - listitem [ref=e172]: - - link "Training" [ref=e173] [cursor=pointer]: - - /url: https://instil.co/what-we-do/training - - generic [ref=e174]: - - heading "COMPANY" [level=5] [ref=e175] - - list [ref=e176]: - - listitem [ref=e177]: - - link "About" [ref=e178] [cursor=pointer]: - - /url: https://instil.co/about - - listitem [ref=e179]: - - link "Work" [ref=e180] [cursor=pointer]: - - /url: https://instil.co/case-studies - - listitem [ref=e181]: - - link "Contact" [ref=e182] [cursor=pointer]: - - /url: https://instil.co/enquiry - - listitem [ref=e183]: - - link "Customer Portal" [ref=e184] [cursor=pointer]: - - /url: https://customers.instil.co/ - - generic [ref=e185]: - - heading "LATEST" [level=5] [ref=e186] - - list [ref=e187]: - - listitem [ref=e188]: - - link "Insights" [ref=e189] [cursor=pointer]: - - /url: https://instil.co/blog - - listitem [ref=e190]: - - link "News" [ref=e191] [cursor=pointer]: - - /url: https://instil.co/news - - listitem [ref=e192]: - - link "Events" [ref=e193] [cursor=pointer]: - - /url: https://instil.co/events - - generic [ref=e194]: - - heading "CAREERS" [level=5] [ref=e195] - - list [ref=e196]: - - listitem [ref=e197]: - - link "Careers" [ref=e198] [cursor=pointer]: - - /url: https://careers.instil.co - - listitem [ref=e199]: - - link "Culture" [ref=e200] [cursor=pointer]: - - /url: https://instil.co/life - - separator [ref=e201] - - generic [ref=e202]: - - paragraph [ref=e204]: © 2026 Instil Software - - generic [ref=e205]: - - link "Privacy Policy" [ref=e206] [cursor=pointer]: - - /url: https://instil.co/privacy-policy - - link "Modern Slavery Act" [ref=e207] [cursor=pointer]: - - /url: https://instil.co/modern-slavery-act - - generic [ref=e208]: - - link "LinkedIn" [ref=e209] [cursor=pointer]: - - /url: https://www.linkedin.com/company/304661 - - img [ref=e210] - - link "Instagram" [ref=e212] [cursor=pointer]: - - /url: https://www.instagram.com/InstilHQ - - img [ref=e213] - - link "YouTube" [ref=e215] [cursor=pointer]: - - /url: https://www.youtube.com/user/instilsoftware - - img [ref=e216] \ No newline at end of file diff --git a/.playwright-cli/page-2026-06-08T12-13-09-706Z.yml b/.playwright-cli/page-2026-06-08T12-13-09-706Z.yml deleted file mode 100644 index ce4c9c4..0000000 --- a/.playwright-cli/page-2026-06-08T12-13-09-706Z.yml +++ /dev/null @@ -1,230 +0,0 @@ -- generic [active] [ref=e1]: - - region "We value your privacy" [ref=e2]: - - generic [ref=e4]: - - heading "We value your privacy" [level=2] [ref=e5] - - generic [ref=e6]: - - paragraph [ref=e8]: We use cookies to enhance your browsing experience, serve personalised ads or content, and analyse our traffic. By clicking "Accept All", you consent to our use of cookies. - - generic [ref=e9]: - - button "Customise" [ref=e10] [cursor=pointer] - - button "Reject All" [ref=e11] [cursor=pointer] - - button "Accept All" [ref=e12] [cursor=pointer] - - banner: - - navigation [ref=e13]: - - generic [ref=e14]: - - link [ref=e15] [cursor=pointer]: - - /url: https://instil.co/ - - img [ref=e16] - - list [ref=e19]: - - listitem [ref=e20]: - - link "What we do" [ref=e21] [cursor=pointer]: - - /url: https://instil.co/what-we-do - - listitem [ref=e22]: - - link "CASE STUDIES" [ref=e23] [cursor=pointer]: - - /url: https://instil.co/case-studies - - listitem [ref=e24]: - - link "INSIGHTS" [ref=e25] [cursor=pointer]: - - /url: https://instil.co/blog - - listitem [ref=e26]: - - link "TALK TO US" [ref=e27] [cursor=pointer]: - - /url: https://instil.co/contact - - main [ref=e28]: - - generic [ref=e32]: - - heading "Insights" [level=1] [ref=e33] - - heading "Articles, tutorials, and the occasional opinion piece from the team" [level=3] [ref=e34] - - generic [ref=e38]: - - link "All" [ref=e39] [cursor=pointer]: - - /url: /blog - - link "insight" [ref=e40] [cursor=pointer]: - - /url: "?category=insight" - - link "AI" [ref=e41] [cursor=pointer]: - - /url: "?category=ai" - - link "cyber security" [ref=e42] [cursor=pointer]: - - /url: "?category=cyber-security" - - link "product" [ref=e43] [cursor=pointer]: - - /url: "?category=product" - - link "learning" [ref=e44] [cursor=pointer]: - - /url: "?category=learning" - - link "engineering" [ref=e45] [cursor=pointer]: - - /url: "?category=engineering" - - link "cloud" [ref=e46] [cursor=pointer]: - - /url: "?category=cloud" - - link "Racetolean The race is on. Agentic engineering is about to get lean. Unlimited AI usage is ending. As costs rise, agentic AI systems will need to route tasks across models instead of relying on a single frontier model. Read More >" [ref=e49] [cursor=pointer]: - - /url: https://instil.co/blog/the-race-is-on-ai-agentic-engineering-is-about-to-get-lean - - generic [ref=e51]: - - img "Racetolean" [ref=e53] - - generic [ref=e55]: - - heading "The race is on. Agentic engineering is about to get lean." [level=3] [ref=e56] - - paragraph [ref=e57]: Unlimited AI usage is ending. As costs rise, agentic AI systems will need to route tasks across models instead of relying on a single frontier model. - - paragraph [ref=e58]: Read More > - - generic [ref=e60]: - - generic [ref=e61]: - - link "Craftsman v3 insight Want real AI transformation? Stop tinkering at the edges. Fix the system" [ref=e63] [cursor=pointer]: - - /url: https://instil.co/blog/want-to-ai-transform-its-time-to-stop-tinkering-at-the-edges-and-fix-the-system - - generic [ref=e64]: - - img "Craftsman v3" [ref=e66] - - heading "insight" [level=5] [ref=e67] - - heading "Want real AI transformation? Stop tinkering at the edges. Fix the system" [level=4] [ref=e68] - - link "Urban Night Signboard AI The AI pricing honeymoon is over. How to tame your next token bill." [ref=e70] [cursor=pointer]: - - /url: https://instil.co/blog/taming-the-token-bill - - generic [ref=e71]: - - img "Urban Night Signboard" [ref=e73] - - heading "AI" [level=5] [ref=e74] - - heading "The AI pricing honeymoon is over. How to tame your next token bill." [level=4] [ref=e75] - - link "Glad you called that v2 AI Copilot has entered the usage-based era. What can engineers do to reduce costs?" [ref=e77] [cursor=pointer]: - - /url: https://instil.co/blog/github-copilot-enters-the-usage-based-era - - generic [ref=e78]: - - img "Glad you called that v2" [ref=e80] - - heading "AI" [level=5] [ref=e81] - - heading "Copilot has entered the usage-based era. What can engineers do to reduce costs?" [level=4] [ref=e82] - - link "B2 TF FINAL v4 AI Back to the future - preparing the ground for agentic engineering" [ref=e84] [cursor=pointer]: - - /url: https://instil.co/blog/back-to-the-future-preparing-the-ground-for-agentic-engineering - - generic [ref=e85]: - - img "B2 TF FINAL v4" [ref=e87] - - heading "AI" [level=5] [ref=e88] - - heading "Back to the future - preparing the ground for agentic engineering" [level=4] [ref=e89] - - link "Blurred lines AI Blurred lines - better outcomes, not just faster delivery" [ref=e91] [cursor=pointer]: - - /url: https://instil.co/blog/blurred-lines-better-outcomes-not-just-faster-delivery - - generic [ref=e92]: - - img "Blurred lines" [ref=e94] - - heading "AI" [level=5] [ref=e95] - - heading "Blurred lines - better outcomes, not just faster delivery" [level=4] [ref=e96] - - 'link "AI Post AI AI in 2025: The terminal becomes cool again" [ref=e98] [cursor=pointer]': - - /url: https://instil.co/blog/ai-predictions-2026 - - generic [ref=e99]: - - img "AI Post" [ref=e101] - - heading "AI" [level=5] [ref=e102] - - 'heading "AI in 2025: The terminal becomes cool again" [level=4] [ref=e103]' - - link "Rag flows product Enhancing user experience at the frontier of AI" [ref=e105] [cursor=pointer]: - - /url: https://instil.co/blog/enhancing-user-experience-at-the-frontier-of-ai - - generic [ref=e106]: - - img "Rag flows" [ref=e108] - - heading "product" [level=5] [ref=e109] - - heading "Enhancing user experience at the frontier of AI" [level=4] [ref=e110] - - link "A buyers guide insight A buyer's guide to selecting the right software delivery partner" [ref=e112] [cursor=pointer]: - - /url: https://instil.co/blog/a-buyers-guide-to-selecting-the-right-software-delivery-partner-2 - - generic [ref=e113]: - - img "A buyers guide" [ref=e115] - - heading "insight" [level=5] [ref=e116] - - heading "A buyer's guide to selecting the right software delivery partner" [level=4] [ref=e117] - - link "Stragetic leadership AI The future of strategic decision-making" [ref=e119] [cursor=pointer]: - - /url: https://instil.co/blog/the-future-of-strategic-decision-making - - generic [ref=e120]: - - img "Stragetic leadership" [ref=e122] - - heading "AI" [level=5] [ref=e123] - - heading "The future of strategic decision-making" [level=4] [ref=e124] - - link "People are your weakest link cyber security They say people are your weakest link... We think not" [ref=e126] [cursor=pointer]: - - /url: https://instil.co/blog/they-say-people-are-your-weakest-link-we-think-not - - generic [ref=e127]: - - img "People are your weakest link" [ref=e129] - - heading "cyber security" [level=5] [ref=e130] - - heading "They say people are your weakest link... We think not" [level=4] [ref=e131] - - link "Instil atlassian two v2 insight Atlassian DC has reached end-of-life, is it time to get off Jira?" [ref=e133] [cursor=pointer]: - - /url: https://instil.co/blog/atlassian-dc-has-reached-end-of-life-is-it-time-to-get-off-jira - - generic [ref=e134]: - - img "Instil atlassian two v2" [ref=e136] - - heading "insight" [level=5] [ref=e137] - - heading "Atlassian DC has reached end-of-life, is it time to get off Jira?" [level=4] [ref=e138] - - link "Wheres the experience1 product You’ve done security - now do experience" [ref=e140] [cursor=pointer]: - - /url: https://instil.co/blog/youve-done-security-now-do-experience - - generic [ref=e141]: - - img "Wheres the experience1" [ref=e143] - - heading "product" [level=5] [ref=e144] - - heading "You’ve done security - now do experience" [level=4] [ref=e145] - - link "Atlassian p1 insight Atlassian is retiring Data Center – what does it mean and what are your options?" [ref=e147] [cursor=pointer]: - - /url: https://instil.co/blog/atlassian-is-retiring-data-center-what-does-it-mean-and-what-are-your - - generic [ref=e148]: - - img "Atlassian p1" [ref=e150] - - heading "insight" [level=5] [ref=e151] - - heading "Atlassian is retiring Data Center – what does it mean and what are your options?" [level=4] [ref=e152] - - link "GPT5v2 AI Prompting has evolved - GPT-5 proves it" [ref=e154] [cursor=pointer]: - - /url: https://instil.co/blog/prompting-has-evolved-and-gpt-5-proves-it - - generic [ref=e155]: - - img "GPT5v2" [ref=e157] - - heading "AI" [level=5] [ref=e158] - - heading "Prompting has evolved - GPT-5 proves it" [level=4] [ref=e159] - - link "Cra v7 cyber security Cyber Resilience Audit - the ultimate test for Critical National Infrastructure" [ref=e161] [cursor=pointer]: - - /url: https://instil.co/blog/cyber-resilience-audit-critical-national-infrastructure - - generic [ref=e162]: - - img "Cra v7" [ref=e164] - - heading "cyber security" [level=5] [ref=e165] - - heading "Cyber Resilience Audit - the ultimate test for Critical National Infrastructure" [level=4] [ref=e166] - - navigation "Blog pagination" [ref=e167]: - - link "NEXT →" [ref=e169] [cursor=pointer]: - - /url: https://instil.co/blog/p2 - - contentinfo [ref=e170]: - - generic [ref=e174]: - - img - - heading "Interested in working with us?" [level=3] [ref=e175] - - link "Talk To Us" [ref=e176] [cursor=pointer]: - - /url: https://instil.co/enquiry - - generic [ref=e177]: - - generic [ref=e178]: - - generic [ref=e179]: - - heading "SERVICES" [level=5] [ref=e180] - - list [ref=e181]: - - listitem [ref=e182]: - - link "Product Development" [ref=e183] [cursor=pointer]: - - /url: https://instil.co/what-we-do/product-development - - listitem [ref=e184]: - - link "Cyber Security" [ref=e185] [cursor=pointer]: - - /url: https://instil.co/what-we-do/cyber-security - - listitem [ref=e186]: - - link "AI & Data" [ref=e187] [cursor=pointer]: - - /url: https://instil.co/what-we-do/ai-data - - listitem [ref=e188]: - - link "Training" [ref=e189] [cursor=pointer]: - - /url: https://instil.co/what-we-do/training - - generic [ref=e190]: - - heading "COMPANY" [level=5] [ref=e191] - - list [ref=e192]: - - listitem [ref=e193]: - - link "About" [ref=e194] [cursor=pointer]: - - /url: https://instil.co/about - - listitem [ref=e195]: - - link "Work" [ref=e196] [cursor=pointer]: - - /url: https://instil.co/case-studies - - listitem [ref=e197]: - - link "Contact" [ref=e198] [cursor=pointer]: - - /url: https://instil.co/enquiry - - listitem [ref=e199]: - - link "Customer Portal" [ref=e200] [cursor=pointer]: - - /url: https://customers.instil.co/ - - generic [ref=e201]: - - heading "LATEST" [level=5] [ref=e202] - - list [ref=e203]: - - listitem [ref=e204]: - - link "Insights" [ref=e205] [cursor=pointer]: - - /url: https://instil.co/blog - - listitem [ref=e206]: - - link "News" [ref=e207] [cursor=pointer]: - - /url: https://instil.co/news - - listitem [ref=e208]: - - link "Events" [ref=e209] [cursor=pointer]: - - /url: https://instil.co/events - - generic [ref=e210]: - - heading "CAREERS" [level=5] [ref=e211] - - list [ref=e212]: - - listitem [ref=e213]: - - link "Careers" [ref=e214] [cursor=pointer]: - - /url: https://careers.instil.co - - listitem [ref=e215]: - - link "Culture" [ref=e216] [cursor=pointer]: - - /url: https://instil.co/life - - separator [ref=e217] - - generic [ref=e218]: - - paragraph [ref=e220]: © 2026 Instil Software - - generic [ref=e221]: - - link "Privacy Policy" [ref=e222] [cursor=pointer]: - - /url: https://instil.co/privacy-policy - - link "Modern Slavery Act" [ref=e223] [cursor=pointer]: - - /url: https://instil.co/modern-slavery-act - - generic [ref=e224]: - - link "LinkedIn" [ref=e225] [cursor=pointer]: - - /url: https://www.linkedin.com/company/304661 - - img [ref=e226] - - link "Instagram" [ref=e228] [cursor=pointer]: - - /url: https://www.instagram.com/InstilHQ - - img [ref=e229] - - link "YouTube" [ref=e231] [cursor=pointer]: - - /url: https://www.youtube.com/user/instilsoftware - - img [ref=e232] \ No newline at end of file diff --git a/exercises/sample-app/src/exporters/csv.ts b/exercises/sample-app/src/exporters/csv.ts deleted file mode 100644 index a010249..0000000 --- a/exercises/sample-app/src/exporters/csv.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type { Task } from '../tasks.js'; - -const HEADER = 'id,title,completed'; - -export function toCSV(tasks: Task[]): string { - const rows = tasks.map(task => - [ - escapeCSVField(task.id), - escapeCSVField(task.title), - escapeCSVField(String(task.completed)), - ].join(','), - ); - - return [HEADER, ...rows].join('\n'); -} - -function escapeCSVField(value: string): string { - if (!/[",\n\r]/.test(value)) { - return value; - } - - return `"${value.replace(/"/g, '""')}"`; -} diff --git a/exercises/sample-app/src/exporters/json.ts b/exercises/sample-app/src/exporters/json.ts deleted file mode 100644 index 7cad470..0000000 --- a/exercises/sample-app/src/exporters/json.ts +++ /dev/null @@ -1,5 +0,0 @@ -import type { Task } from '../tasks.js'; - -export function toJSON(tasks: Task[]): string { - return JSON.stringify(tasks, null, 2); -} diff --git a/exercises/sample-app/src/exporters/markdown.ts b/exercises/sample-app/src/exporters/markdown.ts deleted file mode 100644 index f1a0434..0000000 --- a/exercises/sample-app/src/exporters/markdown.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { Task } from '../tasks.js'; - -export function toMarkdown(tasks: Task[]): string { - return tasks - .map(task => `- [${task.completed ? 'x' : ' '}] ${task.title}`) - .join('\n'); -} diff --git a/exercises/sample-app/tests/csv.test.ts b/exercises/sample-app/tests/csv.test.ts deleted file mode 100644 index 5ee0994..0000000 --- a/exercises/sample-app/tests/csv.test.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { describe, expect, it } from 'vitest'; -import { toCSV } from '../src/exporters/csv.js'; -import type { Task } from '../src/tasks.js'; - -describe('toCSV', () => { - it('exports multiple tasks with a header row', () => { - const tasks: Task[] = [ - { - id: '1', - title: 'Buy groceries', - priority: 'medium', - completed: false, - createdAt: new Date('2026-01-01T00:00:00.000Z'), - }, - { - id: '2', - title: 'Ship feature', - priority: 'high', - completed: true, - createdAt: new Date('2026-01-02T00:00:00.000Z'), - }, - ]; - - expect(toCSV(tasks)).toBe(['id,title,completed', '1,Buy groceries,false', '2,Ship feature,true'].join('\n')); - }); - - it('returns only the header row for an empty array', () => { - expect(toCSV([])).toBe('id,title,completed'); - }); - - it('escapes commas in titles', () => { - const tasks: Task[] = [ - { - id: '1', - title: 'Review, test, and ship', - priority: 'medium', - completed: false, - createdAt: new Date('2026-01-01T00:00:00.000Z'), - }, - ]; - - expect(toCSV(tasks)).toBe(['id,title,completed', '1,"Review, test, and ship",false'].join('\n')); - }); - - it('escapes quotes in titles', () => { - const tasks: Task[] = [ - { - id: '1', - title: 'Say "hello"', - priority: 'low', - completed: true, - createdAt: new Date('2026-01-01T00:00:00.000Z'), - }, - ]; - - expect(toCSV(tasks)).toBe(['id,title,completed', '1,"Say ""hello""",true'].join('\n')); - }); -}); diff --git a/exercises/sample-app/tests/json.test.ts b/exercises/sample-app/tests/json.test.ts deleted file mode 100644 index 68812f0..0000000 --- a/exercises/sample-app/tests/json.test.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { describe, expect, it } from 'vitest'; - -import type { Task } from '../src/tasks.js'; -import { toJSON } from '../src/exporters/json.js'; - -describe('toJSON', () => { - it('exports multiple tasks as pretty-printed JSON', () => { - const tasks: Task[] = [ - { - id: 'task-1', - title: 'Write docs', - priority: 'low', - completed: false, - createdAt: new Date('2026-01-02T03:04:05.000Z'), - }, - { - id: 'task-2', - title: 'Ship feature', - priority: 'high', - completed: true, - createdAt: new Date('2026-02-03T04:05:06.000Z'), - }, - ]; - - expect(toJSON(tasks)).toBe(JSON.stringify(tasks, null, 2)); - }); - - it('returns formatted empty array JSON for no tasks', () => { - expect(toJSON([])).toBe('[]'); - }); - - it('escapes special characters and produces parseable JSON', () => { - const tasks: Task[] = [ - { - id: 'task-"quotes"\nnewline', - title: 'Handle unicode ✓ and emoji 🚀 with "quotes"\nand tabs\ttoo', - priority: 'medium', - completed: false, - createdAt: new Date('2026-03-04T05:06:07.000Z'), - }, - ]; - - const exported = toJSON(tasks); - const parsed = JSON.parse(exported); - - expect(parsed).toEqual([ - { - id: 'task-"quotes"\nnewline', - title: 'Handle unicode ✓ and emoji 🚀 with "quotes"\nand tabs\ttoo', - priority: 'medium', - completed: false, - createdAt: '2026-03-04T05:06:07.000Z', - }, - ]); - }); -}); diff --git a/exercises/sample-app/tests/markdown.test.ts b/exercises/sample-app/tests/markdown.test.ts deleted file mode 100644 index 84b3c13..0000000 --- a/exercises/sample-app/tests/markdown.test.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { describe, expect, it } from 'vitest'; - -import type { Task } from '../src/tasks.js'; -import { toMarkdown } from '../src/exporters/markdown.js'; - -describe('toMarkdown', () => { - it('formats multiple tasks as a markdown checklist', () => { - const tasks: Task[] = [ - { - id: '1', - title: 'Write docs', - priority: 'medium', - completed: true, - createdAt: new Date('2026-01-01T00:00:00.000Z'), - }, - { - id: '2', - title: 'Ship feature', - priority: 'high', - completed: false, - createdAt: new Date('2026-01-02T00:00:00.000Z'), - }, - ]; - - expect(toMarkdown(tasks)).toBe('- [x] Write docs\n- [ ] Ship feature'); - }); - - it('returns an empty string for an empty task list', () => { - expect(toMarkdown([])).toBe(''); - }); - - it('preserves special markdown characters in task titles', () => { - const tasks: Task[] = [ - { - id: '3', - title: 'Review *important* [notes] (v2) #123', - priority: 'low', - completed: false, - createdAt: new Date('2026-01-03T00:00:00.000Z'), - }, - ]; - - expect(toMarkdown(tasks)).toBe('- [ ] Review *important* [notes] (v2) #123'); - }); -}); diff --git a/tutorial/lessons/24-graduation.md b/tutorial/lessons/23-graduation.md similarity index 100% rename from tutorial/lessons/24-graduation.md rename to tutorial/lessons/23-graduation.md diff --git a/tutorial/lessons/23-remote-control.md b/tutorial/lessons/23-remote-control.md deleted file mode 100644 index 3aeedd9..0000000 --- a/tutorial/lessons/23-remote-control.md +++ /dev/null @@ -1,88 +0,0 @@ -# Lesson 23: Remote Control - -## What You'll Learn - -How to monitor and control a running Copilot CLI session from GitHub.com or GitHub Mobile. Perfect for long-running tasks, checking progress away from your desk, or finishing the tutorial on your phone. - -## Concepts - ---- - -### What is remote control? - -Remote control lets you access a running CLI session from anywhere — GitHub.com in your browser, or the GitHub Mobile app on your phone. You can: - -- Monitor the session in real-time as Copilot works -- Respond to permission requests remotely -- Provide input or answer questions -- Review changes and approve tool calls - -This is valuable when you kick off a complex task and need to step away. Instead of waiting at your desk, you can check in from anywhere and keep the work moving. - ---- - -### Starting a remote session - -**From an interactive session**, enable remote control with: - -``` -/remote on -``` - -This generates a link to view the session on GitHub.com. The link appears in your terminal — click it or copy it to your browser or GitHub Mobile app. - -**When starting a new session**, use the `--remote` flag: - -``` -copilot --remote -``` - -The CLI displays the remote access link immediately. Open it on any device. - -### Viewing from GitHub.com or Mobile - -Once you open the remote link: - -- **On GitHub.com**: You see the conversation in real-time. Copilot's responses appear as they're generated. Permission requests show up as interactive prompts you can approve or deny. - -- **On GitHub Mobile**: The mobile app provides the same interface, optimized for touch. You can read, respond, and approve from your phone. - -The remote view is **live** — it updates as the session progresses. If Copilot asks a question or requests permission, you'll see it immediately. - ---- - -### Use cases - -Remote control shines in these scenarios: - -- **Long-running tasks**: Start a large refactoring or test generation task, enable remote control, and step away. Check in periodically from your phone. -- **Collaborative debugging**: Share the remote link with a teammate so they can follow along or provide input. -- **Mobile check-ins**: Quickly approve a permission request or answer a question from GitHub Mobile while you're away from your desk. -- **Cross-device workflows**: Start a session on your workstation, continue it on your laptop, finish it on your phone. - ---- - -## Exercise - -**Finish this tutorial from GitHub.com or GitHub Mobile.** - -This is an optional capstone experience — you can complete this lesson from your terminal as usual, or try the remote control feature and finish the tutorial from a different device. - -**If you want to try remote control:** - -1. Enable remote control for this session: - ``` - /remote on - ``` - -2. Open github another device: - - **GitHub.com in a browser** - - **GitHub Mobile app** on your phone - -3. Navigate to the agents tab and find your remote session. - -4. Prompt to finish the tutorial from there - -**If you prefer to stay in the terminal:** - -That's completely fine — remote control is an optional advanced feature. Just say **"next"** when you're ready to continue. \ No newline at end of file From 5bebc3968d1cda21b2404e172aed496e2d25f98d Mon Sep 17 00:00:00 2001 From: Caleb Wilson Date: Mon, 29 Jun 2026 09:50:48 +0100 Subject: [PATCH 5/5] fix: update lesson numbering in progress agent and reset state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Correct progress.agent.md QA lesson range (24-28 → 25-29), Design range (29-34 → 30-35), add lesson 24 to core list - Fix 23-graduation.md heading (was still labelled Lesson 24) - Reset tutorial/state.json to default values Co-Authored-By: Claude Sonnet 4.6 --- .github/agents/progress.agent.md | 23 ++++++++++++----------- tutorial/lessons/23-graduation.md | 2 +- tutorial/state.json | 4 ++-- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/.github/agents/progress.agent.md b/.github/agents/progress.agent.md index c6360d8..2b54178 100644 --- a/.github/agents/progress.agent.md +++ b/.github/agents/progress.agent.md @@ -40,21 +40,22 @@ Show the user their tutorial progress. Do the following: - 21: CLI Tools and MCP - 22: Billing and Premium Requests - 23: Graduation + Bonus + - 24: Remote Control (optional capstone) 4. If `track` is `"qa"`, also show a **QA Track:** section: - - 24: Test Planning from Requirements - - 25: Test Case Design & Coverage Analysis - - 26: Automated Test Generation - - 27: E2E Testing with Playwright - - 28: QA Track Graduation + - 25: Test Planning from Requirements + - 26: Test Case Design & Coverage Analysis + - 27: Automated Test Generation + - 28: E2E Testing with Playwright + - 29: QA Track Graduation If `track` is `"design"`, also show a **Design Track:** section: - - 29: Translating Mockups to Specs - - 30: Accessibility & Design Review - - 31: Design System Instructions - - 32: Designer ↔ Developer Handoff - - 33: Rapid Prototyping - - 34: Design Track Graduation + - 30: Translating Mockups to Specs + - 31: Accessibility & Design Review + - 32: Design System Instructions + - 33: Designer ↔ Developer Handoff + - 34: Rapid Prototyping + - 35: Design Track Graduation If `track` is NOT `"qa"` and NOT `"design"`, show a note instead: "Type /agent qa to start the QA track or /agent design to start the Design track" diff --git a/tutorial/lessons/23-graduation.md b/tutorial/lessons/23-graduation.md index 06099c6..8b24003 100644 --- a/tutorial/lessons/23-graduation.md +++ b/tutorial/lessons/23-graduation.md @@ -1,4 +1,4 @@ -# Lesson 24: Graduation +# Lesson 23: Graduation ## You Did It diff --git a/tutorial/state.json b/tutorial/state.json index de96654..3ca71d3 100644 --- a/tutorial/state.json +++ b/tutorial/state.json @@ -1,7 +1,7 @@ { - "currentLesson": 23, + "currentLesson": 0, "completedLessons": [], "skippedLessons": [], - "startedAt": "2026-06-09T11:46:21.333Z", + "startedAt": "", "track": "core" }