npx skills add https://github.com/vltansky/debug-skill --skill debug基于运行时证据修复错误,而非猜测。
不要猜测 → 假设 → 插桩 → 复现 → 分析 → 修复 → 验证
触发信号(如果你即将进行以下任何操作,请使用此技能替代):
应触发此技能的示例场景:
❌ 不使用技能(手动,缓慢):
"我添加了调试日志。请:
1. 在浏览器中打开应用
2. 打开 DevTools 控制台 (F12)
3. 打开缺陷模态框并选择一个缺陷
4. 在控制台中检查 [DEBUG] 日志
5. 告诉我你看到了什么"
✅ 使用技能(自动化):
日志在服务器端捕获 → 你直接读取 → 无需用户复制粘贴
在调试以下问题时使用:
/debug /path/to/project
如果未提供路径,则使用当前工作目录。
步骤 1:确保服务器正在运行(如果需要则启动,如果已在运行则无操作):
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
node skills/debug/scripts/debug_server.js /path/to/project &
服务器输出 JSON:
{"status":"started",...} - 新服务器已启动{"status":"already_running",...} - 服务器已在运行(这没问题!)步骤 2:创建会话(服务器根据你的描述生成唯一 ID):
curl -s -X POST http://localhost:8787/session -d '{"name":"fix-null-userid"}'
响应:
{"session_id":"fix-null-userid-a1b2c3","log_file":"/path/to/project/.debug/debug-fix-null-userid-a1b2c3.log"}
保存响应中的 session_id - 在后续所有步骤中使用它。
服务器端点:
/session 附带 {"name": "description"} → 创建会话,返回 {session_id, log_file}/log 附带 {"sessionId": "...", "msg": "..."} → 写入日志文件/ → 返回状态和日志目录如果端口 8787 被占用: lsof -ti :8787 | xargs kill -9 然后重启
──────────
在插桩之前,生成 3-5 个具体的假设:
假设 H1:userId 在传递给 calculateScore() 时为 null
预期:数字(例如,5)
实际:null
测试:在函数入口记录 userId
假设 H2:score 是字符串而不是数字
预期:85(数字)
实际:"85"(字符串)
测试:记录 typeof score
每个假设必须:
──────────
添加日志调用来测试所有假设。
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')
指南:
hypothesisId 标记每条日志// #region debug ... // #endregion 包裹──────────
清除日志:
: > /path/to/project/.debug/debug-$SESSION_ID.log
提供复现步骤:
<reproduction_steps>
1. 启动应用:yarn dev
2. 导航到 /users
3. 点击 "Calculate Score"
4. 观察到显示 NaN
</reproduction_steps>
用户复现错误
──────────
读取并评估:
cat /path/to/project/.debug/debug-$SESSION_ID.log
对于每个假设:
假设 H1:userId 是 null
状态:已确认
证据:{"msg":"Function entry","data":{"userId":null}}
假设 H2:score 是字符串
状态:已拒绝
证据:{"data":{"typeScore":"number"}}
状态选项:
如果全部为不确定/已拒绝:生成新的假设,添加更多日志,迭代。
──────────
仅当日志确认根本原因时才进行修复。
保持插桩处于活动状态(暂时不要移除)。
用 runId: "post-fix" 标记验证日志:
debugLog('Function entry', { userId, runId: 'post-fix' }, 'H1');
──────────
清除日志
用户复现(错误应该消失)
比较修复前/后:
Before: {"data":{"userId":null},"runId":"run1"}
After: {"data":{"userId":5},"runId":"post-fix"}
用日志证据确认
如果仍然有问题:新的假设,更多日志,迭代。
──────────
何时运行:反复出现的错误、生产事故、安全问题或"这种情况不断发生"。
修复后,询问"为什么存在这个错误?"以找出系统性原因:
错误:API 返回 NaN
为什么 1:userId 是 null → 代码修复:空值检查
为什么 2:没有输入验证 → 添加验证
为什么 3:没有针对 null 情况的测试 → 添加测试
为什么 4:审查未发现 →(一次性,可接受)
类别:
| 类型 | 操作 |
|---|---|
| 代码 | 立即修复 |
| 测试 | 添加测试 |
| 流程 | 更新检查清单/审查 |
| 系统性 | 记录模式 |
跳过如果:简单的一次性错误,影响小,不反复出现。
──────────
仅在以下情况下移除插桩:
搜索 #region debug 并移除所有调试代码。
每行是 NDJSON:
{"ts":"2024-01-03T12:00:00.000Z","msg":"Button clicked","data":{"id":5},"hypothesisId":"H1","loc":"app.js:42"}
| 问题 | 解决方案 |
|---|---|
| 服务器无法启动 | 检查端口 8787 未被占用:lsof -i :8787 |
| 日志为空 | 检查浏览器阻止(混合内容/CSP/CORS)、防火墙 |
| 错误的日志文件 | 验证 session ID 是否匹配 |
| 日志过多 | 按 hypothesisId 过滤,使用状态变更日志记录 |
| 无法复现 | 向用户询问确切步骤,检查环境 |
如果日志未到达,通常是以下原因之一:
http://localhost:8787 被阻止。使用开发服务器代理(同源)或通过 HTTPS 提供日志端点。connect-src 阻止了日志 URL。使用开发服务器代理或更新 CSP。Content-Type: application/json 触发 OPTIONS。使用"简单"请求(text/plain)或 sendBeacon。1. sendBeacon(避免预检;发送即忘):
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(() => {});
};
注意:仍然会被混合内容和 CSP 阻止。
2. 开发服务器代理(Vite 示例) - 同源 /__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. 最后手段(仅限本地) - 在浏览器设置中允许不安全内容 / 禁用混合内容阻止
内容脚本在具有严格 CSP 的隔离世界中运行 - 它们无法直接获取 localhost:8787。解决方案是通过后台脚本(服务工作者)中继日志。
内容脚本(发送者):
// #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');
后台脚本(中继):
// #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
为什么这可行:
chrome.runtime.sendMessage 是内容脚本和后台之间的桥梁注入脚本(主世界): 如果调试代码通过 <script> 注入到页面上下文中,使用 window.postMessage 中继到内容脚本,然后内容脚本再中继到后台:
// 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 创建会话 - 保存返回的 session_id每周安装次数
108
仓库
GitHub 星标数
10
首次出现
2026年1月20日
安全审计
安装于
opencode99
codex96
gemini-cli94
cursor94
github-copilot92
amp89
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 thesession_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');
──────────
Clear logs
User reproduces (bug should be gone)
Compare before/after:
Before: {"data":{"userId":null},"runId":"run1"}
After: {"data":{"userId":5},"runId":"post-fix"}
Confirm with log evidence
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_idWeekly Installs
108
Repository
GitHub Stars
10
First Seen
Jan 20, 2026
Security Audits
Gen Agent Trust HubFailSocketPassSnykWarn
Installed on
opencode99
codex96
gemini-cli94
cursor94
github-copilot92
amp89
agent-browser 浏览器自动化工具 - Vercel Labs 命令行网页操作与测试
159,700 周安装