Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Une commande directe contourne le prompt de vérification. Examinez la source avant de l'exécuter.
Full code quality audit for a file, package, or directory. Combines LSP batch
analysis (blast_radius) with targeted per-symbol checks and LLM-driven
heuristic analysis. Produces a severity-tiered findings report with confidence
tiers and fix suggestions.
When to Use
Auditing a package before a release or major refactor
Finding dead code, untested exports, and error handling gaps in unfamiliar code
Reviewing code quality of an external codebase for contribution opportunities
Pre-merge quality gate on a set of changed files
Batch inspection of an entire directory with ranked output
Comparing branch changes against main to find newly introduced issues
A directory/package: /lsp-inspect internal/runnables/
Multiple targets: /lsp-inspect pkg/a.go pkg/b.go
Directory detection: When target is a directory, walk all .go, .ts, .py
files in it recursively. Produce a ranked report: "Top N findings sorted by
severity then blast radius."
Flags:
--checks <type1>,<type2>: only run listed check types (default: all applicable)
--json: emit structured JSON instead of markdown
--top N: Maximum findings to report (default 20). Only applies to directory/batch mode.
--diff: Only inspect files changed vs main branch. Filter findings to lines within the diff ranges. Output header: "New issues introduced by this branch."
Warm-up check (mandatory): Pick one symbol you know is actively used.
Call find_references on it. If it returns [], wait 3-5 seconds and retry.
Do not proceed until a known-active symbol returns >= 1 reference.
Step 0.5: Diff mode file selection
When --diff is set:
Run git diff --name-only main to get changed files
Run git diff main to get line-level change ranges
Use only changed files as inspection targets
After Step 3, filter findings: keep only those whose File:Line falls within
a changed line range from the diff
Prepend output with: '## New issues introduced by this branch'
Rust: .unwrap_or_default() on fallible ops that should propagate
error_wrapping: Look for:
Go: return err without fmt.Errorf("context: %w", err)
Python: raise ValueError(str(e)) without from e
TypeScript: throw e without wrapping in a contextual error
coverage_gap: Look for:
Switch/match without exhaustive cases or default branch
Unchecked type assertions (v := x.(Type) vs v, ok := x.(Type))
Missing nil/null checks before dereference after fallible calls
doc_drift: For exported functions, compare:
Parameter names in docstring vs actual signature
Return type described in doc vs actual return
Use inspect_symbol hover text to cross-reference
panic_not_recovered: Look for:
Go: go func() { ... }() without defer recover()
Rust: .unwrap() or .expect() in non-test, non-main code
Python: bare thread creation without exception handling
context_propagation: Look for:
Functions that accept ctx context.Context but call context.Background() or context.TODO() internally
unrecovered_concurrent_entry: Detect concurrent entry points without recovery.
Language-specific patterns (check by language family):
Go: go func() { ... }() where the function body has no defer func() { if r := recover() pattern. Weight: library transport code (error severity), application code with middleware protection (info).
Java/Kotlin/Scala: new Thread(...) or ExecutorService.submit(...) without try-catch wrapping the Runnable body, and no UncaughtExceptionHandler set on the thread.
C#: Task.Run(...) or new Thread(...) without try-catch in the delegate body.
C/C++: pthread_create or std::thread without exception handling in the thread function.
Rust: std::thread::spawn without catch_unwind in the closure. Also flag .unwrap() inside spawned threads (panics kill only that thread but lose the error).
Swift: DispatchQueue.async or Task { } without do-catch.
Python: threading.Thread(target=...) without try-except in the target function. asyncio.create_task() without error handling on the awaited result.
TypeScript/JavaScript: new Worker() without onerror or error event handler. Promise constructor without .catch() on the chain.
Zig: try std.Thread.spawn without error handling on the spawned function.
Elixir/Erlang/Gleam: Skip (actor model with supervisors; unrecovered processes are by design).
Lua/Bash/SQL: Skip (no concurrency primitives).
unchecked_shared_state: Detect unsafe type operations on concurrent data structures:
Go: sync.Map.Load(), .LoadOrStore(), or .LoadAndDelete() followed by a bare type assertion actual.(*Type) without the , ok pattern. The safe pattern is v, ok := actual.(*Type).
Java: ConcurrentHashMap.get() with unchecked cast and no instanceof guard.
C#: ConcurrentDictionary value retrieval with unchecked cast.
Other languages: skip (dynamic typing or type system prevents this class of bug).
channel_never_closed: Detect channels or queues that are created but never closed:
Go: make(chan T) or make(chan T, N) where close(channelName) does not appear in the same package. May indicate goroutine leaks (receivers block on range forever).
Python: queue.Queue() creation without a sentinel value pattern (queue.put(None) + if item is None: break).
Rust: mpsc::channel() where the sender is never dropped or explicitly closed.
TypeScript: new MessageChannel() or new BroadcastChannel() without .close().
Java: BlockingQueue creation without a poison pill or shutdown pattern.
Other languages: skip if no channel/queue primitives.
shared_field_without_sync: Detect struct/class fields accessed from multiple
concurrent contexts without synchronization. This check composes two tools:
Call blast_radius on the target file. For each symbol where
sync_guarded: false (or absent), the symbol's type lacks sync primitives.
For each such symbol, call find_callers with cross_concurrent: true.
If concurrent_callers is non-empty, the symbol is called from a concurrent
context (goroutine, thread, async task) without synchronization.
Flag any symbol where: (a) it modifies state (writes to fields, not a pure
read-only function), AND (b) it has concurrent callers, AND (c) its parent
type is not sync-guarded.
Language-agnostic: blast_radius provides sync_guarded, find_callers
provides concurrent_callers. The check logic is identical regardless of
whether the concurrent boundary is a goroutine, thread, or async task.
Severity:
error: field written from 2+ concurrent contexts with no sync (data race)
warning: field written from 1 concurrent context (potential race under load)
info: field read-only from concurrent contexts (likely safe, but flag for review)
Step 3: Cross-check and classify
For each finding, assign:
Severity (calibrated by blast radius):
error: Will cause runtime failure, data loss, or resource leak, OR any finding where non_test_callers >= 10 (high blast radius amplifies severity)
warning: May cause confusion, maintenance burden, or subtle bugs, OR findings where non_test_callers is 3-9
info: Style issue or improvement opportunity, OR non_test_callers <= 2
Use the non_test_callers count from Step 1's blast_radius result as a
severity multiplier. A silent failure in a function with 50 callers is
error-severity; the same pattern with 2 callers is info.
Cross-file impact scoring: For every finding, look up the symbol's
non_test_callers count from Step 1's blast_radius result. Use this as a
severity multiplier: if non_test_callers >= 10, escalate severity by one tier
(info->warning, warning->error). Document the caller count in the finding.
Confidence tiers:
verified: LSP-confirmed (Tier 1A/1B) or unambiguous code pattern (act immediately)
suspected: Heuristic match with possible false positive (pattern match, investigate first)
advisory: Grep-based or uncertain pattern match (style, optional)
Step 4: Output
Produce the findings report:
## Inspection Report: <target>**Files analyzed:** N
**Checks applied:** [list]
**Findings:** E errors, W warnings, I info
### Errors
| # | Check | File:Line | Finding | Confidence | Fix |
|---|-------|-----------|---------|------------|-----|
| 1 | dead_symbol | pkg/foo.go:42 | `UnusedHelper` has 0 references (0 callers) | verified (LSP) | Remove lines 42-55 (function `UnusedHelper`) |
### Warnings
| # | Check | File:Line | Finding | Confidence | Fix |
|---|-------|-----------|---------|------------|-----|
| 1 | error_wrapping | pkg/bar.go:88 | `return err` without context wrapping (5 callers) | verified | Change `return err` to `return fmt.Errorf("funcName: %w", err)` |
| 2 | test_coverage | pkg/foo.go:15 | `ProcessInput` has 0 test callers (8 callers) | verified (LSP) | Add test for `ProcessInput` in foo_test.go |
### Info
| # | Check | File:Line | Finding | Confidence | Fix |
|---|-------|-----------|---------|------------|-----|
| 1 | doc_drift | pkg/foo.go:20 | Docstring mentions `timeout` param, signature has `deadline` (1 caller) | suspected | Update docstring parameter name from timeout to deadline |
error_wrapping: "Change return err to return fmt.Errorf(\"funcName: %w\", err)"
silent_failure: "Add return fmt.Errorf(...) after the if block"
test_coverage: "Add test for FuncName in file_test.go"
coverage_gap: "Add default case to switch statement at line N"
doc_drift: "Update docstring parameter name from X to Y"
panic_not_recovered: "Add defer func() { if r := recover()... }() at goroutine start"
context_propagation: "Replace context.Background() with ctx parameter"
When --json is passed, emit structured JSON with the same fields.
Step 4.5: Batch ranking
When multiple files are analyzed, sort all findings by: (1) severity tier
(error > warning > info), (2) blast radius (non_test_callers descending),
(3) file path alphabetically. Emit only the top N findings (default 20,
controlled by --top flag). Append a summary line: 'Showing N of M total findings.'
Step 5: Persist results
After producing the findings report, write a JSON file to
.agent-lsp/last-inspection.json in the workspace root. The JSON schema:
This file is served by the inspect://last MCP resource for programmatic access.
Caveats
Re-exports: Symbols in __init__.py (Python), index.ts (TypeScript),
or public API surface files may appear dead locally but are consumed externally.
Check __all__, barrel exports, and package-level re-exports before classifying.
Registration patterns: Symbols passed as values to framework registrars
(HTTP handlers, plugin hooks) show zero LSP references. Grep wiring files
before confirming dead.
Library public API: If the target is a library consumed by external repos,
zero internal references doesn't mean dead. Use --consumer-repos or note
as "library export, verify externally."
Heuristic checks are advisory. Silent failure and error wrapping checks
depend on LLM reasoning about intent. False positives are expected; always
review before acting on findings.
Large files: For files over 500 lines, read targeted sections (use
offset/limit). Do not read entire large files into context.