skills/debug-mode/SKILL.md
This skill should be used when debugging frontend/UI bugs that need runtime evidence. USE THIS SKILL (instead of adding console.log) when you're about to say "add console.log and ask user to check", "open DevTools and tell me what you see", "reproduce the bug and share the output", "check the browser console". Triggers: "debug this", "fix this bug", "why isn't this working", "investigate this issue", "trace the problem", "figure out why X happens", "UI not updating", "state is wrong", "value is null/undefined", "click doesn't work", "modal not showing". Automates log collection server-side - you read logs directly, no user copy-paste needed.
npx skillsauth add vltansky/skills debug-modeInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Fix bugs with runtime evidence, not guesses.
Don't guess → Hypothesize → Instrument → Reproduce → Analyze → Fix → Verify
Trigger signals (if you're about to do any of these, use this skill instead):
Example scenario that should trigger this skill:
❌ Without skill (manual, slow):
"I added debug logging. Please:
1. Open the app in browser
2. Open DevTools Console (F12)
3. Open the defect modal and select a defect
4. Check console for [DEBUG] logs
5. Tell me what you see"
✅ With skill (automated):
Logs are captured server-side → you read them directly → no user copy-paste needed
Use when debugging:
/debug /path/to/project
If no path provided, use current working directory.
Step 1: Ensure server is running (starts if needed, no-op if already running):
node skills/debug/scripts/debug_server.js /path/to/project &
Server outputs JSON:
{"status":"started",...} - new server started{"status":"already_running",...} - server was already running (this is fine!)Step 2: Create session (server generates unique ID from your description):
curl -s -X POST http://localhost:8787/session -d '{"name":"fix-null-userid"}'
Response:
{"session_id":"fix-null-userid-a1b2c3","log_file":"/path/to/project/.debug/debug-fix-null-userid-a1b2c3.log"}
Save the session_id from the response - use it in all subsequent steps.
Server endpoints:
/session with {"name": "description"} → creates session, returns {session_id, log_file}/log with {"sessionId": "...", "msg": "..."} → writes to log file/ → returns status and log directoryIf port 8787 busy: lsof -ti :8787 | xargs kill -9 then restart
──────────
Before instrumenting, generate 3-5 specific hypotheses:
Hypothesis H1: userId is null when passed to calculateScore()
Expected: number (e.g., 5)
Actual: null
Test: Log userId at function entry
Hypothesis H2: score is string instead of number
Expected: 85 (number)
Actual: "85" (string)
Test: Log typeof score
Each hypothesis must be:
──────────
Add logging calls to test all hypotheses.
JavaScript/TypeScript:
// #region debug
const SESSION_ID = 'REPLACE_WITH_SESSION_ID'; // e.g. 'fix-null-userid-a1b2c3'
const DEBUG_LOG_URL = 'http://localhost:8787/log';
const debugLog = (msg, data = {}, hypothesisId = null) => {
const payload = JSON.stringify({
sessionId: SESSION_ID,
msg,
data,
hypothesisId,
loc: new Error().stack?.split('\n')[2],
});
if (navigator.sendBeacon?.(DEBUG_LOG_URL, payload)) return;
fetch(DEBUG_LOG_URL, { method: 'POST', body: payload }).catch(() => {});
};
// #endregion
// Usage
debugLog('Function entry', { userId, score, typeScore: typeof score }, 'H1,H2');
Python:
# #region debug
import requests, traceback
SESSION_ID = 'REPLACE_WITH_SESSION_ID' # e.g. 'fix-null-userid-a1b2c3'
def debug_log(msg, data=None, hypothesis_id=None):
try:
requests.post('http://localhost:8787/log', json={
'sessionId': SESSION_ID, 'msg': msg, 'data': data,
'hypothesisId': hypothesis_id, 'loc': traceback.format_stack()[-2].strip()
}, timeout=0.5)
except: pass
# #endregion
# Usage
debug_log('Function entry', {'user_id': user_id, 'type': type(user_id)}, 'H1')
Guidelines:
hypothesisId// #region debug ... // #endregion──────────
Clear logs:
: > /path/to/project/.debug/debug-$SESSION_ID.log
Provide reproduction steps:
<reproduction_steps>
1. Start app: yarn dev
2. Navigate to /users
3. Click "Calculate Score"
4. Observe NaN displayed
</reproduction_steps>
User reproduces bug
──────────
Read and evaluate:
cat /path/to/project/.debug/debug-$SESSION_ID.log
For each hypothesis:
Hypothesis H1: userId is null
Status: CONFIRMED
Evidence: {"msg":"Function entry","data":{"userId":null}}
Hypothesis H2: score is string
Status: REJECTED
Evidence: {"data":{"typeScore":"number"}}
Status options:
If all INCONCLUSIVE/REJECTED: Generate new hypotheses, add more logs, iterate.
──────────
Only fix when logs confirm root cause.
Keep instrumentation active (don't remove yet).
Tag verification logs with runId: "post-fix":
debugLog('Function entry', { userId, runId: 'post-fix' }, 'H1');
──────────
Before: {"data":{"userId":null},"runId":"run1"}
After: {"data":{"userId":5},"runId":"post-fix"}
If still broken: New hypotheses, more logs, iterate.
──────────
When to run: Recurring bug, prod incident, security issue, or "this keeps happening".
After fixing, ask "Why did this bug exist?" to find systemic causes:
Bug: API returns NaN
Why 1: userId was null → Code fix: null check
Why 2: No input validation → Add validation
Why 3: No test for null case → Add test
Why 4: Review didn't catch → (one-off, acceptable)
Categories: | Type | Action | |------|--------| | CODE | Fix immediately | | TEST | Add test | | PROCESS | Update checklist/review | | SYSTEMIC | Document patterns |
Skip if: Simple one-off bug, low impact, not recurring.
──────────
Remove instrumentation only after:
Search for #region debug and remove all debug code.
Each line is NDJSON:
{"ts":"2024-01-03T12:00:00.000Z","msg":"Button clicked","data":{"id":5},"hypothesisId":"H1","loc":"app.js:42"}
| Issue | Solution |
|-------|----------|
| Server won't start | Check port 8787 not in use: lsof -i :8787 |
| Logs empty | Check browser blocks (mixed content/CSP/CORS), firewall |
| Wrong log file | Verify session ID matches |
| Too many logs | Filter by hypothesisId, use state-change logging |
| Can't reproduce | Ask user for exact steps, check environment |
If logs aren't arriving, it’s usually one of:
http://localhost:8787 is blocked. Use a dev-server proxy (same origin) or serve the log endpoint over HTTPS.connect-src blocks the log URL. Use a dev-server proxy or update CSP.Content-Type: application/json triggers OPTIONS. Use a “simple” request (text/plain) or sendBeacon.1. sendBeacon (avoids preflight; fire-and-forget):
const DEBUG_LOG_URL = 'http://localhost:8787/log';
const debugLog = (msg, data = {}, hypothesisId = null) => {
const payload = JSON.stringify({ sessionId: SESSION_ID, msg, data, hypothesisId });
if (navigator.sendBeacon?.(DEBUG_LOG_URL, payload)) return;
fetch(DEBUG_LOG_URL, { method: 'POST', body: payload }).catch(() => {});
};
Note: still blocked by mixed content + CSP.
2. Dev server proxy (Vite example) - same-origin /__log → http://localhost:8787/log:
// vite.config.js
export default {
server: {
proxy: {
'/__log': {
target: 'http://localhost:8787',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/__log/, '/log'),
},
},
},
};
// Then POST to /__log instead of localhost:8787/log
3. Last resort (local only) - allow insecure content / disable mixed-content blocking in browser settings
Content scripts run in an isolated world with strict CSP - they cannot directly fetch to localhost:8787. The solution is to relay logs through the background script (service worker).
Content Script (sender):
// #region debug
const DEBUG_SESSION_ID = 'your-session-id-here';
const debugLog = (msg, data = {}, hypothesisId = null) => {
chrome.runtime.sendMessage({
type: 'DEBUG_LOG',
payload: {
sessionId: DEBUG_SESSION_ID,
msg,
data,
hypothesisId,
loc: new Error().stack?.split('\n')[2]?.trim(),
},
}).catch(() => {});
};
// #endregion
// Usage
debugLog('handleMouseMove', { target: target.tagName, rect }, 'H1');
Background Script (relay):
// #region debug - relay logs to debug server
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.type === 'DEBUG_LOG') {
fetch('http://localhost:8787/log', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(message.payload),
}).catch(() => {});
sendResponse({ ok: true });
return true;
}
});
// #endregion
Why this works:
chrome.runtime.sendMessage is the bridge between content script and backgroundInjected scripts (MAIN world):
If debugging code injected via <script> into the page context, use window.postMessage to relay to content script, which then relays to background:
// In MAIN world (injected script)
window.postMessage({ type: 'DEBUG_LOG_RELAY', payload: { ... } }, '*');
// In content script
window.addEventListener('message', (e) => {
if (e.data?.type === 'DEBUG_LOG_RELAY') {
chrome.runtime.sendMessage({ type: 'DEBUG_LOG', payload: e.data.payload });
}
});
POST /session - save the returned session_idtools
Prepare a Hetzner Cloud VPS for secure Codex remote SSH access. Use when the user wants to create or configure a Hetzner server for Codex remote control, fix "No codex found in PATH" on a remote machine, install agent development tooling on a VPS, harden SSH access to a Hetzner server, or connect the server through Codex Settings, Connections, Add SSH.
data-ai
Summarize your GitHub activity from the last 24 hours across all repos. Use when user says "what did I do", "my activity", "standup", "recap", "summarize my day", "what-i-did", "git activity", "daily summary".
development
Test-driven development loop. Write failing test first, then implement to make it pass. Use when the user says 'tdd', 'test first', 'write the test first', 'failing test', 'red green refactor', or for any bug fix where the fix should be proven by a test. Also use when autopilot or other skills need test-first execution.
development
Review changed code for reuse, quality, and efficiency, then fix any issues found. Use when the user says "simplify", "simplify this", "review changes", "clean up my code", "check for duplicates", "code reuse review", or wants a post-change quality sweep.