Review the plugin for pitfalls
A read-only audit of the plugin source against the toolkit's pitfalls list. Find real,
Minecraft-specific defects, cite each with file:line, and recommend a concrete fix. Do not
edit anything — this skill only reports.
Phase 1: Load context
- Read
.mcplugin/config.yml for platform, mc_version, version_scheme, api_version,
main_class, package, plugin_name. Missing → note it and review generically (some checks,
e.g. main matching or Folia-correctness, need the platform/version to be precise).
- Read
../../references/pitfalls.md — the authoritative checklist (Glob
**/references/pitfalls.md as fallback). Also skim ../../references/api/VERSION.md for the
version→api-version facts.
- Map the source:
Glob src/main/java/**/*.java, read the main class, listeners, command
classes; read src/main/resources/plugin.yml (or paper-plugin.yml). If the user named a
focus path/class, prioritize it but still sanity-check the lifecycle glue.
Phase 2: Systematic checks
Go category by category. Grep for the risky patterns, then read the surrounding code to confirm
(don't flag on a pattern match alone).
- Threading — Bukkit API touched off the main thread (world/entity/player/block/inventory
work inside
runTaskAsynchronously, CompletableFuture, raw Thread); blocking calls on the
main thread (Thread.sleep, synchronous HTTP/JDBC/file I/O in handlers or onEnable). On
Folia, flag Bukkit.getScheduler() assumptions — it needs region/entity/global schedulers.
- Memory leaks —
Player/Entity/Block stored in long-lived fields/collections (should
store UUID or a light location record and look up on demand); per-player state that's never
cleared; tasks/listeners not cancelled/unregistered in onDisable().
- Events — listener actually registered (
registerEvents); handlers have @EventHandler
and a single event param; EventPriority sensible and no state mutation in MONITOR;
cancelled events handled (isCancelled() / ignoreCancelled); cancels use
setCancelled(true), not an early return.
- Commands — every
plugin.yml command has an executor set in code; getCommand("x")
null-guarded (null when not declared); args.length checked before indexing;
sender instanceof Player checked before casting; permission nodes consistent between code and
plugin.yml; tab completion present.
- Config —
saveDefaultConfig() with a bundled config.yml; reloadConfig() before reads
after on-disk edits and saveConfig() after programmatic changes; getX(path) results
defaulted / null-checked.
- Persistence — prefer
PersistentDataContainer over deprecated metadata/NBT hacks; one
reused NamespacedKey per key (not recreated per call); DB/file I/O done async.
- Lifecycle & correctness —
main: in the descriptor equals <package>.<main_class>;
api-version set (per VERSION.md); cleanup in onDisable(); no heavy blocking work in
onEnable(); null-checks on ItemStack/ItemMeta/getClickedBlock() and similar.
- Performance — heavy work in hot paths (
PlayerMoveEvent, BlockPhysicsEvent, per-tick
tasks); PlayerMoveEvent handlers that don't early-return when the block position is unchanged;
scanning all players/entities every tick; uncached repeated lookups.
- 2026 version drift — version parsing that assumes a leading
1. (breaks on 26.1.x);
reliance on obfuscated NMS names on 26.x (mappings are native now); APIs asserted from memory
for a target newer than VERSION.md documents — flag as "verify against live docs".
Phase 3: Report — grouped by severity
Output findings in three tiers, each as file:line — problem → concrete fix:
- Critical — will crash, corrupt state, leak unboundedly, or stop the plugin loading
(off-main-thread world access,
main mismatch, unregistered core listener, main-thread
blocking I/O).
- Warning — likely bug or real risk under load (missing cancel handling,
Player held in a
map, no onDisable cleanup, missing null guard).
- Suggestion — correctness/robustness/perf polish (add tab completion, reuse a
NamespacedKey, early-return in PlayerMoveEvent, set an explicit EventPriority).
Be specific: name the symptom ("holds Player in Map<String,Player> homes — pins the object
after logout") and the fix ("key by UUID, resolve via Bukkit.getPlayer(uuid)"). If a category
is clean, say so in one line rather than padding.
End with a short summary: counts per severity, the top 1–3 things to fix first, and a pointer
to re-run /build and /run-server after fixing. This skill recommends fixes; it does not apply
them — offer to hand off to the relevant implement skill (/add-command, /add-listener, …) or
to make the edits in a follow-up.
1---2name: plugin-review3description: Read-only review of a Minecraft plugin's source against the common pitfalls — threading, memory leaks, events, commands, config, persistence, lifecycle, performance, and 2026 version drift. Use this when the user wants their plugin reviewed, audited, or checked for bugs/leaks/thread-safety before shipping, or asks "is my plugin correct / safe / production-ready?". Reports findings by severity; never edits. Reads the stack from .mcplugin/config.yml.4---56# Review the plugin for pitfalls78A **read-only** audit of the plugin source against the toolkit's pitfalls list. Find real,9Minecraft-specific defects, cite each with `file:line`, and recommend a concrete fix. **Do not10edit anything** — this skill only reports.1112## Phase 1: Load context1314- Read `.mcplugin/config.yml` for `platform`, `mc_version`, `version_scheme`, `api_version`,15 `main_class`, `package`, `plugin_name`. Missing → note it and review generically (some checks,16 e.g. `main` matching or Folia-correctness, need the platform/version to be precise).17- Read `../../references/pitfalls.md` — the authoritative checklist (`Glob`18 `**/references/pitfalls.md` as fallback). Also skim `../../references/api/VERSION.md` for the19 version→api-version facts.20- Map the source: `Glob` `src/main/java/**/*.java`, read the main class, listeners, command21 classes; read `src/main/resources/plugin.yml` (or `paper-plugin.yml`). If the user named a22 focus path/class, prioritize it but still sanity-check the lifecycle glue.2324## Phase 2: Systematic checks2526Go category by category. `Grep` for the risky patterns, then read the surrounding code to confirm27(don't flag on a pattern match alone).2829- **Threading** — Bukkit API touched off the main thread (world/entity/player/block/inventory30 work inside `runTaskAsynchronously`, `CompletableFuture`, raw `Thread`); blocking calls on the31 main thread (`Thread.sleep`, synchronous HTTP/JDBC/file I/O in handlers or `onEnable`). On32 **Folia**, flag `Bukkit.getScheduler()` assumptions — it needs region/entity/global schedulers.33- **Memory leaks** — `Player`/`Entity`/`Block` stored in long-lived fields/collections (should34 store `UUID` or a light location record and look up on demand); per-player state that's never35 cleared; tasks/listeners not cancelled/unregistered in `onDisable()`.36- **Events** — listener actually **registered** (`registerEvents`); handlers have `@EventHandler`37 and a single event param; `EventPriority` sensible and **no state mutation in `MONITOR`**;38 cancelled events handled (`isCancelled()` / `ignoreCancelled`); cancels use39 `setCancelled(true)`, not an early `return`.40- **Commands** — every `plugin.yml` command has an executor set in code; `getCommand("x")`41 null-guarded (null when not declared); `args.length` checked before indexing;42 `sender instanceof Player` checked before casting; permission nodes consistent between code and43 `plugin.yml`; tab completion present.44- **Config** — `saveDefaultConfig()` with a bundled `config.yml`; `reloadConfig()` before reads45 after on-disk edits and `saveConfig()` after programmatic changes; `getX(path)` results46 defaulted / null-checked.47- **Persistence** — prefer `PersistentDataContainer` over deprecated metadata/NBT hacks; one48 reused `NamespacedKey` per key (not recreated per call); DB/file I/O done **async**.49- **Lifecycle & correctness** — `main:` in the descriptor equals `<package>.<main_class>`;50 `api-version` set (per VERSION.md); cleanup in `onDisable()`; no heavy blocking work in51 `onEnable()`; null-checks on `ItemStack`/`ItemMeta`/`getClickedBlock()` and similar.52- **Performance** — heavy work in hot paths (`PlayerMoveEvent`, `BlockPhysicsEvent`, per-tick53 tasks); `PlayerMoveEvent` handlers that don't early-return when the block position is unchanged;54 scanning all players/entities every tick; uncached repeated lookups.55- **2026 version drift** — version parsing that assumes a leading `1.` (breaks on `26.1.x`);56 reliance on obfuscated NMS names on 26.x (mappings are native now); APIs asserted from memory57 for a target newer than VERSION.md documents — flag as "verify against live docs".5859## Phase 3: Report — grouped by severity6061Output findings in three tiers, each as `file:line — problem → concrete fix`:62- **Critical** — will crash, corrupt state, leak unboundedly, or stop the plugin loading63 (off-main-thread world access, `main` mismatch, unregistered core listener, main-thread64 blocking I/O).65- **Warning** — likely bug or real risk under load (missing cancel handling, `Player` held in a66 map, no `onDisable` cleanup, missing null guard).67- **Suggestion** — correctness/robustness/perf polish (add tab completion, reuse a68 `NamespacedKey`, early-return in `PlayerMoveEvent`, set an explicit `EventPriority`).6970Be specific: name the symptom ("holds `Player` in `Map<String,Player> homes` — pins the object71after logout") and the fix ("key by `UUID`, resolve via `Bukkit.getPlayer(uuid)`"). If a category72is clean, say so in one line rather than padding.7374End with a short **summary**: counts per severity, the top 1–3 things to fix first, and a pointer75to re-run `/build` and `/run-server` after fixing. This skill recommends fixes; it does not apply76them — offer to hand off to the relevant implement skill (`/add-command`, `/add-listener`, …) or77to make the edits in a follow-up.