debug について
debugは、開発者やコーディングエージェント向けのソフトウェアデバッグスキルです。推測ではなく実行時ログを使い、仮説の設定から計測、再現、分析、修正、検証までを進めます。
主な機能
- 01仮説に基づくデバッグ
- 02Node.jsログサーバーとセッション
- 03JavaScript・Pythonの計測手順
- 04ログによる仮説の評価
- 05証拠に基づく修正検証
- 06ブラウザー・拡張機能のログ対処
活用シーン
- 01null値や型の誤りの追跡
- 02条件分岐や非同期処理の調査
- 03操作フローの不具合の再現と分析
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ vltansky/ debug-skill --skill debugインストール後、Claude Code で/debugを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
package.jsonREADME.md.claude-plugin/marketplace.jsonscripts/debug_cleanup.jsdebug_server.js詳細
デバッグモード
推測ではなく、実行時の証拠に基づいてバグを修正します。
Don't guess → Hypothesize → Instrument → Reproduce → Analyze → Fix → Verify
使用するタイミング
トリガーとなる兆候(次のいずれかを行おうとしている場合は、代わりにこのスキルを使用してください):
- 「DevTools のコンソールを開いて、……を確認してください」
- 「バグを再現して、何が表示されるか教えてください」
- 「console.log を追加して、出力を教えてください」
- 「X をクリックし、Y を開いて、Z がコンソールに表示されるか確認してください」
このスキルを使用すべきシナリオの例:
❌ 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
次のデバッグに使用します:
- 状態/値の問題(null、undefined、型の誤り)
- 条件分岐のロジック(どの分岐が実行されたか)
- 非同期処理のタイミング(競合状態、読み込み順序)
- ユーザー操作のフロー(モーダル、フォーム、クリック)
引数
/debug /path/to/project
パスが指定されていない場合は、現在の作業ディレクトリを使用してください。
ワークフロー
フェーズ 1:ログサーバーを起動する
ステップ 1:サーバーが実行中であることを確認する(必要に応じて起動し、すでに実行中の場合は何もしません):
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 を保存してください - 以降のすべての手順で使用します。
サーバーのエンドポイント:
- POST
/sessionに{"name": "description"}を送信 → セッションを作成し、{session_id, log_file}を返します - POST
/logに{"sessionId": "...", "msg": "..."}を送信 → ログファイルに書き込みます - GET
/→ ステータスとログディレクトリを返します
ポート 8787 が使用中の場合: lsof -ti :8787 | xargs kill -9 を実行してから再起動します
──────────
フェーズ 2:仮説を立てる
計測を追加する前に、具体的な仮説を 3~5 個立てます:
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
各仮説は次の条件を満たす必要があります:
- 具体的である(「何かがおかしい」ではない)
- 検証可能である(ログで確認または棄却できる)
- 異なるサブシステムを対象とする(特定の領域に偏らせない)
──────────
フェーズ 3:コードに計測を追加する
すべての仮説を検証するため、ログ呼び出しを追加します。
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')
ガイドライン:
- 計測ポイントは 3~8 か所
- 次を網羅します:開始/終了、重要な操作の前後、分岐経路
- 各ログに
hypothesisIdを付けます // #region debug...// #endregionで囲みます- 高頻度のイベント(mousemove、scroll):状態が変化したときのみログを記録します
- 意図と結果の両方を記録します
──────────
フェーズ 4:ログをクリアして再現する
-
ログをクリアします:
: > /path/to/project/.debug/debug-$SESSION_ID.log -
再現手順を提示します:
<reproduction_steps> 1. Start app: yarn dev 2. Navigate to /users 3. Click "Calculate Score" 4. Observe NaN displayed </reproduction_steps> -
ユーザーがバグを再現します
──────────
フェーズ 5:ログを分析する
ログを読み取り、評価します:
cat /path/to/project/.debug/debug-$SESSION_ID.log
各仮説について:
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"}}
ステータスの選択肢:
- CONFIRMED:ログで証明されています
- REJECTED:ログで否定されています
- INCONCLUSIVE:さらに計測が必要です
すべてが INCONCLUSIVE/REJECTED の場合:新しい仮説を立て、ログを追加して、繰り返します。
──────────
フェーズ 6:修正する
ログで根本原因が確認できた場合にのみ修正します。
計測は有効なままにしておきます(まだ削除しないでください)。
検証用ログに runId: "post-fix" を付けます:
debugLog('Function entry', { userId, runId: 'post-fix' }, 'H1');
──────────
フェーズ 7:検証する
- ログをクリアします
- ユーザーが再現します(バグが解消されているはずです)
- 修正前後を比較します:
Before: {"data":{"userId":null},"runId":"run1"} After: {"data":{"userId":5},"runId":"post-fix"} - ログの証拠で確認します
まだ解決していない場合:新しい仮説を立て、ログを追加して、繰り返します。
──────────
フェーズ 8:5 つのなぜ(任意)
実施するタイミング: 繰り返し発生するバグ、本番環境でのインシデント、セキュリティ上の問題、または「何度も起きる」場合です。
修正後、「なぜこのバグが存在したのか?」と問い、根本的な原因を見つけます:
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)
カテゴリ:
| 種類 | 対応 |
|---|---|
| CODE | すぐに修正する |
| TEST | テストを追加する |
| PROCESS | チェックリスト/レビューを更新する |
| SYSTEMIC | パターンを文書化する |
省略する場合: 単発の単純なバグ、影響が小さい場合、繰り返し発生していない場合。
──────────
フェーズ 9:クリーンアップする
次の条件を満たした後にのみ、計測を削除します:
- 修正後のログで成功が証明されている
- ユーザーが解決を確認している
#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)とファイアウォールを確認します |
| ログファイルが違う | セッション ID が一致していることを確認します |
| ログが多すぎる | hypothesisId で絞り込み、状態変化のログを使用します |
| 再現できない | ユーザーに正確な手順を尋ね、環境を確認します |
CORS/混在コンテンツへの対処方法
ログが届かない場合、通常は次のいずれかが原因です。
- 混在コンテンツ:HTTPS のアプリから
http://localhost:8787へのアクセスはブロックされます。同一オリジンの開発サーバープロキシを使用するか、ログエンドポイントを HTTPS で配信します。 - CSP:
connect-srcがログ URL をブロックしています。開発サーバープロキシを使用するか、CSP を更新します。 - CORS プリフライト:
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. 最終手段(ローカルのみ) - ブラウザーの設定で安全でないコンテンツを許可するか、混在コンテンツのブロックを無効にします
Chrome 拡張機能のデバッグ
コンテンツスクリプトは厳格な 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
これが機能する理由:
- バックグラウンドスクリプト(サービスワーカー)は緩和された CSP を持ち、localhost にフェッチできます
chrome.runtime.sendMessageはコンテンツスクリプトとバックグラウンドの間をつなぐブリッジです- 後から簡単に削除できるよう、両方のデバッグ領域にタグを付けたままにします
挿入スクリプト(MAIN ワールド):
<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 });
}
});
チェックリスト
- サーバーが実行中(起動済み、または already_running)
-
POST /sessionでセッションを作成し、返されたsession_idを保存 - 3~5 個の仮説を立てた
- hypothesisId タグ付きのログを 3~8 個追加した
- 再現前にログを消去した
- 再現手順が提供された
- 各仮説を評価した(確認済み/否定/判定不能)
- 証拠のみに基づいて修正した
- 修正前と修正後を比較した
- 確認後に計測コードを削除した