@shaykec/bridge 0.4.25 → 0.4.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/journeys/ai-engineer.yaml +34 -0
- package/journeys/backend-developer.yaml +36 -0
- package/journeys/business-analyst.yaml +37 -0
- package/journeys/devops-engineer.yaml +37 -0
- package/journeys/engineering-manager.yaml +44 -0
- package/journeys/frontend-developer.yaml +41 -0
- package/journeys/fullstack-developer.yaml +49 -0
- package/journeys/mobile-developer.yaml +42 -0
- package/journeys/product-manager.yaml +35 -0
- package/journeys/qa-engineer.yaml +37 -0
- package/journeys/ux-designer.yaml +43 -0
- package/modules/README.md +52 -0
- package/modules/accessibility-fundamentals/content.md +126 -0
- package/modules/accessibility-fundamentals/exercises.md +88 -0
- package/modules/accessibility-fundamentals/module.yaml +43 -0
- package/modules/accessibility-fundamentals/quick-ref.md +71 -0
- package/modules/accessibility-fundamentals/quiz.md +100 -0
- package/modules/accessibility-fundamentals/resources.md +29 -0
- package/modules/accessibility-fundamentals/walkthrough.md +80 -0
- package/modules/adr-writing/content.md +121 -0
- package/modules/adr-writing/exercises.md +81 -0
- package/modules/adr-writing/module.yaml +41 -0
- package/modules/adr-writing/quick-ref.md +57 -0
- package/modules/adr-writing/quiz.md +73 -0
- package/modules/adr-writing/resources.md +29 -0
- package/modules/adr-writing/walkthrough.md +64 -0
- package/modules/ai-agents/content.md +120 -0
- package/modules/ai-agents/exercises.md +82 -0
- package/modules/ai-agents/module.yaml +42 -0
- package/modules/ai-agents/quick-ref.md +60 -0
- package/modules/ai-agents/quiz.md +103 -0
- package/modules/ai-agents/resources.md +30 -0
- package/modules/ai-agents/walkthrough.md +85 -0
- package/modules/ai-assisted-research/content.md +136 -0
- package/modules/ai-assisted-research/exercises.md +80 -0
- package/modules/ai-assisted-research/module.yaml +42 -0
- package/modules/ai-assisted-research/quick-ref.md +67 -0
- package/modules/ai-assisted-research/quiz.md +73 -0
- package/modules/ai-assisted-research/resources.md +33 -0
- package/modules/ai-assisted-research/walkthrough.md +85 -0
- package/modules/ai-pair-programming/content.md +105 -0
- package/modules/ai-pair-programming/exercises.md +98 -0
- package/modules/ai-pair-programming/module.yaml +39 -0
- package/modules/ai-pair-programming/quick-ref.md +58 -0
- package/modules/ai-pair-programming/quiz.md +73 -0
- package/modules/ai-pair-programming/resources.md +34 -0
- package/modules/ai-pair-programming/walkthrough.md +117 -0
- package/modules/ai-test-generation/content.md +125 -0
- package/modules/ai-test-generation/exercises.md +98 -0
- package/modules/ai-test-generation/module.yaml +39 -0
- package/modules/ai-test-generation/quick-ref.md +65 -0
- package/modules/ai-test-generation/quiz.md +74 -0
- package/modules/ai-test-generation/resources.md +41 -0
- package/modules/ai-test-generation/walkthrough.md +100 -0
- package/modules/api-design/content.md +189 -0
- package/modules/api-design/exercises.md +84 -0
- package/modules/api-design/game.yaml +113 -0
- package/modules/api-design/module.yaml +45 -0
- package/modules/api-design/quick-ref.md +73 -0
- package/modules/api-design/quiz.md +100 -0
- package/modules/api-design/resources.md +55 -0
- package/modules/api-design/walkthrough.md +88 -0
- package/modules/clean-code/content.md +136 -0
- package/modules/clean-code/exercises.md +137 -0
- package/modules/clean-code/game.yaml +172 -0
- package/modules/clean-code/module.yaml +44 -0
- package/modules/clean-code/quick-ref.md +44 -0
- package/modules/clean-code/quiz.md +105 -0
- package/modules/clean-code/resources.md +40 -0
- package/modules/clean-code/walkthrough.md +78 -0
- package/modules/clean-code/workshop.yaml +149 -0
- package/modules/code-review/content.md +130 -0
- package/modules/code-review/exercises.md +95 -0
- package/modules/code-review/game.yaml +83 -0
- package/modules/code-review/module.yaml +42 -0
- package/modules/code-review/quick-ref.md +77 -0
- package/modules/code-review/quiz.md +105 -0
- package/modules/code-review/resources.md +40 -0
- package/modules/code-review/walkthrough.md +106 -0
- package/modules/daily-workflow/content.md +81 -0
- package/modules/daily-workflow/exercises.md +50 -0
- package/modules/daily-workflow/module.yaml +33 -0
- package/modules/daily-workflow/quick-ref.md +37 -0
- package/modules/daily-workflow/quiz.md +65 -0
- package/modules/daily-workflow/resources.md +38 -0
- package/modules/daily-workflow/walkthrough.md +83 -0
- package/modules/debugging-systematically/content.md +139 -0
- package/modules/debugging-systematically/exercises.md +91 -0
- package/modules/debugging-systematically/module.yaml +46 -0
- package/modules/debugging-systematically/quick-ref.md +59 -0
- package/modules/debugging-systematically/quiz.md +105 -0
- package/modules/debugging-systematically/resources.md +42 -0
- package/modules/debugging-systematically/walkthrough.md +84 -0
- package/modules/debugging-systematically/workshop.yaml +127 -0
- package/modules/demo-test/content.md +68 -0
- package/modules/demo-test/exercises.md +28 -0
- package/modules/demo-test/game.yaml +171 -0
- package/modules/demo-test/module.yaml +41 -0
- package/modules/demo-test/quick-ref.md +54 -0
- package/modules/demo-test/quiz.md +74 -0
- package/modules/demo-test/resources.md +21 -0
- package/modules/demo-test/walkthrough.md +122 -0
- package/modules/demo-test/workshop.yaml +31 -0
- package/modules/design-critique/content.md +93 -0
- package/modules/design-critique/exercises.md +71 -0
- package/modules/design-critique/module.yaml +41 -0
- package/modules/design-critique/quick-ref.md +63 -0
- package/modules/design-critique/quiz.md +73 -0
- package/modules/design-critique/resources.md +27 -0
- package/modules/design-critique/walkthrough.md +68 -0
- package/modules/design-patterns/content.md +335 -0
- package/modules/design-patterns/exercises.md +82 -0
- package/modules/design-patterns/game.yaml +55 -0
- package/modules/design-patterns/module.yaml +45 -0
- package/modules/design-patterns/quick-ref.md +44 -0
- package/modules/design-patterns/quiz.md +101 -0
- package/modules/design-patterns/resources.md +40 -0
- package/modules/design-patterns/walkthrough.md +64 -0
- package/modules/exploratory-testing/content.md +133 -0
- package/modules/exploratory-testing/exercises.md +88 -0
- package/modules/exploratory-testing/module.yaml +41 -0
- package/modules/exploratory-testing/quick-ref.md +68 -0
- package/modules/exploratory-testing/quiz.md +75 -0
- package/modules/exploratory-testing/resources.md +39 -0
- package/modules/exploratory-testing/walkthrough.md +87 -0
- package/modules/git/content.md +128 -0
- package/modules/git/exercises.md +53 -0
- package/modules/git/game.yaml +190 -0
- package/modules/git/module.yaml +44 -0
- package/modules/git/quick-ref.md +67 -0
- package/modules/git/quiz.md +89 -0
- package/modules/git/resources.md +49 -0
- package/modules/git/walkthrough.md +92 -0
- package/modules/git/workshop.yaml +145 -0
- package/modules/hiring-interviews/content.md +130 -0
- package/modules/hiring-interviews/exercises.md +88 -0
- package/modules/hiring-interviews/module.yaml +41 -0
- package/modules/hiring-interviews/quick-ref.md +68 -0
- package/modules/hiring-interviews/quiz.md +73 -0
- package/modules/hiring-interviews/resources.md +36 -0
- package/modules/hiring-interviews/walkthrough.md +75 -0
- package/modules/hooks/content.md +97 -0
- package/modules/hooks/exercises.md +69 -0
- package/modules/hooks/module.yaml +39 -0
- package/modules/hooks/quick-ref.md +93 -0
- package/modules/hooks/quiz.md +81 -0
- package/modules/hooks/resources.md +34 -0
- package/modules/hooks/walkthrough.md +105 -0
- package/modules/hooks/workshop.yaml +64 -0
- package/modules/incident-response/content.md +124 -0
- package/modules/incident-response/exercises.md +82 -0
- package/modules/incident-response/game.yaml +132 -0
- package/modules/incident-response/module.yaml +45 -0
- package/modules/incident-response/quick-ref.md +53 -0
- package/modules/incident-response/quiz.md +103 -0
- package/modules/incident-response/resources.md +40 -0
- package/modules/incident-response/walkthrough.md +82 -0
- package/modules/llm-fundamentals/content.md +114 -0
- package/modules/llm-fundamentals/exercises.md +83 -0
- package/modules/llm-fundamentals/module.yaml +42 -0
- package/modules/llm-fundamentals/quick-ref.md +64 -0
- package/modules/llm-fundamentals/quiz.md +103 -0
- package/modules/llm-fundamentals/resources.md +30 -0
- package/modules/llm-fundamentals/walkthrough.md +91 -0
- package/modules/one-on-ones/content.md +133 -0
- package/modules/one-on-ones/exercises.md +81 -0
- package/modules/one-on-ones/module.yaml +44 -0
- package/modules/one-on-ones/quick-ref.md +67 -0
- package/modules/one-on-ones/quiz.md +73 -0
- package/modules/one-on-ones/resources.md +37 -0
- package/modules/one-on-ones/walkthrough.md +69 -0
- package/modules/package.json +9 -0
- package/modules/prioritization-frameworks/content.md +130 -0
- package/modules/prioritization-frameworks/exercises.md +93 -0
- package/modules/prioritization-frameworks/module.yaml +41 -0
- package/modules/prioritization-frameworks/quick-ref.md +77 -0
- package/modules/prioritization-frameworks/quiz.md +73 -0
- package/modules/prioritization-frameworks/resources.md +32 -0
- package/modules/prioritization-frameworks/walkthrough.md +69 -0
- package/modules/prompt-engineering/content.md +123 -0
- package/modules/prompt-engineering/exercises.md +82 -0
- package/modules/prompt-engineering/game.yaml +101 -0
- package/modules/prompt-engineering/module.yaml +45 -0
- package/modules/prompt-engineering/quick-ref.md +65 -0
- package/modules/prompt-engineering/quiz.md +105 -0
- package/modules/prompt-engineering/resources.md +36 -0
- package/modules/prompt-engineering/walkthrough.md +81 -0
- package/modules/rag-fundamentals/content.md +111 -0
- package/modules/rag-fundamentals/exercises.md +80 -0
- package/modules/rag-fundamentals/module.yaml +45 -0
- package/modules/rag-fundamentals/quick-ref.md +58 -0
- package/modules/rag-fundamentals/quiz.md +75 -0
- package/modules/rag-fundamentals/resources.md +34 -0
- package/modules/rag-fundamentals/walkthrough.md +75 -0
- package/modules/react-fundamentals/content.md +140 -0
- package/modules/react-fundamentals/exercises.md +81 -0
- package/modules/react-fundamentals/game.yaml +145 -0
- package/modules/react-fundamentals/module.yaml +45 -0
- package/modules/react-fundamentals/quick-ref.md +62 -0
- package/modules/react-fundamentals/quiz.md +106 -0
- package/modules/react-fundamentals/resources.md +42 -0
- package/modules/react-fundamentals/walkthrough.md +89 -0
- package/modules/react-fundamentals/workshop.yaml +112 -0
- package/modules/react-native-fundamentals/content.md +141 -0
- package/modules/react-native-fundamentals/exercises.md +79 -0
- package/modules/react-native-fundamentals/module.yaml +42 -0
- package/modules/react-native-fundamentals/quick-ref.md +60 -0
- package/modules/react-native-fundamentals/quiz.md +61 -0
- package/modules/react-native-fundamentals/resources.md +24 -0
- package/modules/react-native-fundamentals/walkthrough.md +84 -0
- package/modules/registry.yaml +1650 -0
- package/modules/risk-management/content.md +162 -0
- package/modules/risk-management/exercises.md +86 -0
- package/modules/risk-management/module.yaml +41 -0
- package/modules/risk-management/quick-ref.md +82 -0
- package/modules/risk-management/quiz.md +73 -0
- package/modules/risk-management/resources.md +40 -0
- package/modules/risk-management/walkthrough.md +67 -0
- package/modules/running-effective-standups/content.md +119 -0
- package/modules/running-effective-standups/exercises.md +79 -0
- package/modules/running-effective-standups/module.yaml +40 -0
- package/modules/running-effective-standups/quick-ref.md +61 -0
- package/modules/running-effective-standups/quiz.md +73 -0
- package/modules/running-effective-standups/resources.md +36 -0
- package/modules/running-effective-standups/walkthrough.md +76 -0
- package/modules/solid-principles/content.md +154 -0
- package/modules/solid-principles/exercises.md +107 -0
- package/modules/solid-principles/module.yaml +42 -0
- package/modules/solid-principles/quick-ref.md +50 -0
- package/modules/solid-principles/quiz.md +102 -0
- package/modules/solid-principles/resources.md +39 -0
- package/modules/solid-principles/walkthrough.md +84 -0
- package/modules/sprint-planning/content.md +142 -0
- package/modules/sprint-planning/exercises.md +79 -0
- package/modules/sprint-planning/game.yaml +84 -0
- package/modules/sprint-planning/module.yaml +44 -0
- package/modules/sprint-planning/quick-ref.md +76 -0
- package/modules/sprint-planning/quiz.md +102 -0
- package/modules/sprint-planning/resources.md +39 -0
- package/modules/sprint-planning/walkthrough.md +75 -0
- package/modules/sql-fundamentals/content.md +160 -0
- package/modules/sql-fundamentals/exercises.md +87 -0
- package/modules/sql-fundamentals/game.yaml +105 -0
- package/modules/sql-fundamentals/module.yaml +45 -0
- package/modules/sql-fundamentals/quick-ref.md +53 -0
- package/modules/sql-fundamentals/quiz.md +103 -0
- package/modules/sql-fundamentals/resources.md +42 -0
- package/modules/sql-fundamentals/walkthrough.md +92 -0
- package/modules/sql-fundamentals/workshop.yaml +109 -0
- package/modules/stakeholder-communication/content.md +186 -0
- package/modules/stakeholder-communication/exercises.md +87 -0
- package/modules/stakeholder-communication/module.yaml +38 -0
- package/modules/stakeholder-communication/quick-ref.md +89 -0
- package/modules/stakeholder-communication/quiz.md +73 -0
- package/modules/stakeholder-communication/resources.md +41 -0
- package/modules/stakeholder-communication/walkthrough.md +74 -0
- package/modules/system-design/content.md +149 -0
- package/modules/system-design/exercises.md +83 -0
- package/modules/system-design/game.yaml +95 -0
- package/modules/system-design/module.yaml +46 -0
- package/modules/system-design/quick-ref.md +59 -0
- package/modules/system-design/quiz.md +102 -0
- package/modules/system-design/resources.md +46 -0
- package/modules/system-design/walkthrough.md +90 -0
- package/modules/team-topologies/content.md +166 -0
- package/modules/team-topologies/exercises.md +85 -0
- package/modules/team-topologies/module.yaml +41 -0
- package/modules/team-topologies/quick-ref.md +61 -0
- package/modules/team-topologies/quiz.md +101 -0
- package/modules/team-topologies/resources.md +37 -0
- package/modules/team-topologies/walkthrough.md +76 -0
- package/modules/technical-debt/content.md +111 -0
- package/modules/technical-debt/exercises.md +92 -0
- package/modules/technical-debt/module.yaml +39 -0
- package/modules/technical-debt/quick-ref.md +60 -0
- package/modules/technical-debt/quiz.md +73 -0
- package/modules/technical-debt/resources.md +25 -0
- package/modules/technical-debt/walkthrough.md +94 -0
- package/modules/technical-mentoring/content.md +128 -0
- package/modules/technical-mentoring/exercises.md +84 -0
- package/modules/technical-mentoring/module.yaml +41 -0
- package/modules/technical-mentoring/quick-ref.md +74 -0
- package/modules/technical-mentoring/quiz.md +73 -0
- package/modules/technical-mentoring/resources.md +33 -0
- package/modules/technical-mentoring/walkthrough.md +65 -0
- package/modules/test-strategy/content.md +136 -0
- package/modules/test-strategy/exercises.md +84 -0
- package/modules/test-strategy/game.yaml +99 -0
- package/modules/test-strategy/module.yaml +45 -0
- package/modules/test-strategy/quick-ref.md +66 -0
- package/modules/test-strategy/quiz.md +99 -0
- package/modules/test-strategy/resources.md +60 -0
- package/modules/test-strategy/walkthrough.md +97 -0
- package/modules/test-strategy/workshop.yaml +96 -0
- package/modules/typescript-fundamentals/content.md +127 -0
- package/modules/typescript-fundamentals/exercises.md +79 -0
- package/modules/typescript-fundamentals/game.yaml +111 -0
- package/modules/typescript-fundamentals/module.yaml +45 -0
- package/modules/typescript-fundamentals/quick-ref.md +55 -0
- package/modules/typescript-fundamentals/quiz.md +104 -0
- package/modules/typescript-fundamentals/resources.md +42 -0
- package/modules/typescript-fundamentals/walkthrough.md +71 -0
- package/modules/typescript-fundamentals/workshop.yaml +146 -0
- package/modules/user-story-mapping/content.md +123 -0
- package/modules/user-story-mapping/exercises.md +87 -0
- package/modules/user-story-mapping/module.yaml +41 -0
- package/modules/user-story-mapping/quick-ref.md +64 -0
- package/modules/user-story-mapping/quiz.md +73 -0
- package/modules/user-story-mapping/resources.md +29 -0
- package/modules/user-story-mapping/walkthrough.md +86 -0
- package/modules/writing-prds/content.md +133 -0
- package/modules/writing-prds/exercises.md +93 -0
- package/modules/writing-prds/game.yaml +83 -0
- package/modules/writing-prds/module.yaml +44 -0
- package/modules/writing-prds/quick-ref.md +77 -0
- package/modules/writing-prds/quiz.md +103 -0
- package/modules/writing-prds/resources.md +30 -0
- package/modules/writing-prds/walkthrough.md +87 -0
- package/package.json +1 -1
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# Technical Hiring — Walkthrough
|
|
2
|
+
|
|
3
|
+
## Before We Begin
|
|
4
|
+
|
|
5
|
+
**Diagnostic Question:** What's the difference between a structured interview and an unstructured one? When might each be appropriate?
|
|
6
|
+
|
|
7
|
+
**Checkpoint:** You recognize that structured interviews use consistent questions and rubrics for fairness; unstructured interviews feel more natural but risk bias. Both have tradeoffs.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Step 1: Create an Interview Scorecard
|
|
12
|
+
|
|
13
|
+
<!-- hint:list style="cards" -->
|
|
14
|
+
|
|
15
|
+
**Task:** For a role you hire for (or a hypothetical mid-level engineer), create a scorecard. List 4–6 competencies. Mark each as must-have or nice-to-have. For each, specify how you'll assess it (behavioral, technical, system design, resume).
|
|
16
|
+
|
|
17
|
+
**Question:** How do you avoid "kitchen sink" scorecards? What makes a competency truly must-have?
|
|
18
|
+
|
|
19
|
+
**Checkpoint:** Scorecard has 4–6 competencies, must/nice classification, and assessment method for each.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Step 2: Write a Behavioral Question + Rubric
|
|
24
|
+
|
|
25
|
+
<!-- hint:buttons type="single" prompt="Which format structures behavioral answers?" options="STAR,SMART,DRY" -->
|
|
26
|
+
|
|
27
|
+
**Task:** Pick one must-have competency from your scorecard. Write a behavioral question in STAR format. Then write a 3-level rubric: strong, medium, weak. What would each look like in practice?
|
|
28
|
+
|
|
29
|
+
**Question:** How do you make the rubric useful without being rigid? What if the candidate's story doesn't fit the expected mold?
|
|
30
|
+
|
|
31
|
+
**Checkpoint:** One question and rubric; usable by another interviewer.
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Step 3: Write a Technical Question + Rubric
|
|
36
|
+
|
|
37
|
+
**Task:** Pick one technical competency. Write a coding or debugging question that's bounded (15–20 min). Write a rubric: what does strong/medium/weak look like? Include: correctness, edge cases, explanation quality.
|
|
38
|
+
|
|
39
|
+
**Question:** How do you balance "can they code?" with "can they think?"? When do hints help vs. hurt?
|
|
40
|
+
|
|
41
|
+
**Checkpoint:** Question is unambiguous; rubric has clear levels.
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Step 4: Design Your Debrief Process
|
|
46
|
+
|
|
47
|
+
**Task:** Document your debrief process: Who attends? Do interviewers score before or during? How do you resolve disagreement? What's the hire/no-hire bar? Keep it to one page.
|
|
48
|
+
|
|
49
|
+
**Question:** What happens when two interviewers strongly disagree? How do you avoid groupthink?
|
|
50
|
+
|
|
51
|
+
**Checkpoint:** Process is documented; disagreement resolution is clear.
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## Step 5: Audit for Bias
|
|
56
|
+
|
|
57
|
+
<!-- hint:card type="warning" title="Bias" -->
|
|
58
|
+
|
|
59
|
+
**Task:** Review your current (or a past) interview process. List 3 potential bias sources (e.g., similarity bias, halo effect, anchoring). For each, propose one mitigation.
|
|
60
|
+
|
|
61
|
+
**Question:** What's the tension between "culture fit" and bias? How do you assess culture add without filtering for similarity?
|
|
62
|
+
|
|
63
|
+
**Checkpoint:** Three bias sources identified; three mitigations proposed.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Step 6: Improve Candidate Experience
|
|
68
|
+
|
|
69
|
+
<!-- hint:celebrate -->
|
|
70
|
+
|
|
71
|
+
**Task:** Map the candidate journey from application to offer/reject. Identify 3 pain points (e.g., long wait, unclear feedback, disorganized onsite). Propose one improvement for each.
|
|
72
|
+
|
|
73
|
+
**Question:** What does a great candidate experience cost? What does a bad one cost?
|
|
74
|
+
|
|
75
|
+
**Checkpoint:** Journey mapped; three improvements proposed.
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# Claude Code Hooks
|
|
2
|
+
|
|
3
|
+
> **Level: 🌿 Intermediate**
|
|
4
|
+
|
|
5
|
+
## What Are Hooks?
|
|
6
|
+
|
|
7
|
+
Hooks are shell commands that Claude Code executes automatically in response to lifecycle events. They let you automate workflows without manual intervention — run linters after edits, track usage statistics, validate changes before commits, and more.
|
|
8
|
+
|
|
9
|
+
## Lifecycle Events
|
|
10
|
+
|
|
11
|
+
| Event | When It Fires | Use Cases |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| **PostToolUse** | After any tool is used (Edit, Write, Bash, etc.) | Linting, formatting, usage tracking |
|
|
14
|
+
| **UserPromptSubmit** | When the user sends a message | Input validation, command detection |
|
|
15
|
+
| **Stop** | When the agent finishes a response | Session tracking, cleanup, notifications |
|
|
16
|
+
|
|
17
|
+
## Configuration
|
|
18
|
+
|
|
19
|
+
Hooks are defined in `hooks.json` (for plugins) or in `.claude/settings.json` (for user hooks):
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"hooks": {
|
|
24
|
+
"PostToolUse": [
|
|
25
|
+
{
|
|
26
|
+
"command": "./scripts/on-tool-use.sh",
|
|
27
|
+
"timeout": 5000
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"UserPromptSubmit": [
|
|
31
|
+
{
|
|
32
|
+
"command": "./scripts/on-prompt.sh",
|
|
33
|
+
"timeout": 3000
|
|
34
|
+
}
|
|
35
|
+
],
|
|
36
|
+
"Stop": [
|
|
37
|
+
{
|
|
38
|
+
"command": "./scripts/on-stop.sh",
|
|
39
|
+
"timeout": 5000
|
|
40
|
+
}
|
|
41
|
+
]
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Environment Variables
|
|
47
|
+
|
|
48
|
+
Hook scripts receive context via environment variables:
|
|
49
|
+
|
|
50
|
+
### PostToolUse
|
|
51
|
+
- `TOOL_NAME` — name of the tool that was used (e.g., "Edit", "Bash")
|
|
52
|
+
- `TOOL_INPUT` — JSON string of the tool's input parameters
|
|
53
|
+
- `TOOL_OUTPUT` — JSON string of the tool's output
|
|
54
|
+
|
|
55
|
+
### UserPromptSubmit
|
|
56
|
+
- `USER_PROMPT` — the text the user submitted
|
|
57
|
+
|
|
58
|
+
### Stop
|
|
59
|
+
- `STOP_REASON` — why the agent stopped (e.g., "end_turn", "max_tokens")
|
|
60
|
+
- `TOKEN_USAGE` — JSON with input/output token counts
|
|
61
|
+
|
|
62
|
+
## Example: Auto-Format on Edit
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
#!/bin/bash
|
|
66
|
+
# scripts/auto-format.sh — runs after PostToolUse
|
|
67
|
+
if [ "$TOOL_NAME" = "Edit" ] || [ "$TOOL_NAME" = "Write" ]; then
|
|
68
|
+
FILE=$(echo "$TOOL_INPUT" | jq -r '.file_path // empty')
|
|
69
|
+
if [ -n "$FILE" ] && [ -f "$FILE" ]; then
|
|
70
|
+
npx prettier --write "$FILE" 2>/dev/null
|
|
71
|
+
fi
|
|
72
|
+
fi
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Example: Track Tool Usage
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
#!/bin/bash
|
|
79
|
+
# scripts/track-usage.sh — counts tool usage per category
|
|
80
|
+
DATA_FILE=".local/usage.json"
|
|
81
|
+
CATEGORY=$(echo "$TOOL_NAME" | tr '[:upper:]' '[:lower:]')
|
|
82
|
+
|
|
83
|
+
if [ ! -f "$DATA_FILE" ]; then
|
|
84
|
+
echo '{}' > "$DATA_FILE"
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
jq --arg cat "$CATEGORY" '.[$cat] = ((.[$cat] // 0) + 1)' "$DATA_FILE" > "${DATA_FILE}.tmp"
|
|
88
|
+
mv "${DATA_FILE}.tmp" "$DATA_FILE"
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Best Practices
|
|
92
|
+
|
|
93
|
+
1. **Keep hooks fast** — they block the agent while running. Use the `timeout` field.
|
|
94
|
+
2. **Fail gracefully** — a hook error shouldn't break the user's workflow.
|
|
95
|
+
3. **Use exit codes** — exit 0 for success, non-zero to signal the agent.
|
|
96
|
+
4. **Log, don't print** — write to files, not stdout (stdout goes back to the agent).
|
|
97
|
+
5. **Test independently** — run your hook scripts manually before configuring them.
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Claude Code Hooks — Exercises
|
|
2
|
+
|
|
3
|
+
## Exercise 1: Write a Linting Hook
|
|
4
|
+
|
|
5
|
+
**Task:** Create a PostToolUse hook that runs a linter (e.g., ESLint, ShellCheck, or a language-appropriate linter) on files when the Edit or Write tool is used. Only run on files that match your project's conventions (e.g., `*.js`, `*.sh`).
|
|
6
|
+
|
|
7
|
+
**Validation:**
|
|
8
|
+
- [ ] Hook configured in `.claude/settings.json` under PostToolUse
|
|
9
|
+
- [ ] Hook checks `TOOL_NAME` and only runs for Edit/Write
|
|
10
|
+
- [ ] File path extracted from `TOOL_INPUT` (e.g., via `jq`)
|
|
11
|
+
- [ ] Linter runs on the edited file and results are written to a log file (not stdout)
|
|
12
|
+
- [ ] Hook has a reasonable timeout (e.g., 5000ms)
|
|
13
|
+
|
|
14
|
+
**Hints:**
|
|
15
|
+
1. Parse `TOOL_INPUT` with `jq`: `FILE=$(echo "$TOOL_INPUT" | jq -r '.file_path // empty')`
|
|
16
|
+
2. Check file extension before invoking the linter
|
|
17
|
+
3. Redirect linter output to a file: `eslint "$FILE" >> .local/lint.log 2>&1`
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Exercise 2: Write a Usage Tracking Hook
|
|
22
|
+
|
|
23
|
+
**Task:** Create a PostToolUse hook that increments a counter for each tool category in a JSON file (e.g., `.local/usage.json`). Structure: `{"edit": 5, "write": 3, "bash": 2}`. Use the hook's env vars to determine the tool.
|
|
24
|
+
|
|
25
|
+
**Validation:**
|
|
26
|
+
- [ ] Hook writes to a JSON file (create it if missing)
|
|
27
|
+
- [ ] Each tool use increments the correct key
|
|
28
|
+
- [ ] File is updated atomically (write to temp, then mv) to avoid corruption
|
|
29
|
+
- [ ] Hook exits 0 on success so it doesn't break the session
|
|
30
|
+
|
|
31
|
+
**Hints:**
|
|
32
|
+
1. Use `jq` to merge: `jq --arg cat "$TOOL_NAME" '.[$cat] = ((.[$cat] // 0) + 1)' file.json`
|
|
33
|
+
2. For atomic write: `jq ... file.json > file.json.tmp && mv file.json.tmp file.json`
|
|
34
|
+
3. Normalize tool name: `$(echo "$TOOL_NAME" | tr '[:upper:]' '[:lower:]')`
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Exercise 3: Write a Validation Hook
|
|
39
|
+
|
|
40
|
+
**Task:** Create a UserPromptSubmit hook that checks if the user's prompt contains a blocklisted word or pattern (e.g., "delete everything"). If detected, write a warning to a file and optionally append the prompt to an audit log. Do not block the agent — just log.
|
|
41
|
+
|
|
42
|
+
**Validation:**
|
|
43
|
+
- [ ] Hook reads `USER_PROMPT` from the environment
|
|
44
|
+
- [ ] Pattern matching implemented (grep, case statement, or simple `[[ == *pattern* ]]`)
|
|
45
|
+
- [ ] Warning written to a file when pattern matches
|
|
46
|
+
- [ ] Hook fails gracefully (handles empty `USER_PROMPT`, missing files)
|
|
47
|
+
- [ ] Hook has a short timeout (e.g., 2000ms)
|
|
48
|
+
|
|
49
|
+
**Hints:**
|
|
50
|
+
1. `if [[ "$USER_PROMPT" == *"delete everything"* ]]; then ...`
|
|
51
|
+
2. Write audit trail: `echo "$(date -Iseconds) $USER_PROMPT" >> .local/audit.log`
|
|
52
|
+
3. Use `set +e` or ensure script exits 0 even when pattern matches — you're logging, not blocking
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Exercise 4: Chain Multiple Hooks
|
|
57
|
+
|
|
58
|
+
**Task:** Configure at least two different hooks for the same project: (1) a PostToolUse hook that runs your linter, and (2) a Stop hook that logs token usage. Verify both run correctly in a single session.
|
|
59
|
+
|
|
60
|
+
**Validation:**
|
|
61
|
+
- [ ] PostToolUse hook runs during the session (check log/file output)
|
|
62
|
+
- [ ] Stop hook runs when the agent finishes (check log/file output)
|
|
63
|
+
- [ ] Both hooks are in `.claude/settings.json`
|
|
64
|
+
- [ ] Session completes without errors from the hooks
|
|
65
|
+
|
|
66
|
+
**Hints:**
|
|
67
|
+
1. Hooks are arrays — you can have multiple commands per event: `"PostToolUse": [{...}, {...}]`
|
|
68
|
+
2. Run a quick Edit in Claude Code, then check your hook output files
|
|
69
|
+
3. Ensure each script has a unique log file so you can distinguish them
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
slug: hooks
|
|
2
|
+
title: "Claude Code Hooks"
|
|
3
|
+
version: 1.0.0
|
|
4
|
+
description: "Automate workflows with lifecycle hooks — PostToolUse, UserPromptSubmit, Stop events."
|
|
5
|
+
category: claude-code
|
|
6
|
+
tags: [hooks, automation, claude-code, lifecycle]
|
|
7
|
+
difficulty: intermediate
|
|
8
|
+
|
|
9
|
+
xp:
|
|
10
|
+
read: 15
|
|
11
|
+
walkthrough: 30
|
|
12
|
+
exercise: 20
|
|
13
|
+
quiz: 15
|
|
14
|
+
quiz-perfect-bonus: 10
|
|
15
|
+
game: 25
|
|
16
|
+
game-perfect-bonus: 15
|
|
17
|
+
|
|
18
|
+
time:
|
|
19
|
+
quick: 5
|
|
20
|
+
read: 10
|
|
21
|
+
guided: 45
|
|
22
|
+
|
|
23
|
+
prerequisites: []
|
|
24
|
+
related: [skills, sub-agents]
|
|
25
|
+
|
|
26
|
+
triggers:
|
|
27
|
+
- "How do hooks work in Claude Code?"
|
|
28
|
+
- "How do I run a script after every tool use?"
|
|
29
|
+
- "What lifecycle events are available?"
|
|
30
|
+
|
|
31
|
+
visuals:
|
|
32
|
+
diagrams: [diagram-flow, diagram-architecture]
|
|
33
|
+
quiz-types: [quiz-timed-choice, quiz-fill-blank, quiz-drag-order]
|
|
34
|
+
playground: javascript
|
|
35
|
+
workshop: true
|
|
36
|
+
|
|
37
|
+
sources:
|
|
38
|
+
- url: "https://docs.anthropic.com/en/docs/claude-code"
|
|
39
|
+
label: "Claude Code Documentation"
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Claude Code Hooks — Quick Reference
|
|
2
|
+
|
|
3
|
+
## Lifecycle Events
|
|
4
|
+
|
|
5
|
+
| Event | When It Fires | Use Cases |
|
|
6
|
+
|---|---|---|
|
|
7
|
+
| **PostToolUse** | After any tool is used (Edit, Write, Bash, etc.) | Linting, formatting, usage tracking |
|
|
8
|
+
| **UserPromptSubmit** | When the user sends a message | Input validation, command detection |
|
|
9
|
+
| **Stop** | When the agent finishes a response | Session tracking, cleanup, notifications |
|
|
10
|
+
|
|
11
|
+
## Environment Variables by Event
|
|
12
|
+
|
|
13
|
+
### PostToolUse
|
|
14
|
+
| Variable | Description |
|
|
15
|
+
|---|---|
|
|
16
|
+
| `TOOL_NAME` | Name of the tool (e.g., "Edit", "Write", "Bash") |
|
|
17
|
+
| `TOOL_INPUT` | JSON string of the tool's input parameters |
|
|
18
|
+
| `TOOL_OUTPUT` | JSON string of the tool's output |
|
|
19
|
+
|
|
20
|
+
### UserPromptSubmit
|
|
21
|
+
| Variable | Description |
|
|
22
|
+
|---|---|
|
|
23
|
+
| `USER_PROMPT` | The text the user submitted |
|
|
24
|
+
|
|
25
|
+
### Stop
|
|
26
|
+
| Variable | Description |
|
|
27
|
+
|---|---|
|
|
28
|
+
| `STOP_REASON` | Why the agent stopped (e.g., "end_turn", "max_tokens") |
|
|
29
|
+
| `TOKEN_USAGE` | JSON with input/output token counts |
|
|
30
|
+
|
|
31
|
+
## Configuration Syntax
|
|
32
|
+
|
|
33
|
+
Hooks are defined in `.claude/settings.json` or `hooks.json`:
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
{
|
|
37
|
+
"hooks": {
|
|
38
|
+
"PostToolUse": [
|
|
39
|
+
{
|
|
40
|
+
"command": "./scripts/on-tool-use.sh",
|
|
41
|
+
"timeout": 5000
|
|
42
|
+
}
|
|
43
|
+
],
|
|
44
|
+
"UserPromptSubmit": [
|
|
45
|
+
{
|
|
46
|
+
"command": "./scripts/on-prompt.sh",
|
|
47
|
+
"timeout": 3000
|
|
48
|
+
}
|
|
49
|
+
],
|
|
50
|
+
"Stop": [
|
|
51
|
+
{
|
|
52
|
+
"command": "./scripts/on-stop.sh",
|
|
53
|
+
"timeout": 5000
|
|
54
|
+
}
|
|
55
|
+
]
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Parsing Tool Input (PostToolUse)
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# Get file path from Edit/Write tool input
|
|
64
|
+
FILE=$(echo "$TOOL_INPUT" | jq -r '.file_path // empty')
|
|
65
|
+
|
|
66
|
+
# Check if it's a code file
|
|
67
|
+
if [ -n "$FILE" ] && [[ "$FILE" == *.js ]]; then
|
|
68
|
+
npx eslint "$FILE" >> .local/lint.log 2>&1
|
|
69
|
+
fi
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Best Practices
|
|
73
|
+
|
|
74
|
+
| Practice | Reason |
|
|
75
|
+
|---|---|
|
|
76
|
+
| **Keep hooks fast** | They block the agent; use `timeout` |
|
|
77
|
+
| **Fail gracefully** | A hook error shouldn't break the user's workflow |
|
|
78
|
+
| **Log, don't print** | Stdout can be fed back to the agent |
|
|
79
|
+
| **Test independently** | Run scripts manually with fake env vars first |
|
|
80
|
+
| **Use exit codes** | Exit 0 for success; document non-zero semantics |
|
|
81
|
+
|
|
82
|
+
## Testing Hooks Manually
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# Simulate PostToolUse
|
|
86
|
+
TOOL_NAME=Edit TOOL_INPUT='{"file_path":"src/app.js"}' TOOL_OUTPUT='{}' ./scripts/on-tool-use.sh
|
|
87
|
+
|
|
88
|
+
# Simulate UserPromptSubmit
|
|
89
|
+
USER_PROMPT="Review this PR" ./scripts/on-prompt.sh
|
|
90
|
+
|
|
91
|
+
# Simulate Stop
|
|
92
|
+
STOP_REASON=end_turn TOKEN_USAGE='{"input":1000,"output":500}' ./scripts/on-stop.sh
|
|
93
|
+
```
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Claude Code Hooks — Quiz
|
|
2
|
+
|
|
3
|
+
## Question 1
|
|
4
|
+
|
|
5
|
+
What are Claude Code hooks?
|
|
6
|
+
|
|
7
|
+
A) AI-generated code snippets
|
|
8
|
+
B) Shell commands that run automatically in response to lifecycle events
|
|
9
|
+
C) Plugins that extend the Claude Code UI
|
|
10
|
+
D) Environment variables set by the user
|
|
11
|
+
|
|
12
|
+
<!-- ANSWER: B -->
|
|
13
|
+
<!-- EXPLANATION: Hooks are shell commands defined in hooks.json or .claude/settings.json. Claude Code executes them automatically when events like PostToolUse, UserPromptSubmit, or Stop occur. -->
|
|
14
|
+
|
|
15
|
+
## Question 2
|
|
16
|
+
|
|
17
|
+
<!-- VISUAL: fill-blank -->
|
|
18
|
+
|
|
19
|
+
Complete the hook configuration. To run a script after every tool use, you add it under the __________ key in `.claude/settings.json`:
|
|
20
|
+
|
|
21
|
+
```json
|
|
22
|
+
{
|
|
23
|
+
"hooks": {
|
|
24
|
+
"__________": [
|
|
25
|
+
{ "command": "./scripts/on-tool-use.sh", "timeout": 5000 }
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
<!-- ANSWER: PostToolUse -->
|
|
32
|
+
<!-- EXPLANATION: PostToolUse is the lifecycle event that fires after any tool is used (Edit, Write, Bash, etc.). The configuration key must match the exact event name. -->
|
|
33
|
+
|
|
34
|
+
## Question 3
|
|
35
|
+
|
|
36
|
+
Which environment variable is available in a PostToolUse hook?
|
|
37
|
+
|
|
38
|
+
A) `USER_PROMPT`
|
|
39
|
+
B) `TOOL_NAME`
|
|
40
|
+
C) `STOP_REASON`
|
|
41
|
+
D) `TOKEN_USAGE`
|
|
42
|
+
|
|
43
|
+
<!-- ANSWER: B -->
|
|
44
|
+
<!-- EXPLANATION: PostToolUse hooks receive TOOL_NAME, TOOL_INPUT, and TOOL_OUTPUT. USER_PROMPT is for UserPromptSubmit; STOP_REASON and TOKEN_USAGE are for Stop. -->
|
|
45
|
+
|
|
46
|
+
## Question 4
|
|
47
|
+
|
|
48
|
+
<!-- VISUAL: drag-order -->
|
|
49
|
+
|
|
50
|
+
Put these lifecycle events in the order they occur during a single agent turn:
|
|
51
|
+
|
|
52
|
+
A) Stop
|
|
53
|
+
B) UserPromptSubmit
|
|
54
|
+
C) PostToolUse (may fire multiple times)
|
|
55
|
+
|
|
56
|
+
<!-- ANSWER: B,C,A -->
|
|
57
|
+
<!-- EXPLANATION: UserPromptSubmit fires when the user sends a message (start of turn). PostToolUse fires after each tool call during the turn. Stop fires when the agent finishes its response (end of turn). -->
|
|
58
|
+
|
|
59
|
+
## Question 5
|
|
60
|
+
|
|
61
|
+
Why should hook scripts "log, don't print"?
|
|
62
|
+
|
|
63
|
+
A) Printing is slower than file I/O
|
|
64
|
+
B) Stdout from hooks can be fed back to the agent and confuse it
|
|
65
|
+
C) Logs are required by Claude Code
|
|
66
|
+
D) Printing only works in certain shells
|
|
67
|
+
|
|
68
|
+
<!-- ANSWER: B -->
|
|
69
|
+
<!-- EXPLANATION: When a hook runs, its stdout may be captured and sent to the agent as context. Writing diagnostic output to stdout could pollute the agent's context. Writing to files keeps the agent's input clean. -->
|
|
70
|
+
|
|
71
|
+
## Question 6
|
|
72
|
+
|
|
73
|
+
What is a key reason to set a `timeout` on a hook?
|
|
74
|
+
|
|
75
|
+
A) Hooks are asynchronous by default
|
|
76
|
+
B) Hooks block the agent — a slow or stuck hook delays the whole session
|
|
77
|
+
C) Timeouts are required for security
|
|
78
|
+
D) Without a timeout, hooks run indefinitely in the background
|
|
79
|
+
|
|
80
|
+
<!-- ANSWER: B -->
|
|
81
|
+
<!-- EXPLANATION: Hooks run synchronously. While a hook is executing, the agent waits. A hung or very slow hook (e.g., a network call) would block the user's workflow. The timeout field prevents runaway hooks. -->
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Claude Code Hooks — Resources
|
|
2
|
+
|
|
3
|
+
## Official Docs
|
|
4
|
+
|
|
5
|
+
- [Claude Code Documentation](https://docs.anthropic.com/en/docs/claude-code) — Main reference for hooks configuration, lifecycle events, and environment variables.
|
|
6
|
+
- [Claude Code Settings](https://docs.anthropic.com/en/docs/claude-code/settings) — Where hooks are configured in `.claude/settings.json`.
|
|
7
|
+
|
|
8
|
+
## Videos
|
|
9
|
+
|
|
10
|
+
- [Claude Code Deep Dive](https://www.youtube.com/results?search_query=claude+code+hooks+automation) — Walkthroughs demonstrating hook-based automation.
|
|
11
|
+
- [Git Hooks Explained](https://www.youtube.com/watch?v=egfuwOe8nXc) — Fireship. Conceptually similar lifecycle hooks in Git — helpful mental model.
|
|
12
|
+
|
|
13
|
+
## Articles and Readings
|
|
14
|
+
|
|
15
|
+
- [Anthropic Cookbook — Tool Use](https://github.com/anthropics/anthropic-cookbook/tree/main/tool_use) — Patterns for extending agent behavior, relevant to hook design.
|
|
16
|
+
- [Git Hooks Documentation](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) — Pro Git. Lifecycle hook patterns that inspired Claude Code hooks.
|
|
17
|
+
- [Husky — Git Hooks Made Easy](https://typicode.github.io/husky/) — Popular tool for managing Git hooks, illustrates the hook automation pattern.
|
|
18
|
+
|
|
19
|
+
## Books
|
|
20
|
+
|
|
21
|
+
- **The Pragmatic Programmer** by Hunt & Thomas — Chapter on "Domain Languages" covers automation pipelines relevant to hook scripting. [pragprog.com](https://pragprog.com/titles/tpp20/the-pragmatic-programmer-20th-anniversary-edition/).
|
|
22
|
+
|
|
23
|
+
## Tools and Frameworks
|
|
24
|
+
|
|
25
|
+
- [jq](https://jqlang.github.io/jq/) — Lightweight JSON processor used in hook scripts to parse tool input/output.
|
|
26
|
+
- [Prettier](https://prettier.io/) — Code formatter commonly invoked from PostToolUse hooks for auto-formatting.
|
|
27
|
+
- [ESLint](https://eslint.org/) — Linter frequently wired into hooks for real-time code quality checks.
|
|
28
|
+
- [ShellCheck](https://www.shellcheck.net/) — Static analysis for bash scripts — useful for validating your hook scripts.
|
|
29
|
+
- [lefthook](https://github.com/evilmartians/lefthook) — Fast hook runner for Git, useful pattern reference for lifecycle automation.
|
|
30
|
+
|
|
31
|
+
## Related Concepts
|
|
32
|
+
|
|
33
|
+
- [Webhooks (MDN)](https://developer.mozilla.org/en-US/docs/Glossary/Webhook) — The broader pattern of event-driven callbacks that hooks implement.
|
|
34
|
+
- [Observer Pattern (Refactoring Guru)](https://refactoring.guru/design-patterns/observer) — Design pattern behind lifecycle event systems.
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Claude Code Hooks — Walkthrough
|
|
2
|
+
|
|
3
|
+
## Before We Begin
|
|
4
|
+
|
|
5
|
+
<!-- hint:card type="concept" title="Automation runs in response to events" -->
|
|
6
|
+
|
|
7
|
+
**Warm-up:** Think about automation you use today — Git hooks, CI pipelines, formatters that run on save. What do they have in common? What "events" trigger them, and what happens after?
|
|
8
|
+
|
|
9
|
+
**Checkpoint:** The user should recognize that automation is often event-driven: something happens (a commit, a push, a file save), and a script runs. Hooks extend this pattern to the AI coding session.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Step 1: What Are Hooks?
|
|
14
|
+
|
|
15
|
+
Hooks are shell commands that Claude Code runs automatically when certain events occur.
|
|
16
|
+
|
|
17
|
+
<!-- hint:diagram mermaid-type="flowchart" topic="Hook flow: event fires → script runs → continues" -->
|
|
18
|
+
|
|
19
|
+
**Task:** Look at your project's `.claude/settings.json` (or the hooks docs). See if any hooks are already configured.
|
|
20
|
+
|
|
21
|
+
**Question:** Why might you want a script to run *after* the agent uses a tool, rather than manually running it yourself?
|
|
22
|
+
|
|
23
|
+
**Checkpoint:** The user should understand that hooks automate repetitive workflows — e.g., linting after every edit, so you never forget. They run transparently in the background of the session.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Step 2: Lifecycle Events
|
|
28
|
+
|
|
29
|
+
Claude Code fires hooks at three lifecycle points: PostToolUse, UserPromptSubmit, and Stop.
|
|
30
|
+
|
|
31
|
+
**Task:** Review the table of events. For each event, name one concrete use case from your own workflow.
|
|
32
|
+
|
|
33
|
+
**Question:** PostToolUse runs after *every* tool call — Edit, Write, Bash, etc. What's the trade-off? When might that be too frequent?
|
|
34
|
+
|
|
35
|
+
**Checkpoint:** The user should see that PostToolUse is powerful but can be noisy — you might want to filter by tool name (e.g., only run on Edit/Write) or add a timeout to avoid slowdown.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Step 3: Writing a PostToolUse Hook
|
|
40
|
+
|
|
41
|
+
PostToolUse receives `TOOL_NAME`, `TOOL_INPUT`, and `TOOL_OUTPUT` as environment variables.
|
|
42
|
+
|
|
43
|
+
<!-- hint:terminal -->
|
|
44
|
+
|
|
45
|
+
**Task:** Write a minimal PostToolUse hook that logs the tool name to a file (e.g., `.local/hook-log.txt`) when the tool is Edit or Write. Configure it in `.claude/settings.json`.
|
|
46
|
+
|
|
47
|
+
**Question:** How would you extract the file path from `TOOL_INPUT` inside a shell script? (Hint: it's JSON.)
|
|
48
|
+
|
|
49
|
+
**Checkpoint:** The user should use `jq` or similar to parse `TOOL_INPUT` and get `file_path`. They should configure the hook with a timeout.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Step 4: Writing a UserPromptSubmit Hook
|
|
54
|
+
|
|
55
|
+
UserPromptSubmit fires when the user sends a message, before the agent responds.
|
|
56
|
+
|
|
57
|
+
<!-- hint:card type="concept" title="UserPromptSubmit runs before the agent thinks" -->
|
|
58
|
+
|
|
59
|
+
**Task:** Write a UserPromptSubmit hook that writes the first 100 characters of the user's prompt to a log file. Use the `USER_PROMPT` environment variable.
|
|
60
|
+
|
|
61
|
+
**Question:** What could you use UserPromptSubmit for? Think beyond logging — validation, command detection, rate limiting.
|
|
62
|
+
|
|
63
|
+
**Checkpoint:** The user should identify uses like: detecting `/teach` and routing to a specific handler; validating prompt length; blocking unsafe prompts.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
## Step 5: Writing a Stop Hook
|
|
68
|
+
|
|
69
|
+
Stop fires when the agent finishes a response. It receives `STOP_REASON` and `TOKEN_USAGE`.
|
|
70
|
+
|
|
71
|
+
<!-- hint:terminal -->
|
|
72
|
+
|
|
73
|
+
**Task:** Write a Stop hook that appends the session's token usage to a file. Parse `TOKEN_USAGE` (JSON) and log input/output counts.
|
|
74
|
+
|
|
75
|
+
**Question:** Why run something at "Stop" rather than at "PostToolUse"? What kinds of things only make sense at the end of a turn?
|
|
76
|
+
|
|
77
|
+
**Checkpoint:** The user should recognize that session-level metrics (duration, token usage, cleanup) only make sense when the turn is complete. PostToolUse would fire many times per turn; Stop fires once.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Step 6: Testing Hooks
|
|
82
|
+
|
|
83
|
+
Hooks block the agent while they run. Slow or failing hooks hurt the experience.
|
|
84
|
+
|
|
85
|
+
<!-- hint:terminal -->
|
|
86
|
+
|
|
87
|
+
**Task:** Run your PostToolUse hook script manually with fake env vars: `TOOL_NAME=Edit TOOL_INPUT='{"file_path":"test.txt"}' ./scripts/on-tool-use.sh`. Verify it behaves correctly.
|
|
88
|
+
|
|
89
|
+
**Question:** What happens if your hook script exits with code 1? Should it? How does the agent interpret non-zero exit?
|
|
90
|
+
|
|
91
|
+
**Checkpoint:** The user should test hooks in isolation before wiring them up. They should consider: hooks should fail gracefully — a lint failure shouldn't crash the session. Exit codes can signal the agent; document the contract.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Step 7: Error Handling and Best Practices
|
|
96
|
+
|
|
97
|
+
Hooks run synchronously. They can slow down or break the workflow if misused.
|
|
98
|
+
|
|
99
|
+
<!-- hint:card type="concept" title="Keep hooks fast; fail gracefully" -->
|
|
100
|
+
|
|
101
|
+
**Task:** Add a `timeout` to each hook in your config. Ensure your scripts handle missing or malformed env vars (e.g., empty `TOOL_INPUT`).
|
|
102
|
+
|
|
103
|
+
**Question:** The content says "Log, don't print" — why? What happens to stdout when a hook runs?
|
|
104
|
+
|
|
105
|
+
**Checkpoint:** The user should understand that stdout from hooks can be fed back to the agent, which may confuse it. Write to files instead. Use `timeout` to prevent runaway hooks.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
scenarios:
|
|
2
|
+
- id: lint-hook
|
|
3
|
+
title: "Build a Lint Hook"
|
|
4
|
+
difficulty: beginner
|
|
5
|
+
xp: 25
|
|
6
|
+
setup: []
|
|
7
|
+
files:
|
|
8
|
+
- name: scripts/lint-hook.sh
|
|
9
|
+
content: |
|
|
10
|
+
#!/bin/bash
|
|
11
|
+
# TODO: Implement PostToolUse hook that checks for leftover TODO comments
|
|
12
|
+
exit 0
|
|
13
|
+
language: bash
|
|
14
|
+
readonly: false
|
|
15
|
+
task: "Write a PostToolUse hook that runs after Edit or Write and checks the modified file for leftover TODO comments. If any are found, append the file path and line numbers to .local/todo-report.txt. Configure the hook in .claude/settings.json with a 5000ms timeout."
|
|
16
|
+
validations:
|
|
17
|
+
- label: "Hook script exists and is executable"
|
|
18
|
+
check: output-contains
|
|
19
|
+
pattern: "lint-hook|todo-report|TODO"
|
|
20
|
+
- label: "Hook configured in settings"
|
|
21
|
+
check: output-contains
|
|
22
|
+
pattern: "PostToolUse|hooks|settings.json"
|
|
23
|
+
- label: "TODO detection implemented"
|
|
24
|
+
check: output-contains
|
|
25
|
+
pattern: "grep|TODO|todo"
|
|
26
|
+
hints:
|
|
27
|
+
- "Parse TOOL_INPUT with jq to get file_path. Only run when TOOL_NAME is Edit or Write."
|
|
28
|
+
- "Use grep -n 'TODO' to find lines. Append results to .local/todo-report.txt."
|
|
29
|
+
- "Ensure the script handles missing files and exits 0 to avoid breaking the session."
|
|
30
|
+
agent_prompts:
|
|
31
|
+
on_start: "What's the difference between a hook that runs on every tool and one that only runs on Edit/Write? Why might you want to filter?"
|
|
32
|
+
on_complete: "How would you extend this hook to also check for FIXME or XXX comments? What about excluding comments in test files?"
|
|
33
|
+
|
|
34
|
+
- id: session-tracker
|
|
35
|
+
title: "Build a Session Tracker"
|
|
36
|
+
difficulty: beginner
|
|
37
|
+
xp: 20
|
|
38
|
+
setup: []
|
|
39
|
+
files:
|
|
40
|
+
- name: scripts/session-tracker.sh
|
|
41
|
+
content: |
|
|
42
|
+
#!/bin/bash
|
|
43
|
+
# TODO: Implement Stop hook that logs session duration
|
|
44
|
+
exit 0
|
|
45
|
+
language: bash
|
|
46
|
+
readonly: false
|
|
47
|
+
task: "Write a Stop hook that logs the session end time and token usage to .local/session-log.json. Each entry should include: timestamp, STOP_REASON, and parsed input/output token counts from TOKEN_USAGE. Configure it in .claude/settings.json."
|
|
48
|
+
validations:
|
|
49
|
+
- label: "Stop hook script exists"
|
|
50
|
+
check: output-contains
|
|
51
|
+
pattern: "session-tracker|session-log|STOP_REASON|TOKEN_USAGE"
|
|
52
|
+
- label: "Hook configured for Stop event"
|
|
53
|
+
check: output-contains
|
|
54
|
+
pattern: "Stop|hooks"
|
|
55
|
+
- label: "JSON logging implemented"
|
|
56
|
+
check: output-contains
|
|
57
|
+
pattern: "jq|json|timestamp"
|
|
58
|
+
hints:
|
|
59
|
+
- "STOP_REASON and TOKEN_USAGE are in the environment. TOKEN_USAGE is JSON."
|
|
60
|
+
- "Use jq to parse TOKEN_USAGE and build a log entry. Append to .local/session-log.json as a new line (JSONL format)."
|
|
61
|
+
- "For timestamp: date -Iseconds or date +%Y-%m-%dT%H:%M:%S"
|
|
62
|
+
agent_prompts:
|
|
63
|
+
on_start: "Why does a session tracker belong in a Stop hook rather than PostToolUse? What would happen if you used PostToolUse?"
|
|
64
|
+
on_complete: "How could you extend this to also record session duration? What would you need to capture at the start of a session?"
|