Skip to content

Commit 8971150

Browse files
authored
Merge pull request #9 from DojoCodingLabs/daniel/doj-2434-debugging-skill
feat: add debugging skill with error detection (DOJ-2434)
2 parents f2da367 + 1cc76db commit 8971150

File tree

3 files changed

+157
-33
lines changed

3 files changed

+157
-33
lines changed

data/concept-tree.json

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -358,8 +358,36 @@
358358
"triggers": ["cache", "redis", "CDN", "load balancer"]
359359
}
360360
}
361+
},
362+
"debugging": {
363+
"name": "Debugging",
364+
"icon": "🐛",
365+
"belt_requirement": "white",
366+
"concepts": {
367+
"error-reading": {
368+
"name": "Error Reading",
369+
"xp_to_master": 15,
370+
"prerequisites": [],
371+
"description": "Understanding error messages and stack traces",
372+
"triggers": ["error", "Error:", "Traceback", "ENOENT", "stack trace"]
373+
},
374+
"debugging-mindset": {
375+
"name": "Debugging Mindset",
376+
"xp_to_master": 20,
377+
"prerequisites": ["error-reading"],
378+
"description": "Systematic approach to finding and fixing bugs",
379+
"triggers": ["debug", "breakpoint", "console.log", "print("]
380+
},
381+
"common-errors": {
382+
"name": "Common Errors",
383+
"xp_to_master": 25,
384+
"prerequisites": ["error-reading", "debugging-mindset"],
385+
"description": "Recognizing and fixing common programming errors",
386+
"triggers": ["TypeError", "ReferenceError", "SyntaxError", "undefined", "null"]
387+
}
388+
}
361389
}
362390
},
363-
"total_concepts": 42,
391+
"total_concepts": 45,
364392
"mastery_quiz_threshold": 3
365393
}

data/quiz-bank.json

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,51 @@
215215
"expected_understanding": "Docker packages an app with all its dependencies so it runs the same everywhere. Solves 'works on my machine' problems by creating consistent environments.",
216216
"hint": "Think about shipping a complete kitchen vs. just a recipe."
217217
}
218+
],
219+
"error-reading": [
220+
{
221+
"belt": "white",
222+
"question": "What does a stack trace show you?",
223+
"options": ["The list of files in your project", "The sequence of function calls that led to the error", "All the variables currently in memory"],
224+
"correct": 1,
225+
"explanation": "A stack trace is like a breadcrumb trail — it shows every function that was called, in order, up until the crash. Reading it from bottom to top tells you where the error originated and how the code got there.",
226+
"hint": "Think of it as a history of every step the program took before it fell over."
227+
},
228+
{
229+
"belt": "white",
230+
"question": "What is the first thing you should read in an error message?",
231+
"options": ["The line number at the very bottom", "The error type and the first line of the message", "The full stack trace from top to bottom"],
232+
"correct": 1,
233+
"explanation": "The error type (like `TypeError` or `SyntaxError`) and the first descriptive line tell you WHAT went wrong. Once you know what, you can use the line number and stack trace to figure out WHERE. Start at the top, not the bottom.",
234+
"hint": "Skimming a news article — you read the headline first, then the details."
235+
}
236+
],
237+
"debugging-mindset": [
238+
{
239+
"belt": "yellow",
240+
"question": "What is the most effective first step when debugging?",
241+
"options": ["Delete the code and rewrite it from scratch", "Reproduce the error reliably, then read the error message carefully", "Ask someone else to fix it"],
242+
"correct": 1,
243+
"explanation": "You can't fix what you can't consistently reproduce. Once you can make the bug happen on demand, read the error message carefully — it usually tells you exactly what went wrong and where. Only after understanding the error should you start changing code.",
244+
"hint": "A doctor diagnoses before prescribing. What's the equivalent first step for a bug?"
245+
}
246+
],
247+
"common-errors": [
248+
{
249+
"belt": "orange",
250+
"question": "What typically causes a ReferenceError in JavaScript?",
251+
"options": ["Using the wrong data type in a calculation", "Trying to use a variable that hasn't been declared or is out of scope", "A typo in an HTML tag"],
252+
"correct": 1,
253+
"explanation": "A `ReferenceError` means JavaScript looked for a variable name and couldn't find it anywhere in scope. Common causes: misspelling the variable name, using it before declaring it, or accessing it outside the block where it was defined.",
254+
"hint": "The error name says 'reference' — what does it mean when a reference points to nothing?"
255+
},
256+
{
257+
"belt": "orange",
258+
"format": "free_response",
259+
"question": "What's the difference between a syntax error and a runtime error? Give an example of each.",
260+
"expected_understanding": "A syntax error is caught before the code runs — it's a grammar mistake the interpreter can't parse (e.g., missing closing bracket). A runtime error happens while the code is running, when an operation fails on valid-looking code (e.g., calling a method on null).",
261+
"hint": "Think about the difference between a grammatically incorrect sentence and a sentence that makes sense but describes something impossible."
262+
}
218263
]
219264
}
220265
}

scripts/track-command.sh

Lines changed: 83 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,44 @@ else
3333
check_jq() { command -v jq &>/dev/null; }
3434
fi
3535

36+
record_profile_concept() {
37+
local concept="$1"
38+
local first_flag_var="$2"
39+
local already_in_session
40+
local already_in_lifetime
41+
42+
if [ -z "$concept" ] || [ ! -f "$PROFILE_FILE" ]; then
43+
return
44+
fi
45+
46+
already_in_session=$(jq --arg c "$concept" '.session_concepts | index($c)' "$PROFILE_FILE" 2>&1)
47+
if [ $? -ne 0 ]; then
48+
log_error "$SCRIPT_NAME" "jq failed checking session_concepts for $concept: $already_in_session"
49+
already_in_session="0"
50+
fi
51+
52+
already_in_lifetime=$(jq --arg c "$concept" '.concepts_seen | index($c)' "$PROFILE_FILE" 2>&1)
53+
if [ $? -ne 0 ]; then
54+
log_error "$SCRIPT_NAME" "jq failed checking concepts_seen for $concept: $already_in_lifetime"
55+
already_in_lifetime="0"
56+
fi
57+
58+
if [ "$already_in_lifetime" = "null" ]; then
59+
if update_profile --arg c "$concept" '
60+
.session_concepts += (if (.session_concepts | index($c)) == null then [$c] else [] end) |
61+
.concepts_seen += (if (.concepts_seen | index($c)) == null then [$c] else [] end)
62+
'; then
63+
printf -v "$first_flag_var" '%s' "true"
64+
else
65+
log_error "$SCRIPT_NAME" "Failed updating profile for first-time concept: $concept"
66+
fi
67+
elif [ "$already_in_session" = "null" ]; then
68+
if ! update_profile --arg c "$concept" '.session_concepts += [$c]'; then
69+
log_error "$SCRIPT_NAME" "Failed updating session_concepts for concept: $concept"
70+
fi
71+
fi
72+
}
73+
3674
INPUT=$(cat)
3775

3876
ensure_profile_dir
@@ -104,40 +142,51 @@ then
104142
log_error "$SCRIPT_NAME" "Failed to write to commands log: $COMMANDS_LOG"
105143
fi
106144

107-
if [ -z "$CONCEPT" ]; then
145+
SAFE_CMD=$(printf '%s' "$COMMAND" | head -c 80 | tr '"' "'" | tr '\\' '/')
146+
147+
IS_TEST_RUNNER="false"
148+
case "$COMMAND" in
149+
jest\ *|"jest"|\
150+
pytest\ *|"pytest"|\
151+
vitest\ *|"vitest"|\
152+
bats\ *|"bats"|\
153+
mocha\ *|"mocha"|\
154+
"npm test"*|"npm run test"*|\
155+
"yarn test"*|"yarn run test"*|\
156+
"pnpm test"*|"pnpm run test"*|"pnpm vitest"*|\
157+
"npx jest"*|"npx vitest"*|"pnpm exec jest"*|"pnpm exec vitest"*)
158+
IS_TEST_RUNNER="true"
159+
;;
160+
esac
161+
162+
ERROR_CONCEPT=""
163+
if [ "$IS_TEST_RUNNER" = "false" ]; then
164+
TOOL_RESPONSE=$(printf '%s' "$INPUT" | jq -r '.tool_response // ""' 2>/dev/null || echo "")
165+
STDOUT=$(printf '%s' "$INPUT" | jq -r '.tool_response.stdout // ""' 2>/dev/null || echo "")
166+
STDERR=$(printf '%s' "$INPUT" | jq -r '.tool_response.stderr // ""' 2>/dev/null || echo "")
167+
OUTPUT="${STDOUT}${STDERR}${TOOL_RESPONSE}"
168+
169+
case "$OUTPUT" in
170+
*"Traceback (most recent call last):"*) ERROR_CONCEPT="error-reading" ;;
171+
*"TypeError"*|*"ReferenceError"*|*"SyntaxError"*) ERROR_CONCEPT="common-errors" ;;
172+
*"Error:"*|*"ERROR:"*|*"error:"*) ERROR_CONCEPT="error-reading" ;;
173+
*"ENOENT"*|*"EACCES"*|*"EPERM"*) ERROR_CONCEPT="error-reading" ;;
174+
*"command not found"*) ERROR_CONCEPT="error-reading" ;;
175+
*"ModuleNotFoundError"*|*"ImportError"*) ERROR_CONCEPT="error-reading" ;;
176+
*"fatal:"*) ERROR_CONCEPT="error-reading" ;;
177+
esac
178+
fi
179+
180+
if [ -z "$CONCEPT" ] && [ -z "$ERROR_CONCEPT" ]; then
108181
printf '{}\n'
109182
exit 0
110183
fi
111184

112185
IS_FIRST_EVER="false"
113-
if [ -f "$PROFILE_FILE" ]; then
114-
ALREADY_IN_SESSION=$(jq --arg c "$CONCEPT" '.session_concepts | index($c)' "$PROFILE_FILE" 2>&1)
115-
if [ $? -ne 0 ]; then
116-
log_error "$SCRIPT_NAME" "jq failed checking session_concepts for $CONCEPT: $ALREADY_IN_SESSION"
117-
ALREADY_IN_SESSION="0"
118-
fi
119-
120-
ALREADY_IN_LIFETIME=$(jq --arg c "$CONCEPT" '.concepts_seen | index($c)' "$PROFILE_FILE" 2>&1)
121-
if [ $? -ne 0 ]; then
122-
log_error "$SCRIPT_NAME" "jq failed checking concepts_seen for $CONCEPT: $ALREADY_IN_LIFETIME"
123-
ALREADY_IN_LIFETIME="0"
124-
fi
186+
record_profile_concept "$CONCEPT" IS_FIRST_EVER
125187

126-
if [ "$ALREADY_IN_LIFETIME" = "null" ]; then
127-
IS_FIRST_EVER="true"
128-
if ! update_profile --arg c "$CONCEPT" '
129-
.session_concepts += (if (.session_concepts | index($c)) == null then [$c] else [] end) |
130-
.concepts_seen += (if (.concepts_seen | index($c)) == null then [$c] else [] end)
131-
'; then
132-
log_error "$SCRIPT_NAME" "Failed updating profile for first-time concept: $CONCEPT"
133-
IS_FIRST_EVER="false"
134-
fi
135-
elif [ "$ALREADY_IN_SESSION" = "null" ]; then
136-
if ! update_profile --arg c "$CONCEPT" '.session_concepts += [$c]'; then
137-
log_error "$SCRIPT_NAME" "Failed updating session_concepts for concept: $CONCEPT"
138-
fi
139-
fi
140-
fi
188+
ERROR_IS_FIRST_EVER="false"
189+
record_profile_concept "$ERROR_CONCEPT" ERROR_IS_FIRST_EVER
141190

142191
NOW=$(date +%s)
143192
if [ -f "$SESSION_STATE" ]; then
@@ -154,7 +203,7 @@ if [ "$TRIGGER_COUNT" -ge "$SESSION_CAP" ]; then
154203
fi
155204

156205
ELAPSED=$((NOW - LAST_TRIGGER))
157-
if [ "$ELAPSED" -lt "$RATE_LIMIT_INTERVAL" ] && [ "$IS_FIRST_EVER" != "true" ]; then
206+
if [ "$ELAPSED" -lt "$RATE_LIMIT_INTERVAL" ] && [ "$IS_FIRST_EVER" != "true" ] && [ "$ERROR_IS_FIRST_EVER" != "true" ]; then
158207
printf '{}\n'
159208
exit 0
160209
fi
@@ -185,9 +234,11 @@ if [ $? -ne 0 ]; then
185234
BELT="white"
186235
fi
187236

188-
SAFE_CMD=$(printf '%s' "$COMMAND" | head -c 80 | tr '"' "'" | tr '\\' '/')
189-
190-
if [ "$IS_FIRST_EVER" = "true" ]; then
237+
if [ "$ERROR_IS_FIRST_EVER" = "true" ] && [ -n "$ERROR_CONCEPT" ]; then
238+
CONTEXT="🥋 CodeSensei micro-lesson trigger: The user just encountered '$ERROR_CONCEPT' for the FIRST TIME while reading command output ($SAFE_CMD). Their belt level is '$BELT'. Provide a brief 2-sentence explanation of how to read this kind of error and why it matters. Adapt language to their belt level. Keep it supportive and practical."
239+
elif [ -n "$ERROR_CONCEPT" ]; then
240+
CONTEXT="🥋 CodeSensei inline insight: An error appeared in the command output ($SAFE_CMD). The user's belt level is '$BELT'. This is a great moment to teach '$ERROR_CONCEPT' -- briefly explain how to read and interpret this type of error in 1-2 sentences, adapted to their belt level. Keep it supportive and practical."
241+
elif [ "$IS_FIRST_EVER" = "true" ]; then
191242
CONTEXT="🥋 CodeSensei micro-lesson trigger: The user just encountered '$CONCEPT' for the FIRST TIME (command: $SAFE_CMD). Their belt level is '$BELT'. Provide a brief 2-sentence explanation of what $CONCEPT means and why it matters. Adapt language to their belt level. Keep it concise and non-intrusive."
192243
else
193244
CONTEXT="🥋 CodeSensei inline insight: Claude just ran a '$CONCEPT' command ($SAFE_CMD). The user's belt level is '$BELT'. Provide a brief 1-sentence explanation of what this command does, adapted to their belt level. Keep it natural and non-intrusive."

0 commit comments

Comments
 (0)