| name | debug-mode |
| description | Use this skill when the user explicitly invokes "/debug-mode" or says "enter debug mode", "start debug mode", "use debug mode". This is a token-heavy workflow — do NOT activate automatically for general debugging questions or simple bug fixes.
|
| version | 0.1.0 |
Debug Mode: Hypothesis-Driven Runtime Debugging
Overview
Debug Mode is a structured debugging workflow inspired by Cursor's Debug Mode. Instead of guessing at fixes, you form hypotheses about root causes, instrument code with targeted logging, collect runtime evidence, then make minimal, precise fixes.
Core principle: Evidence first, fix second.
When to Use
- Runtime bugs that are hard to reproduce or understand from code alone
- Race conditions, async/timing issues
- State corruption or unexpected values at runtime
- Bugs where the root cause isn't obvious from reading code
- Intermittent failures that need runtime data to diagnose
Methodology
The workflow has 7 steps:
- Understand the bug — Read the bug description and relevant code
- Generate hypotheses — Form 3-5 specific theories about the root cause, labeled H1-H5
- Instrument code — Add
fetch() calls at strategic locations, each tagged with which hypothesis it tests
- Start debug server — Copy and run the server that collects logs to
.debug-mode/debug.log
- Reproduce — Ask user to reproduce the bug while instrumentation collects evidence
- Evaluate hypotheses — Read logs, mark each hypothesis CONFIRMED or REJECTED with evidence
- Fix and clean up — Apply minimal fix for confirmed hypothesis, remove all instrumentation
If the fix doesn't work, loop back to step 3 with additional hypotheses or more instrumentation.
Workflow
Step 1: Create Task List
Use TodoWrite to create a task list:
- Understand bug and read codebase
- Generate hypotheses
- Start debug server and instrument code
- Ask user to reproduce
- Analyze logs and evaluate hypotheses
- Propose and verify fix
- Remove all instrumentation and cleanup
Step 2: Understand the Bug
If the bug description is vague or unclear, ask the user:
- What is the unexpected behavior?
- What steps reproduce it?
- What is the expected behavior?
- What environment / runtime?
Read relevant source files to understand the suspected code paths. Use Grep and Glob to find related code.
Step 3: Generate Hypotheses
Based on codebase reading, produce 3-5 hypotheses about the root cause.
Format each hypothesis as a one-sentence theory:
H1: [specific root cause theory]
H2: [specific root cause theory]
H3: [specific root cause theory]
Present these to the user, then proceed directly to Step 4.
Step 4: Start Debug Server
Set up the debug infrastructure:
- Create the debug directory and copy the server script:
mkdir -p .debug-mode && cp "$(find ~/.claude/plugins -path '*/debug-mode/server.js' -print -quit 2>/dev/null || find .claude-plugin -path '*/debug-mode/server.js' -print -quit 2>/dev/null || find plugin/skills -path '*/debug-mode/server.js' -print -quit 2>/dev/null)" .debug-mode/server.js
If the copy command fails, look for server.js in these locations (in order):
~/.claude/plugins/*/skills/debug-mode/server.js (Claude Code plugin installation)
.cursor/skills/debug-mode/server.js (Cursor deployment)
.opencode/skills/debug-mode/server.js (OpenCode deployment)
plugin/skills/debug-mode/server.js (source repo)
- Add
.debug-mode/ to .gitignore if not already present:
grep -qxF '.debug-mode/' .gitignore 2>/dev/null || echo '.debug-mode/' >> .gitignore
- Start the server in the background:
node .debug-mode/server.js 9742 &
- Verify it is running:
curl -s http://localhost:9742/health
If port 9742 is taken, use a different port and update all fetch URLs accordingly.
Step 5: Instrument Code
Add fetch() calls to the source files at strategic locations. Every call must include:
hypothesisId: which hypothesis this point tests ("H1", "H2", etc.)
location: "functionName:entry", "functionName:exit", "functionName:error", etc.
message: what this data point tells us
data: the relevant runtime values at this point
Use this pattern:
fetch("http://localhost:9742/debug", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ hypothesisId: "H1", location: "processOrder:entry", message: "checking initial state", data: { orderId, userId } }) }).catch(() => {});
Place instrumentation at:
- Entry/exit of suspected functions
- Branch points that distinguish hypotheses
- Error catch blocks
- State mutations (before/after values)
Track which files you modify — you will need to remove all instrumentation in Step 9.
Tell the user exactly how to reproduce. Be specific:
"I've added instrumentation for H1, H2, H3. Please reproduce the bug now, then let me know when done."
Step 6: Analyze Logs
Once user confirms they have reproduced the bug, read the log:
cat .debug-mode/debug.log
Evaluate each hypothesis against the evidence:
H1: [theory] → CONFIRMED / REJECTED — [one sentence of evidence from logs]
H2: [theory] → CONFIRMED / REJECTED — [one sentence of evidence from logs]
H3: [theory] → CONFIRMED / REJECTED — [one sentence of evidence from logs]
Present the evaluation to the user.
If no hypothesis is confirmed and the evidence is inconclusive:
- Generate additional hypotheses based on what the logs revealed
- Return to Step 5 to add more targeted instrumentation
Step 7: Apply Fix
For the CONFIRMED hypothesis, implement the minimal fix:
- Keep it small — typically 2-3 lines
- Show exactly what was changed (file, line, change)
Do NOT remove instrumentation yet. Leave all fetch() calls in place — logs are the only truth, and the fix must be verified through them.
Step 8: Verify Fix
Clear the log so the next reproduction is clean:
> .debug-mode/debug.log
Ask user to reproduce the bug again with the fix applied.
Read the log:
cat .debug-mode/debug.log
Evaluate the log evidence:
If the log confirms the bug is fixed (expected values flow correctly, error path no longer triggered, etc.):
- Proceed to Step 9 (cleanup)
If the log shows the bug persists:
- Return to Step 5 to add more instrumentation targeting remaining hypotheses
- Or return to Step 3 to generate additional hypotheses based on new evidence
Step 9: Cleanup
Only remove instrumentation after the logs have confirmed the fix works.
Remove all instrumentation from every source file modified in Step 5. Check each file and remove every fetch("http://localhost:9742/debug", ...) call that was added.
Stop the debug server:
pkill -f ".debug-mode/server.js" 2>/dev/null
Remove the debug directory:
rm -rf .debug-mode/
Optionally remove the .debug-mode/ line from .gitignore if you added it.
Confirm to user:
"Fix verified via logs. All instrumentation removed, debug server stopped, .debug-mode/ cleaned up."
Reference
Debug Server
The debug server ships as server.js alongside this skill file. The server listens for POST requests on /debug and appends structured log lines to .debug-mode/debug.log.
Log Format
Each POST to /debug accepts a JSON body:
{
"hypothesisId": "H1",
"location": "processOrder:entry",
"message": "checking initial state",
"data": { "orderId": 123, "status": "pending" }
}
The server writes log lines as:
[2026-02-26T10:00:01.234Z] [H1] processOrder:entry | checking initial state | {"orderId":123,"status":"pending"}
Instrumentation Placement Guidelines
- Function entry/exit — Capture arguments on entry, return values on exit
- Branch points — Log which branch was taken to distinguish hypotheses
- Error catch blocks — Capture error details
- State mutations — Log before/after values
- Async boundaries — Log before await and after resolution
Label Conventions
| Pattern | Example | Use For |
|---|
fn-name:entry | processOrder:entry | Function start |
fn-name:exit | processOrder:exit | Function end |
fn-name:error | processOrder:error | Caught errors |
fn-name:state | processOrder:state | State snapshots |
fn-name:branch | processOrder:branch-early-return | Control flow |
Hypothesis Example
H1: The discount is applied after tax calculation instead of before
H2: The coupon validation returns stale cached data
H3: Concurrent cart updates cause a race condition on the total
Evaluation Example
H1: The discount is applied after tax calculation → REJECTED — logs show discount applied at line 45 before tax calc at line 72
H2: The coupon validation returns stale cached data → CONFIRMED — cache key "coupon:SAVE10" returns expired entry from 2h ago
H3: Concurrent cart updates cause a race condition → REJECTED — no concurrent access detected in logs