litclaude-ai 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +155 -0
- package/LICENSE +21 -0
- package/README.md +369 -0
- package/README_ko-KR.md +374 -0
- package/RELEASE_CHECKLIST.md +165 -0
- package/bin/litclaude-ai.js +643 -0
- package/cover.png +0 -0
- package/docs/agents.md +67 -0
- package/docs/hooks.md +134 -0
- package/docs/lsp.md +40 -0
- package/docs/migration.md +209 -0
- package/docs/workflow-compatibility-audit.md +119 -0
- package/generate_cover.py +123 -0
- package/package.json +48 -0
- package/plugins/litclaude/.claude-plugin/plugin.json +25 -0
- package/plugins/litclaude/.lsp.json +13 -0
- package/plugins/litclaude/.mcp.json +9 -0
- package/plugins/litclaude/agents/boulder-executor.md +12 -0
- package/plugins/litclaude/agents/librarian-researcher.md +15 -0
- package/plugins/litclaude/agents/oracle-verifier.md +16 -0
- package/plugins/litclaude/agents/prometheus-planner.md +13 -0
- package/plugins/litclaude/agents/qa-runner.md +16 -0
- package/plugins/litclaude/agents/quality-reviewer.md +17 -0
- package/plugins/litclaude/bin/litclaude-hook.js +110 -0
- package/plugins/litclaude/bin/litclaude-hud.js +271 -0
- package/plugins/litclaude/bin/litclaude-lsp-doctor.js +15 -0
- package/plugins/litclaude/bin/litclaude-mcp.js +70 -0
- package/plugins/litclaude/commands/deep-interview.md +21 -0
- package/plugins/litclaude/commands/dynamic-workflow.md +36 -0
- package/plugins/litclaude/commands/lit-loop.md +40 -0
- package/plugins/litclaude/commands/lit-plan.md +35 -0
- package/plugins/litclaude/commands/litgoal.md +30 -0
- package/plugins/litclaude/commands/review-work.md +35 -0
- package/plugins/litclaude/commands/start-work.md +36 -0
- package/plugins/litclaude/hooks/hooks.json +54 -0
- package/plugins/litclaude/lib/context-pressure.mjs +25 -0
- package/plugins/litclaude/lib/hud-accent-palette.mjs +58 -0
- package/plugins/litclaude/lib/litgoal/cli.mjs +266 -0
- package/plugins/litclaude/lib/litgoal/ledger.mjs +16 -0
- package/plugins/litclaude/lib/litgoal/paths.mjs +7 -0
- package/plugins/litclaude/lib/litgoal/state.mjs +67 -0
- package/plugins/litclaude/lib/mutated-file-paths.mjs +63 -0
- package/plugins/litclaude/lib/start-work-continuation.mjs +99 -0
- package/plugins/litclaude/lib/workflow-check.mjs +83 -0
- package/plugins/litclaude/skills/ai-slop-remover/SKILL.md +142 -0
- package/plugins/litclaude/skills/comment-checker/SKILL.md +55 -0
- package/plugins/litclaude/skills/debugging/SKILL.md +70 -0
- package/plugins/litclaude/skills/debugging/references/methodology/00-setup.md +108 -0
- package/plugins/litclaude/skills/debugging/references/methodology/02-investigate.md +126 -0
- package/plugins/litclaude/skills/debugging/references/methodology/04-oracle-triple.md +106 -0
- package/plugins/litclaude/skills/debugging/references/methodology/05-escalate.md +69 -0
- package/plugins/litclaude/skills/debugging/references/methodology/06-fix.md +116 -0
- package/plugins/litclaude/skills/debugging/references/methodology/08-qa.md +94 -0
- package/plugins/litclaude/skills/debugging/references/methodology/09-cleanup.md +164 -0
- package/plugins/litclaude/skills/debugging/references/methodology/partial-runtime-evidence.md +228 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/bundled-js-binary.md +415 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/go.md +252 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/native-binary.md +484 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/node.md +260 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/python.md +248 -0
- package/plugins/litclaude/skills/debugging/references/runtimes/rust.md +234 -0
- package/plugins/litclaude/skills/debugging/references/tools/ghidra.md +212 -0
- package/plugins/litclaude/skills/debugging/references/tools/playwright-cli.md +194 -0
- package/plugins/litclaude/skills/debugging/references/tools/pwndbg.md +263 -0
- package/plugins/litclaude/skills/debugging/references/tools/pwntools.md +265 -0
- package/plugins/litclaude/skills/deep-interview/SKILL.md +323 -0
- package/plugins/litclaude/skills/deep-interview/scripts/render_progress.py +193 -0
- package/plugins/litclaude/skills/frontend-ui-ux/SKILL.md +62 -0
- package/plugins/litclaude/skills/lit-loop/SKILL.md +144 -0
- package/plugins/litclaude/skills/lit-plan/SKILL.md +125 -0
- package/plugins/litclaude/skills/litgoal/SKILL.md +219 -0
- package/plugins/litclaude/skills/lsp/SKILL.md +63 -0
- package/plugins/litclaude/skills/programming/SKILL.md +106 -0
- package/plugins/litclaude/skills/programming/references/go/README.md +90 -0
- package/plugins/litclaude/skills/programming/references/go/backend-stack.md +641 -0
- package/plugins/litclaude/skills/programming/references/go/bootstrap.md +328 -0
- package/plugins/litclaude/skills/programming/references/go/bubbletea-v2.md +360 -0
- package/plugins/litclaude/skills/programming/references/go/cobra-stack.md +468 -0
- package/plugins/litclaude/skills/programming/references/go/concurrency.md +362 -0
- package/plugins/litclaude/skills/programming/references/go/data-modeling.md +329 -0
- package/plugins/litclaude/skills/programming/references/go/error-handling.md +359 -0
- package/plugins/litclaude/skills/programming/references/go/golangci-strict.md +236 -0
- package/plugins/litclaude/skills/programming/references/go/grpc-connect.md +375 -0
- package/plugins/litclaude/skills/programming/references/go/libraries.md +337 -0
- package/plugins/litclaude/skills/programming/references/go/one-liners.md +202 -0
- package/plugins/litclaude/skills/programming/references/go/sqlc-pgx.md +471 -0
- package/plugins/litclaude/skills/programming/references/go/testing.md +467 -0
- package/plugins/litclaude/skills/programming/references/go/type-patterns.md +298 -0
- package/plugins/litclaude/skills/programming/references/python/README.md +314 -0
- package/plugins/litclaude/skills/programming/references/python/async-anyio.md +442 -0
- package/plugins/litclaude/skills/programming/references/python/data-modeling.md +233 -0
- package/plugins/litclaude/skills/programming/references/python/data-processing.md +133 -0
- package/plugins/litclaude/skills/programming/references/python/error-handling.md +218 -0
- package/plugins/litclaude/skills/programming/references/python/fastapi-stack.md +316 -0
- package/plugins/litclaude/skills/programming/references/python/httpx2-optimization.md +360 -0
- package/plugins/litclaude/skills/programming/references/python/libraries.md +307 -0
- package/plugins/litclaude/skills/programming/references/python/one-liners.md +268 -0
- package/plugins/litclaude/skills/programming/references/python/orjson-stack.md +378 -0
- package/plugins/litclaude/skills/programming/references/python/pydantic-ai.md +285 -0
- package/plugins/litclaude/skills/programming/references/python/pyproject-strict.md +232 -0
- package/plugins/litclaude/skills/programming/references/python/textual-tui.md +201 -0
- package/plugins/litclaude/skills/programming/references/python/type-patterns.md +176 -0
- package/plugins/litclaude/skills/programming/references/rust/README.md +317 -0
- package/plugins/litclaude/skills/programming/references/rust/async-tokio.md +299 -0
- package/plugins/litclaude/skills/programming/references/rust/axum-stack.md +467 -0
- package/plugins/litclaude/skills/programming/references/rust/cargo-strict.md +317 -0
- package/plugins/litclaude/skills/programming/references/rust/clap-stack.md +409 -0
- package/plugins/litclaude/skills/programming/references/rust/concurrency.md +375 -0
- package/plugins/litclaude/skills/programming/references/rust/libraries.md +439 -0
- package/plugins/litclaude/skills/programming/references/rust/one-liners.md +291 -0
- package/plugins/litclaude/skills/programming/references/rust/proptest-insta.md +429 -0
- package/plugins/litclaude/skills/programming/references/rust/type-state.md +354 -0
- package/plugins/litclaude/skills/programming/references/rust/unsafe-discipline.md +250 -0
- package/plugins/litclaude/skills/programming/references/rust/zero-cost-safety.md +527 -0
- package/plugins/litclaude/skills/programming/references/rust-ub/README.md +289 -0
- package/plugins/litclaude/skills/programming/references/rust-ub/miri-sanitizers-loom.md +411 -0
- package/plugins/litclaude/skills/programming/references/rust-ub/ub-taxonomy.md +269 -0
- package/plugins/litclaude/skills/programming/references/typescript/README.md +195 -0
- package/plugins/litclaude/skills/programming/references/typescript/backend-hono.md +672 -0
- package/plugins/litclaude/skills/programming/references/typescript/bootstrap.md +199 -0
- package/plugins/litclaude/skills/programming/references/typescript/data-modeling.md +202 -0
- package/plugins/litclaude/skills/programming/references/typescript/error-handling.md +169 -0
- package/plugins/litclaude/skills/programming/references/typescript/tsconfig-strict.md +152 -0
- package/plugins/litclaude/skills/programming/references/typescript/type-patterns.md +196 -0
- package/plugins/litclaude/skills/programming/scripts/go/check-no-excuse-rules.sh +173 -0
- package/plugins/litclaude/skills/programming/scripts/go/new-project.py +138 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/.editorconfig +13 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/.golangci.yml +95 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/AGENTS.md.tmpl +24 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/README.md.tmpl +12 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/Taskfile.yml +40 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/ci.yml +37 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/config.go +24 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/gitignore +15 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/main.go.tmpl +22 -0
- package/plugins/litclaude/skills/programming/scripts/go/templates/run.go +15 -0
- package/plugins/litclaude/skills/programming/scripts/python/check-no-excuse-rules.py +687 -0
- package/plugins/litclaude/skills/programming/scripts/python/new-project.py +172 -0
- package/plugins/litclaude/skills/programming/scripts/python/new-script.py +116 -0
- package/plugins/litclaude/skills/programming/scripts/rust/check-no-excuse-rules.py +296 -0
- package/plugins/litclaude/skills/programming/scripts/rust/check-no-excuse-rules.sh +158 -0
- package/plugins/litclaude/skills/programming/scripts/rust/new-project.py +175 -0
- package/plugins/litclaude/skills/programming/scripts/typescript/check-no-excuse-rules.ts +282 -0
- package/plugins/litclaude/skills/programming/scripts/typescript/new-project.ts +177 -0
- package/plugins/litclaude/skills/refactor/SKILL.md +73 -0
- package/plugins/litclaude/skills/remove-ai-slops/SKILL.md +52 -0
- package/plugins/litclaude/skills/review-work/SKILL.md +331 -0
- package/plugins/litclaude/skills/rules/SKILL.md +66 -0
- package/plugins/litclaude/skills/start-work/SKILL.md +132 -0
- package/scripts/audit-plan-checkboxes.mjs +37 -0
- package/scripts/doctor.mjs +41 -0
- package/scripts/inspect-agent-tools.mjs +27 -0
- package/scripts/postinstall.mjs +50 -0
- package/scripts/qa-claude-plugin-smoke.sh +60 -0
- package/scripts/qa-portable-install.sh +136 -0
- package/scripts/validate-plugin.mjs +72 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# Node.js / tsx / ts-node / Bun / Deno Debugging
|
|
2
|
+
|
|
3
|
+
Covers Node 18+, tsx, ts-node, Bun, Deno. Launch recipes, inspector protocol usage, the `node inspect` CLI, and the **tsx source-map silent-failure** that costs people days.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Environment detection (Phase 0)
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
node --version
|
|
11
|
+
cat package.json | head -40
|
|
12
|
+
|
|
13
|
+
# Which JS runtime launches the app? (order them; the first match wins)
|
|
14
|
+
ls node_modules/.bin/tsx 2>/dev/null && echo 'has tsx'
|
|
15
|
+
ls node_modules/.bin/ts-node 2>/dev/null && echo 'has ts-node'
|
|
16
|
+
ls node_modules/.bin/vitest 2>/dev/null && echo 'has vitest'
|
|
17
|
+
which bun 2>/dev/null && bun --version
|
|
18
|
+
which deno 2>/dev/null && deno --version
|
|
19
|
+
|
|
20
|
+
# Source-map situation
|
|
21
|
+
grep -E '"sourceMap"|"inlineSources"' tsconfig.json 2>/dev/null
|
|
22
|
+
grep -l '//# sourceMappingURL' dist/*.js 2>/dev/null | head -3
|
|
23
|
+
|
|
24
|
+
# Debug-relevant ports
|
|
25
|
+
lsof -iTCP:9229 -sTCP:LISTEN -nP 2>/dev/null
|
|
26
|
+
lsof -iTCP:9230 -sTCP:LISTEN -nP 2>/dev/null
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
31
|
+
## 🚨 The tsx + `node inspect` CLI silent-failure (READ THIS)
|
|
32
|
+
|
|
33
|
+
`tsx` transpiles each `.ts` file on the fly and emits an inline source map. V8 Inspector registers the module with its `.ts` path (so it shows up in the debugger's `scripts` list), **but the `node inspect` CLI REPL does not resolve source-map line numbers reliably**. Setting `sb('session.ts', 285)` will show a "pending" breakpoint that **never fires even after the module loads**.
|
|
34
|
+
|
|
35
|
+
The breakpoint list will happily display it, so you think it's set. It isn't.
|
|
36
|
+
|
|
37
|
+
### Three reliable workarounds
|
|
38
|
+
|
|
39
|
+
| Workaround | When to use | Downside |
|
|
40
|
+
|---|---|---|
|
|
41
|
+
| **`debugger;` statement in source** | You can edit the source, CLI required | Requires source edit + revert |
|
|
42
|
+
| **Chrome DevTools GUI** (`chrome://inspect`) | CLI not required, faster iteration | Not usable if user specifically asked for CLI |
|
|
43
|
+
| **Debug the built `dist/` JS** | Source maps are working end-to-end | Requires `npm run build` on every source change |
|
|
44
|
+
|
|
45
|
+
The `debugger;` statement is the most reliable. Journal the edit — revert at Phase 9.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Launch recipes by runtime
|
|
50
|
+
|
|
51
|
+
### Node (plain JS / compiled TS)
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
# Break on first line, wait for debugger to attach
|
|
55
|
+
node --inspect-brk=9229 dist/index.js
|
|
56
|
+
|
|
57
|
+
# Attach immediately, don't block startup — pair with debugger; statements
|
|
58
|
+
node --inspect=9229 dist/index.js
|
|
59
|
+
|
|
60
|
+
# Wait for debugger to attach, THEN run (new in Node 20.15+)
|
|
61
|
+
node --inspect-wait=9229 dist/index.js
|
|
62
|
+
|
|
63
|
+
# Source maps in stack traces (always a good idea in debug builds)
|
|
64
|
+
node --enable-source-maps --inspect dist/index.js
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### tsx
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
# The tsx runner is --import-compatible, so these work:
|
|
71
|
+
node --inspect-brk=9229 --import tsx index.ts
|
|
72
|
+
node --inspect=9229 --import tsx index.ts
|
|
73
|
+
|
|
74
|
+
# If user prefers invoking tsx directly, this also works but is less explicit:
|
|
75
|
+
NODE_OPTIONS='--inspect-brk=9229' npx tsx index.ts
|
|
76
|
+
|
|
77
|
+
# ⚠️ tsx watch + inspector = inspector reloads per file change
|
|
78
|
+
# Debug without watch:
|
|
79
|
+
node --inspect=9229 --import tsx index.ts # (no `watch`)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### ts-node (legacy but still encountered)
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
node --inspect-brk -r ts-node/register src/index.ts
|
|
86
|
+
# ESM (ts-node's ESM loader is fragile — if possible, migrate to tsx):
|
|
87
|
+
node --inspect --loader ts-node/esm src/index.ts
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Bun (WebKit Inspector Protocol, NOT V8)
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
bun --inspect src/index.ts # opens debug.bun.sh URL
|
|
94
|
+
bun --inspect-brk src/index.ts # break on start
|
|
95
|
+
bun --inspect-wait src/index.ts # wait for attach
|
|
96
|
+
bun test --inspect-brk # debug test runner
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Critical**: Bun uses WebKit Inspector Protocol, not V8. `chrome://inspect` cannot connect directly. Use `debug.bun.sh` or the (currently buggy, per Bun docs) VS Code extension.
|
|
100
|
+
|
|
101
|
+
### Deno (native V8, Chrome DevTools / VS Code compatible)
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
deno run --inspect-brk --allow-all src/main.ts
|
|
105
|
+
deno test --inspect-brk --filter "auth"
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Deno is the smoothest TS debugging experience — native V8 inspector, no source-map workarounds.
|
|
109
|
+
|
|
110
|
+
### Vitest
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# Single worker required — inspector can't attach to multiple workers
|
|
114
|
+
vitest --inspect-brk --no-file-parallelism
|
|
115
|
+
vitest --inspect-brk --browser --no-file-parallelism # browser mode
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Without `--no-file-parallelism`, breakpoints won't fire because the process Vitest spawns workers in isn't the one listening on the inspector port.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Attaching with `node inspect` CLI
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
node inspect 127.0.0.1:9229 # attach to an existing --inspect process
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Core commands at the `debug>` prompt:
|
|
129
|
+
|
|
130
|
+
```
|
|
131
|
+
cont, c resume until next break / debugger;
|
|
132
|
+
next, n step over
|
|
133
|
+
step, s step into
|
|
134
|
+
out, o step out
|
|
135
|
+
pause pause a running process
|
|
136
|
+
bt backtrace
|
|
137
|
+
scripts list all modules V8 has loaded (incl. tsx-transpiled .ts)
|
|
138
|
+
sb(N) set breakpoint at line N of current file
|
|
139
|
+
sb('file', N) set breakpoint at line N of matching file (⚠️ unreliable with tsx)
|
|
140
|
+
sb(func) set breakpoint at function reference
|
|
141
|
+
cb(N), cb('file', N) clear breakpoint
|
|
142
|
+
breakpoints list breakpoints (shows pending ones, doesn't tell you they'll never fire)
|
|
143
|
+
watch('expr') persistent watch expression
|
|
144
|
+
watchers show watchers
|
|
145
|
+
exec('expr') evaluate expression in paused frame's scope
|
|
146
|
+
repl drop into full REPL with frame's scope
|
|
147
|
+
restart restart the debuggee
|
|
148
|
+
kill kill the debuggee
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
**`exec('expr')` is the most powerful tool in this CLI** — it evaluates any JS in the paused frame and returns the value. Use it heavily.
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## `exec()` patterns that resolve hypotheses fast
|
|
156
|
+
|
|
157
|
+
At a breakpoint, these queries resolve most LLM / agent / async bugs in one line each:
|
|
158
|
+
|
|
159
|
+
```js
|
|
160
|
+
// Agent / LLM state
|
|
161
|
+
exec('this.agent.state.messages.length')
|
|
162
|
+
exec('this.agent.state.messages.map(m => m.role)')
|
|
163
|
+
exec('JSON.stringify(this.agent.state.messages.at(-1)).substring(0, 500)')
|
|
164
|
+
exec('this.agent.state.messages.at(-1).errorMessage') // silent-error sentinel
|
|
165
|
+
exec('this.agent.state.messages.at(-1).stopReason')
|
|
166
|
+
exec('JSON.stringify(this.agent.state.usage)') // undefined / all-zero = failed call
|
|
167
|
+
exec('this.agent.state.model.baseUrl') // catch hardcoded vs env-var
|
|
168
|
+
|
|
169
|
+
// Env / config at runtime
|
|
170
|
+
exec('process.env.RELEVANT_VAR')
|
|
171
|
+
exec('Object.keys(process.env).filter(k => k.startsWith("ANTHROPIC"))')
|
|
172
|
+
exec('this.config')
|
|
173
|
+
|
|
174
|
+
// Async / timing
|
|
175
|
+
exec('Date.now() - this._turnStartedAt')
|
|
176
|
+
exec('this._activePromises?.size')
|
|
177
|
+
|
|
178
|
+
// HTTP request/response in-flight
|
|
179
|
+
exec('JSON.stringify(req.body).length')
|
|
180
|
+
exec('res.statusCode')
|
|
181
|
+
exec('res.headersSent')
|
|
182
|
+
|
|
183
|
+
// What's actually running
|
|
184
|
+
exec('process.version')
|
|
185
|
+
exec('process.cwd()')
|
|
186
|
+
exec('process.argv')
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## Silent-failure patterns in Node
|
|
192
|
+
|
|
193
|
+
These are the patterns that most commonly look like success but aren't. Always check when a response is "too fast" or "too empty":
|
|
194
|
+
|
|
195
|
+
| Signal | What it means |
|
|
196
|
+
|---|---|
|
|
197
|
+
| HTTP 200 + `content: ""` | Silent error swallowed |
|
|
198
|
+
| HTTP 200 + response in <1s for an LLM call | Too fast for a real Claude/GPT call; something short-circuited |
|
|
199
|
+
| `usage: { totalTokens: 0 }` | LLM SDK returned a stub without making the call |
|
|
200
|
+
| `stopReason: "error" + content: []` | SDK packaged an error into a "success" message |
|
|
201
|
+
| Unhandled promise rejection with no log | Caller forgot to `await`, or `.catch(() => {})` |
|
|
202
|
+
| `try { await x(); } catch {}` | Error eaten, no log |
|
|
203
|
+
| `void somePromise()` | Explicit opt-out of error propagation; often a bug |
|
|
204
|
+
| Callback-style API where callback never fires | Error happened before callback scheduled |
|
|
205
|
+
| Handler returns `res.json(...)` twice | Second call is silent on some Express versions |
|
|
206
|
+
|
|
207
|
+
When you find one, add a temporary `console.error('[DEBUG]', ...)` to make it loud — journal it, revert at Phase 9.
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
## tmux session layout (two sessions, one purpose each)
|
|
212
|
+
|
|
213
|
+
```bash
|
|
214
|
+
# Long-running inspected process
|
|
215
|
+
tmux new-session -d -s debug-server -c "$PWD"
|
|
216
|
+
tmux send-keys -t debug-server 'node --inspect=9229 --import tsx index.ts' Enter
|
|
217
|
+
|
|
218
|
+
# Interactive debugger client (separate pane for readability)
|
|
219
|
+
tmux new-session -d -s debug-client -c "$PWD"
|
|
220
|
+
tmux send-keys -t debug-client 'node inspect 127.0.0.1:9229' Enter
|
|
221
|
+
|
|
222
|
+
# Non-blocking pane inspection from the outside
|
|
223
|
+
tmux capture-pane -p -t debug-server -S -50
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Journal both session names. Kill both at Phase 9:
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
tmux kill-session -t debug-server
|
|
230
|
+
tmux kill-session -t debug-client
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## When to abandon the CLI and switch to Chrome DevTools
|
|
236
|
+
|
|
237
|
+
The user's preference for CLI is valid and should be respected. But you may recommend a switch in one short sentence if ANY of these hold:
|
|
238
|
+
|
|
239
|
+
- You hit source-map resolution failures (`sb('file', line)` not firing) AND the fix is time-sensitive
|
|
240
|
+
- You need to watch many values simultaneously (GUI watch panel is faster to scan)
|
|
241
|
+
- You're stepping through async-heavy code where CLI step semantics get murky across microtask boundaries
|
|
242
|
+
|
|
243
|
+
Phrase as a note, not a request: "I can push through with `debugger;` statements in CLI. If we hit three or more of these in a row, switching to `chrome://inspect` GUI would cut cycle time in half — your call."
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Phase 9 cleanup specifics
|
|
248
|
+
|
|
249
|
+
```bash
|
|
250
|
+
# Revert source-level debug statements
|
|
251
|
+
git diff | grep -E '(debugger;|console\.log\(.*DEBUG|\[ARBITER-DEBUG|\[DEBUG)'
|
|
252
|
+
# Revert any matching files:
|
|
253
|
+
git checkout <file>
|
|
254
|
+
|
|
255
|
+
# Kill inspector-attached processes
|
|
256
|
+
pkill -f 'node --inspect' || true
|
|
257
|
+
pkill -f 'bun --inspect' || true
|
|
258
|
+
pkill -f 'deno.*--inspect' || true
|
|
259
|
+
lsof -iTCP:9229 -sTCP:LISTEN -nP 2>/dev/null
|
|
260
|
+
```
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
# Python Debugging
|
|
2
|
+
|
|
3
|
+
Covers CPython 3.9+, pytest, asyncio, Django, FastAPI. Setup commands, attach mechanisms, state-query patterns, gotchas, silent-failure signatures.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Environment detection (Phase 0)
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Which Python will actually run the code?
|
|
11
|
+
which python; which python3
|
|
12
|
+
python --version
|
|
13
|
+
|
|
14
|
+
# Is there a project env manager in play?
|
|
15
|
+
ls poetry.lock uv.lock Pipfile.lock requirements*.txt .python-version 2>/dev/null
|
|
16
|
+
|
|
17
|
+
# Installed debuggers / profilers in this env?
|
|
18
|
+
python -c 'import pdb, sys; print("pdb", "built-in"); print("python", sys.executable)'
|
|
19
|
+
pip list 2>/dev/null | grep -iE '^(ipdb|pudb|debugpy|py-spy|memray|rich)\s'
|
|
20
|
+
|
|
21
|
+
# asyncio debug mode available?
|
|
22
|
+
python -c 'import asyncio; print(asyncio.__version__)'
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**Wrapper gotchas** (these change how flags propagate):
|
|
26
|
+
|
|
27
|
+
- `poetry run python ...` — args after `python` are fine; args before `poetry run` go to poetry, not python
|
|
28
|
+
- `uv run python ...` — similar; prefer `uv run -- python -X dev` if flags collide
|
|
29
|
+
- `pipenv run` — same story
|
|
30
|
+
- `./manage.py <cmd>` (Django) — shebang resolution; make sure it points to the right venv
|
|
31
|
+
- `pytest` — loads `conftest.py` at collection; breakpoints inside collection need `pytest --pdb-trace` not `--pdb`
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## The four ways to attach
|
|
36
|
+
|
|
37
|
+
| Method | When to use | Command |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| **`breakpoint()` inline** (Python 3.7+) | You can edit the source and restart. Most reliable. | Add `breakpoint()` to source. Run normally. It invokes `pdb` by default. |
|
|
40
|
+
| **`python -m pdb <script>`** | No source edit desired. Breaks on entry. | `python -m pdb script.py arg1` |
|
|
41
|
+
| **post-mortem `pdb.pm()`** | Exception already happened, you want to inspect state | In an exception-caught REPL: `import pdb; pdb.pm()` after the exception propagates |
|
|
42
|
+
| **debugpy (remote / IDE)** | IDE attach, remote host, containerized process | `python -m debugpy --listen 5678 --wait-for-client script.py` then attach from VS Code / PyCharm |
|
|
43
|
+
|
|
44
|
+
### Prefer `ipdb` or `pudb` over plain `pdb` when available
|
|
45
|
+
|
|
46
|
+
- **ipdb** — drop-in replacement with tab completion, syntax highlighting. `pip install ipdb`, then `PYTHONBREAKPOINT=ipdb.set_trace` or use `import ipdb; ipdb.set_trace()`.
|
|
47
|
+
- **pudb** — full-screen TUI debugger, much faster to navigate stack/locals. `pip install pudb`, then `PYTHONBREAKPOINT=pudb.set_trace`.
|
|
48
|
+
|
|
49
|
+
### Control `breakpoint()` globally
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# Use ipdb instead of pdb
|
|
53
|
+
export PYTHONBREAKPOINT=ipdb.set_trace
|
|
54
|
+
|
|
55
|
+
# Disable all breakpoint() calls (useful to ship without removing them)
|
|
56
|
+
export PYTHONBREAKPOINT=0
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**Journal this env var** — unset at Phase 9.
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## pdb / ipdb essentials
|
|
64
|
+
|
|
65
|
+
At a `(Pdb)` or `ipdb>` prompt:
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
l list source around current line
|
|
69
|
+
ll list whole function
|
|
70
|
+
s step into
|
|
71
|
+
n step over (next)
|
|
72
|
+
r step out (return)
|
|
73
|
+
c continue
|
|
74
|
+
b list breakpoints
|
|
75
|
+
b <line> breakpoint at line
|
|
76
|
+
b <func> breakpoint at function
|
|
77
|
+
cl <n> clear breakpoint n
|
|
78
|
+
w where (backtrace)
|
|
79
|
+
u / d move up/down the stack
|
|
80
|
+
a args of current frame
|
|
81
|
+
p <expr> print expression
|
|
82
|
+
pp <expr> pretty-print
|
|
83
|
+
!<stmt> execute Python statement (e.g. !x = 5)
|
|
84
|
+
interact drop into a full Python REPL with current frame's locals
|
|
85
|
+
q quit (aborts the program)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**`interact` is underused** — it gives you a full IPython-esque REPL with all locals available. Faster than typing `p` for 20 things.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## pytest-specific debugging
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
# Enter pdb on first failure
|
|
96
|
+
pytest --pdb
|
|
97
|
+
|
|
98
|
+
# Enter pdb at the START of each test (not on failure)
|
|
99
|
+
pytest --trace
|
|
100
|
+
|
|
101
|
+
# Run only the failing test, with -s to show print output
|
|
102
|
+
pytest --pdb -x -s path/to/test.py::test_name
|
|
103
|
+
|
|
104
|
+
# Collect-time debugging (for problems in conftest.py / fixture setup)
|
|
105
|
+
pytest --pdb-trace
|
|
106
|
+
|
|
107
|
+
# Disable capture for this test (so breakpoint prompt is visible)
|
|
108
|
+
pytest -s
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Common failure**: `breakpoint()` hangs inside a pytest test — that's because pytest captures stdout/stderr by default. Always add `-s` when debugging with breakpoints inside pytest.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## asyncio gotchas
|
|
116
|
+
|
|
117
|
+
Async is where most Python debug sessions go sideways. Know these before attaching.
|
|
118
|
+
|
|
119
|
+
### Breakpoints inside coroutines
|
|
120
|
+
|
|
121
|
+
`breakpoint()` works inside an async function, but stepping into another coroutine from `pdb` is awkward. Two techniques:
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
async def handler():
|
|
125
|
+
result = await some_async_fn() # add breakpoint ABOVE, not inside, when possible
|
|
126
|
+
breakpoint()
|
|
127
|
+
return result
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Inside the breakpoint, to inspect a coroutine without actually advancing time:
|
|
131
|
+
```
|
|
132
|
+
!import asyncio
|
|
133
|
+
!loop = asyncio.get_event_loop()
|
|
134
|
+
!task = asyncio.ensure_future(some_async_fn())
|
|
135
|
+
# Now inspect task state, don't await it
|
|
136
|
+
p task
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### PYTHONASYNCIODEBUG
|
|
140
|
+
|
|
141
|
+
Enable before running the process:
|
|
142
|
+
|
|
143
|
+
```bash
|
|
144
|
+
PYTHONASYNCIODEBUG=1 python script.py
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Surfaces: coroutines that were never awaited, slow callbacks, unhandled task exceptions. **Always turn this on** if the bug is timing- or async-related.
|
|
148
|
+
|
|
149
|
+
### `asyncio.gather` swallows the first exception
|
|
150
|
+
|
|
151
|
+
By default, `asyncio.gather(t1, t2)` raises the first exception and cancels the rest. If you need all exceptions, use `gather(..., return_exceptions=True)`.
|
|
152
|
+
|
|
153
|
+
### Unhandled task exceptions are silent
|
|
154
|
+
|
|
155
|
+
```python
|
|
156
|
+
async def main():
|
|
157
|
+
task = asyncio.create_task(broken_coroutine())
|
|
158
|
+
# If task raises and we never await it, the exception is eaten at gc time
|
|
159
|
+
await asyncio.sleep(10)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
To catch these, set `loop.set_exception_handler(...)` or upgrade to Python 3.12+ which warns louder by default.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## debugpy — remote / IDE / container attach
|
|
167
|
+
|
|
168
|
+
Listen and wait for attach:
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
python -m debugpy --listen 0.0.0.0:5678 --wait-for-client script.py
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Attach from VS Code:
|
|
175
|
+
```json
|
|
176
|
+
// .vscode/launch.json
|
|
177
|
+
{
|
|
178
|
+
"name": "attach",
|
|
179
|
+
"type": "python",
|
|
180
|
+
"request": "attach",
|
|
181
|
+
"connect": { "host": "localhost", "port": 5678 }
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Inside the code, programmatic attach point:
|
|
186
|
+
```python
|
|
187
|
+
import debugpy
|
|
188
|
+
debugpy.listen(5678)
|
|
189
|
+
debugpy.wait_for_client() # blocks until attached
|
|
190
|
+
debugpy.breakpoint() # programmatic breakpoint
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
**Journal**: the port (5678) and the listener file — unset at Phase 9.
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
## Sampling profilers for "why is it slow / stuck"
|
|
198
|
+
|
|
199
|
+
When the problem is performance or a hang (not a crash), don't attach pdb — it alters timing. Use a sampling profiler that attaches to the running process:
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
# py-spy — production-safe, zero code change, works on running process
|
|
203
|
+
py-spy top --pid <pid> # live top-like view
|
|
204
|
+
py-spy record -o profile.svg --pid <pid> # flamegraph
|
|
205
|
+
py-spy dump --pid <pid> # stack traces of all threads right now
|
|
206
|
+
|
|
207
|
+
# memray — memory allocation tracking
|
|
208
|
+
memray run script.py
|
|
209
|
+
memray flamegraph output.bin
|
|
210
|
+
memray stats output.bin
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
`py-spy dump` on a stuck process is often enough to find the hung call — no breakpoints needed.
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
## Silent-failure patterns in Python
|
|
218
|
+
|
|
219
|
+
Add these to Phase 8's silent-failure check:
|
|
220
|
+
|
|
221
|
+
| Pattern | Why it's silent |
|
|
222
|
+
|---|---|
|
|
223
|
+
| `except Exception: pass` or `except: pass` | Catches and discards every error including KeyboardInterrupt |
|
|
224
|
+
| `logging.exception(...)` in a logger with no handlers | "Logs" but actually writes nowhere |
|
|
225
|
+
| `asyncio.create_task(coro)` without storing the task | Task GC'd before completion, exception swallowed |
|
|
226
|
+
| `return x.get("key")` where key is missing | Returns None silently, caller often doesn't check |
|
|
227
|
+
| `subprocess.run(..., check=False)` with ignored returncode | Non-zero exit treated as success |
|
|
228
|
+
| Django `transaction.atomic()` inside a broader `except` | Rolls back silently |
|
|
229
|
+
| `contextlib.suppress(Exception)` | Explicit silencer; easy to leave wider than intended |
|
|
230
|
+
| `queue.get(block=False)` with `except queue.Empty: pass` | Polling that silently drops the work |
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Phase 9 cleanup specifics
|
|
235
|
+
|
|
236
|
+
```bash
|
|
237
|
+
# Remove breakpoint() / ipdb / pudb lines from source
|
|
238
|
+
git diff | grep -E '(breakpoint\(\)|import ipdb|import pudb|import pdb; pdb\.set_trace)'
|
|
239
|
+
# If the above has output, revert those files:
|
|
240
|
+
git checkout <file>
|
|
241
|
+
|
|
242
|
+
# Unset the global breakpoint override
|
|
243
|
+
unset PYTHONBREAKPOINT
|
|
244
|
+
|
|
245
|
+
# Kill any leftover debugpy listeners
|
|
246
|
+
pkill -f 'debugpy' || true
|
|
247
|
+
lsof -iTCP:5678 -sTCP:LISTEN -nP 2>/dev/null # confirm free
|
|
248
|
+
```
|