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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/skills/next/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Advance the tutorial to the next lesson. Do the following steps in order:
2. Get the current lesson number from the `currentLesson` field
3. Add the current lesson number to the `completedLessons` array if it is not already there
4. Increment `currentLesson` by 1
5. Read the `track` field from state (default `"core"` if missing). If `track` is `"qa"`, cap `currentLesson` at 21. If `track` is `"core"`, cap at 16. (Set it back to the cap value if it exceeds it.)
5. Read the `track` field from state (default `"core"` if missing). If `track` is `"qa"`, cap `currentLesson` at 22. If `track` is `"core"`, cap at 17. (Set it back to the cap value if it exceeds it.)
6. Write the updated JSON back to `tutorial/state.json`
7. 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-")
8. Read that lesson file
Expand Down
19 changes: 10 additions & 9 deletions .claude/skills/progress/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ Show the user their tutorial progress. Do the following:
- 9: Subagents & Parallel Exploration
- 10: Hooks in Action
- 12: Session Management
- 14: CLI Tools & MCP Servers
- 15: Working with Images
- 16: Graduation + Bonus
- 14: Remote Tasks
- 15: CLI Tools & MCP Servers
- 16: Working with Images
- 17: Graduation + Bonus

**Developer:**
- 4: Code Modification
Expand All @@ -32,11 +33,11 @@ Show the user their tutorial progress. Do the following:
- 13: Headless Mode & Automation

4. If `track` is `"qa"`, also show a **QA Track:** section:
- 17: Test Planning from Requirements
- 18: Test Case Design & Coverage Analysis
- 19: Automated Test Generation
- 20: E2E Testing with Playwright
- 21: QA Track Graduation
- 18: Test Planning from Requirements
- 19: Test Case Design & Coverage Analysis
- 20: Automated Test Generation
- 21: E2E Testing with Playwright
- 22: QA Track Graduation

If `track` is NOT `"qa"`, show a note instead: "Type /qa to start the QA track"

Expand All @@ -45,4 +46,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 16 lessons completed". If on the QA track: "Progress: N of 21 lessons completed".
6. Show a summary line. If on the core track: "Progress: N of 17 lessons completed". If on the QA track: "Progress: N of 22 lessons completed".
6 changes: 3 additions & 3 deletions .claude/skills/qa/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ description: Switch to the QA track
disable-model-invocation: true
---

Switch to the QA track (lessons 17–21). Do the following steps in order:
Switch to the QA track (lessons 18–22). 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 17, set `currentLesson` to 17 (otherwise keep it as-is)
3. If `currentLesson` is less than 18, set `currentLesson` to 18 (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 17 becomes "17-")
5. Determine the lesson filename: look in `tutorial/lessons/` for the file whose name starts with the zero-padded lesson number (e.g., lesson 18 becomes "18-")
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.
2 changes: 1 addition & 1 deletion .claude/skills/skip/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Skip the current exercise and move to the next lesson. Do the following steps:

1. Read `tutorial/state.json`
2. Add the current lesson number to the `skippedLessons` array if not already there
3. Increment `currentLesson` by 1. Read the `track` field (default `"core"` if missing). Cap at 21 if `track` is `"qa"`, or at 16 if `track` is `"core"`.
3. Increment `currentLesson` by 1. Read the `track` field (default `"core"` if missing). Cap at 22 if `track` is `"qa"`, or at 17 if `track` is `"core"`.
4. Write the updated state back to `tutorial/state.json`
5. Read the next lesson file from `tutorial/lessons/` (zero-padded prefix)
6. 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.
18 changes: 9 additions & 9 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,26 @@ You are running as an interactive tutorial guide for Claude Code. Your job is to

7. **Exit tutorial mode only if the user explicitly says so** — e.g., "exit tutorial" or "I'm done with the tutorial". Then behave as a normal Claude Code instance.

## QA Track (Lessons 17–21)
## QA Track (Lessons 18–22)

The tutorial includes an optional QA track for QA engineers. It is controlled by the `track` field in `tutorial/state.json`:

- `"core"` (default) — Lessons 0–16, the standard developer-focused track
- `"qa"` — Lessons 17–21, focused on QA workflows
- `"core"` (default) — Lessons 0–17, the standard developer-focused track
- `"qa"` — Lessons 18–22, focused on QA workflows

### QA Track Skills

| Command | What it does |
|---------|-------------|
| `/qa` | Switch to the QA track (starts at lesson 17) |
| `/qa` | Switch to the QA track (starts at lesson 18) |
| `/core` | Switch back to the core track |

### QA Lessons

- **17: Test Planning from Requirements** — Analyze a feature request and create a structured test plan
- **18: Test Case Design & Coverage** — Identify coverage gaps and design test cases using QA techniques
- **19: Automated Test Generation** — Turn test cases into running Vitest tests
- **20: E2E Testing with Playwright** — Write Playwright tests for the web UI
- **21: QA Track Graduation** — Recap and bonus challenges
- **18: Test Planning from Requirements** — Analyze a feature request and create a structured test plan
- **19: Test Case Design & Coverage** — Identify coverage gaps and design test cases using QA techniques
- **20: Automated Test Generation** — Turn test cases into running Vitest tests
- **21: E2E Testing with Playwright** — Write Playwright tests for the web UI
- **22: QA Track Graduation** — Recap and bonus challenges

The `/next`, `/skip`, and `/progress` skills are track-aware — they respect the active track's lesson range. The hooks (`session-start.sh`, `prompt-context.sh`, `validate-exercise.sh`) work with any lesson number automatically.
9 changes: 5 additions & 4 deletions tutorial/lessons/00-welcome.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,10 @@ This tutorial runs entirely inside Claude Code. You interact with me (Claude) na
11. **Git Workflow** — Commits, PRs, and code review with Claude
12. **Session Management** — Resume, rewind, and organize your work
13. **Headless Mode** — Run Claude from scripts and CI/CD
14. **CLI Tools & MCP Servers** — Use any CLI tool and structured tool access
15. **Working with Images** — Debug from screenshots and mockups
16. **Graduation** — Bonus challenges and where to go next
14. **Remote Tasks** — Delegate work to Claude in the cloud
15. **CLI Tools & MCP Servers** — Use any CLI tool and structured tool access
16. **Working with Images** — Debug from screenshots and mockups
17. **Graduation** — Bonus challenges and where to go next

## Coming from IDE Copilots?

Expand All @@ -40,7 +41,7 @@ If you've used GitHub Copilot, Cursor, or other AI coding tools, here's how Clau
- **Runs commands** — Claude executes tests, builds, type-checkers, and git commands, then reads the output and reacts. It's not just suggesting code — it's verifying it works.
- **Persistent memory** — `CLAUDE.md` files store your conventions, architecture decisions, and rules. Claude reads them every session — no re-explaining.
- **Plans before acting** — Planning mode (Shift+Tab) lets Claude explore and design an approach before writing a single line of code. You review and approve the plan first.
- **Works with images** — drag-and-drop screenshots, mockups, or error dialogs directly into the conversation (details in Lesson 15).
- **Works with images** — drag-and-drop screenshots, mockups, or error dialogs directly into the conversation (details in Lesson 16).

None of this replaces what you already know — it builds on it. If you've been using AI for fixing errors, generating boilerplate, or understanding code, Claude Code handles all of those. This tutorial shows you what's different.

Expand Down
17 changes: 17 additions & 0 deletions tutorial/lessons/03-context-and-claude-md.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,23 @@ This keeps you in flow without the hard reset of `/clear`.

If you catch yourself giving Claude the same instruction in multiple sessions — "we use Prisma", "always run tests", "use early returns" — move it to CLAUDE.md. If context is getting long mid-session but you're not done, use `/compact "Focus on [your current task]"` to compress without losing the thread. These two habits together solve most "Claude forgets things" frustrations.

### Resist the manual refactor

When Claude makes a mistake — wrong naming convention, incorrect pattern, missing a project rule — it's tempting to manually fix the code and move on. Resist that instinct.

Instead, tell Claude to update `CLAUDE.md` with the rule it missed. For example:

- "We use camelCase for variables in this project. Add that to CLAUDE.md so you follow it from now on."
- "You put the test file in the wrong directory. Add a note to CLAUDE.md about where tests go."

Why this matters:

- **A manual fix solves it once.** A `CLAUDE.md` update solves it forever — across every future session, for every person on the team.
- **Corrections in chat don't persist.** Even if Claude gets it right after you correct it, that knowledge vanishes with `/clear` or a new session. `CLAUDE.md` survives.
- **It compounds.** Each rule you add makes Claude more aligned with your project. After a few weeks, Claude already knows your conventions before you start typing.

Think of it this way: every time you catch Claude doing something wrong, that's not just a bug to fix — it's a rule to capture. The goal is to make each mistake happen only once.

## Completion Criteria

Use an @file mention to reference a file in the sample app. Then type `/next` to continue.
72 changes: 72 additions & 0 deletions tutorial/lessons/14-remote-tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Lesson 14: Remote Tasks

## What You'll Learn

How to delegate work to Claude in the cloud so you can keep coding — or fire off a task at the end of the day and come back to the results tomorrow. Remote tasks are how you parallelize yourself.

## Concepts

### The difference: local vs cloud

In lesson 13 you saw headless mode (`-p`): Claude runs locally, blocks your terminal, and exits when done. That's great for scripts and CI.

Remote tasks are different. They push work to Claude.ai to run in the cloud. You get a URL back immediately, keep working, and check the results whenever you're ready.

### Sending a remote task

From inside a Claude Code session, use `/task`:

```
/task Refactor the error handling in src/api.ts to use a consistent pattern
```

Claude sends the task to a cloud session and gives you a URL. The work happens in the background — your local session stays free.

### When to use remote tasks

**Good fits:**
- Large refactors that touch many files
- Codebase-wide audits (find all TODO comments, audit error handling, check for security issues)
- Generating tests across a module
- End-of-day tasks — queue up work before you log off and review the results in the morning
- Any task where you'd otherwise sit and watch Claude work for several minutes

**Not ideal for:**
- Quick questions you need answered right now
- Tasks that need back-and-forth conversation
- Work where you want to guide Claude's approach step by step

### The mental model

Think of remote tasks like assigning work to a colleague. You describe what you want, hand it off, and check in later. The key skill is writing a clear enough description that Claude can work independently — the same skill you practised in lesson 2 (effective prompting).

### Comparison

| | Headless (`-p`) | Remote (`/task`) |
|---|---|---|
| Runs | Locally | In the cloud |
| Blocks terminal | Yes | No |
| Best for | Scripts, CI, quick queries | Longer tasks you don't need to babysit |
| Output | stdout | Claude.ai session |

## Exercise

**Think about a task you'd delegate.**

You don't need to run a remote task right now (it requires a Claude.ai account with the feature enabled). Instead, think about this:

1. Pick a task from your real work that takes Claude more than a couple of minutes
2. Write a prompt clear enough that Claude could complete it without any follow-up questions
3. Consider: what context would Claude need? Would you reference specific files? Set constraints?

The goal is to practise the skill of writing self-contained task descriptions — that's the bottleneck for effective delegation, whether to Claude or to a human.

## Hints

1. Good remote task prompts are specific about scope: "Refactor error handling in `src/api/`" is better than "improve error handling"
2. Include constraints: "Don't change the public API" or "Use the existing AppError class"
3. If you find yourself writing a paragraph of context, consider whether CLAUDE.md should hold that context permanently instead

## Completion Criteria

This is a conceptual lesson — there is nothing to validate automatically. Type `/next` when you are ready to continue.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Lesson 14: CLI Tools & MCP Servers
# Lesson 15: CLI Tools & MCP Servers

## What You'll Learn

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Lesson 15: Working with Images
# Lesson 16: Working with Images

## What You'll Learn

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Lesson 16: Graduation
# Lesson 17: Graduation

## You Did It

Expand Down Expand Up @@ -46,7 +46,7 @@ Pick a small feature — for example, "sort tasks by priority". Ask me to implem

### 5. MCP Integration

In Lesson 14 you used `curl` via the CLI to fetch API data. Now try configuring the [GitHub MCP server](https://github.com/modelcontextprotocol/servers/tree/main/src/github) in `.claude/settings.json` and using it to list issues or PRs on a repo. Then do the same thing with the `gh` CLI. Compare the experience — how does structured MCP tool access differ from raw CLI? When would you prefer one over the other?
In Lesson 15 you used `curl` via the CLI to fetch API data. Now try configuring the [GitHub MCP server](https://github.com/modelcontextprotocol/servers/tree/main/src/github) in `.claude/settings.json` and using it to list issues or PRs on a repo. Then do the same thing with the `gh` CLI. Compare the experience — how does structured MCP tool access differ from raw CLI? When would you prefer one over the other?

## Common Frustrations (and What to Do)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Lesson 17: Test Planning from Requirements
# Lesson 18: Test Planning from Requirements

## What You'll Learn

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Lesson 18: Test Case Design & Coverage Analysis
# Lesson 19: Test Case Design & Coverage Analysis

## What You'll Learn

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Lesson 19: Automated Test Generation
# Lesson 20: Automated Test Generation

## What You'll Learn

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Lesson 20: E2E Testing with Playwright
# Lesson 21: E2E Testing with Playwright

## What You'll Learn

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Lesson 21: QA Track Graduation
# Lesson 22: QA Track Graduation

## You Did It

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# Validator for Lesson 14: CLI Tools & MCP Servers
# Validator for Lesson 15: CLI Tools & MCP Servers
# Checks that api-response.json exists with valid JSON content.

set -euo pipefail
Expand All @@ -10,25 +10,25 @@ JSON_FILE="$REPO_ROOT/exercises/sample-app/api-response.json"

# Check 1: api-response.json must exist
if [ ! -f "$JSON_FILE" ]; then
echo "[LESSON 14] INCOMPLETE: exercises/sample-app/api-response.json does not exist"
echo "[LESSON 15] INCOMPLETE: exercises/sample-app/api-response.json does not exist"
exit 1
fi
echo "[LESSON 14] api-response.json found"
echo "[LESSON 15] api-response.json found"

# Check 2: Must contain valid JSON (use Node.js — guaranteed available)
if ! node -e "JSON.parse(require('fs').readFileSync('$JSON_FILE', 'utf8'))" 2>/dev/null; then
echo "[LESSON 14] INCOMPLETE: api-response.json does not contain valid JSON"
echo "[LESSON 15] INCOMPLETE: api-response.json does not contain valid JSON"
exit 1
fi
echo "[LESSON 14] valid JSON"
echo "[LESSON 15] valid JSON"

# Check 3: Must be a non-empty array (length > 0)
ENTRY_COUNT=$(node -e "const d = JSON.parse(require('fs').readFileSync('$JSON_FILE', 'utf8')); if (!Array.isArray(d)) { process.exit(1); } console.log(d.length);" 2>/dev/null || echo "0")
if [ "$ENTRY_COUNT" -ge 1 ]; then
echo "[LESSON 14] JSON contains $ENTRY_COUNT entry/entries"
echo "[LESSON 15] JSON contains $ENTRY_COUNT entry/entries"
else
echo "[LESSON 14] INCOMPLETE: api-response.json appears to be empty"
echo "[LESSON 15] INCOMPLETE: api-response.json appears to be empty"
exit 1
fi

echo "[LESSON 14] PASSED: Lesson 14 exercise complete"
echo "[LESSON 15] PASSED: Lesson 15 exercise complete"
34 changes: 0 additions & 34 deletions tutorial/validators/validate-17.sh

This file was deleted.

Loading