papermc-skill 1.0.1 → 1.2.0
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/.cursor/rules/paper-plugin-dev.mdc +23 -0
- package/.cursorrules +6 -0
- package/.paseo/rules.md +8 -0
- package/.pi/rules.md +8 -0
- package/.windsurfrules +7 -0
- package/CHANGELOG.md +50 -0
- package/CLAUDE.md +27 -0
- package/CODEX.md +12 -0
- package/DEEPSEEK.md +18 -0
- package/README.md +73 -33
- package/assets/banner-animated.svg +360 -0
- package/assets/banner.svg +360 -0
- package/bin/cli.js +565 -35
- package/docs/01-modern-paper-architecture.md +48 -51
- package/docs/02-adventure-and-minimessage.md +48 -52
- package/docs/03-persistent-data-container.md +39 -39
- package/docs/04-commands-and-brigadier.md +32 -35
- package/docs/05-concurrency-and-folia.md +44 -44
- package/docs/06-events-and-listeners.md +30 -31
- package/docs/07-best-practices-and-perf.md +39 -40
- package/docs/08-gui-and-menus.md +34 -37
- package/docs/09-custom-items-and-combat.md +25 -33
- package/docs/10-database-and-storage.md +27 -27
- package/docs/11-security-and-exploit-prevention.md +233 -0
- package/docs/12-data-components-and-display-entities.md +264 -0
- package/docs/13-multi-agent-system-and-tooling.md +90 -0
- package/package.json +17 -4
- package/skills/paper-plugin-dev/SKILL.md +425 -114
- package/templates/paper-modern-template/.gradle/9.7.1/checksums/checksums.lock +0 -0
- package/templates/paper-modern-template/.gradle/9.7.1/checksums/md5-checksums.bin +0 -0
- package/templates/paper-modern-template/.gradle/9.7.1/checksums/sha1-checksums.bin +0 -0
- package/templates/paper-modern-template/.gradle/9.7.1/executionHistory/executionHistory.bin +0 -0
- package/templates/paper-modern-template/.gradle/9.7.1/executionHistory/executionHistory.lock +0 -0
- package/templates/paper-modern-template/.gradle/9.7.1/fileHashes/fileHashes.bin +0 -0
- package/templates/paper-modern-template/.gradle/9.7.1/fileHashes/fileHashes.lock +0 -0
- package/templates/paper-modern-template/.gradle/9.7.1/fileHashes/resourceHashesCache.bin +0 -0
- package/templates/paper-modern-template/.gradle/9.7.1/kotlin-dsl-plugin-entries/kotlin-dsl-plugin-entries.lock +0 -0
- package/templates/paper-modern-template/.gradle/buildOutputCleanup/buildOutputCleanup.lock +0 -0
- package/templates/paper-modern-template/.gradle/buildOutputCleanup/cache.properties +1 -1
- package/templates/paper-modern-template/build/classes/java/main/com/example/paperplugin/ExamplePaperPlugin.class +0 -0
- package/templates/paper-modern-template/build/classes/java/main/com/example/paperplugin/PaperPluginBootstrap.class +0 -0
- package/templates/paper-modern-template/build/classes/java/main/com/example/paperplugin/command/HelloCommand.class +0 -0
- package/templates/paper-modern-template/build/classes/java/main/com/example/paperplugin/listener/PlayerEventListener.class +0 -0
- package/templates/paper-modern-template/build/classes/java/main/com/example/paperplugin/util/SchedulerAdapter.class +0 -0
- package/templates/paper-modern-template/build/tmp/compileJava/previous-compilation-data.bin +0 -0
- package/templates/paper-modern-template/build.gradle.kts +7 -7
- package/templates/paper-modern-template/src/main/java/com/example/paperplugin/ExamplePaperPlugin.java +6 -6
- package/templates/paper-modern-template/src/main/java/com/example/paperplugin/PaperPluginBootstrap.java +2 -2
- package/templates/paper-modern-template/src/main/java/com/example/paperplugin/command/HelloCommand.java +2 -4
- package/templates/paper-modern-template/src/main/java/com/example/paperplugin/data/PluginDataKeys.java +2 -2
- package/templates/paper-modern-template/src/main/java/com/example/paperplugin/listener/PlayerEventListener.java +6 -7
- package/templates/paper-modern-template/src/main/java/com/example/paperplugin/util/SchedulerAdapter.java +6 -6
- package/templates/paper-modern-template/src/main/resources/config.yml +5 -5
package/bin/cli.js
CHANGED
|
@@ -18,6 +18,7 @@ const c = {
|
|
|
18
18
|
green: '\x1b[32m',
|
|
19
19
|
yellow: '\x1b[33m',
|
|
20
20
|
red: '\x1b[31m',
|
|
21
|
+
magenta: '\x1b[35m',
|
|
21
22
|
gray: '\x1b[90m'
|
|
22
23
|
};
|
|
23
24
|
|
|
@@ -28,7 +29,7 @@ ${c.cyan}${c.bold} ____ __ __ ____
|
|
|
28
29
|
| |_) / _\` | '_ \\ / _ \\ '__|____| |\\/| | | / ___|| | __(_) | |
|
|
29
30
|
| __/ (_| | |_) | __/ | |_____| | | | |___ \\___ \\| |/ / | | |
|
|
30
31
|
|_| \\__,_| .__/ \\___|_| |_| |_|\\____| |____/|_|\\_\\_|_|_|
|
|
31
|
-
|_| ${c.reset}${c.dim}Modern Paper & Folia Plugin Toolchain${c.reset}
|
|
32
|
+
|_| ${c.reset}${c.dim}Modern Paper & Folia Plugin Toolchain (1.21+ / 26.x)${c.reset}
|
|
32
33
|
`);
|
|
33
34
|
}
|
|
34
35
|
|
|
@@ -118,7 +119,7 @@ function copyDirRecursive(src, dest, replacements = {}) {
|
|
|
118
119
|
|
|
119
120
|
async function handleCreate(targetDirArg) {
|
|
120
121
|
printBanner();
|
|
121
|
-
console.log(`${c.bold}Scaffolding a new Modern Paper Plugin...${c.reset}\n`);
|
|
122
|
+
console.log(`${c.bold}Scaffolding a new Modern Paper & Folia Plugin...${c.reset}\n`);
|
|
122
123
|
|
|
123
124
|
const rawPluginName = await prompt('Plugin Name', targetDirArg || 'MyPaperPlugin');
|
|
124
125
|
const pluginName = sanitizePluginName(rawPluginName);
|
|
@@ -178,32 +179,427 @@ async function handleCreate(targetDirArg) {
|
|
|
178
179
|
console.log(` ${c.cyan}./gradlew runFoliaServer${c.reset} # Launches Folia multi-threaded test server${c.reset}\n`);
|
|
179
180
|
}
|
|
180
181
|
|
|
181
|
-
|
|
182
|
+
// Universal Multi-Agent Installer
|
|
183
|
+
async function handleInstallSkill(options = []) {
|
|
182
184
|
printBanner();
|
|
183
|
-
console.log(`${c.bold}
|
|
185
|
+
console.log(`${c.bold}Universal AI Coding Agent Skill Installer${c.reset}\n`);
|
|
184
186
|
|
|
185
|
-
const isGlobal = options.includes('--global') || options.includes('-g');
|
|
186
187
|
const homeDir = process.env.HOME || process.env.USERPROFILE;
|
|
187
|
-
|
|
188
|
-
let targetSkillDir;
|
|
189
|
-
if (isGlobal) {
|
|
190
|
-
targetSkillDir = path.join(homeDir, '.gemini', 'config', 'skills', 'paper-plugin-dev');
|
|
191
|
-
} else {
|
|
192
|
-
// Current workspace customization root
|
|
193
|
-
targetSkillDir = path.resolve(process.cwd(), '.agents', 'skills', 'paper-plugin-dev');
|
|
194
|
-
}
|
|
195
|
-
|
|
188
|
+
const cwd = process.cwd();
|
|
196
189
|
const srcSkillDir = path.join(ROOT_DIR, 'skills', 'paper-plugin-dev');
|
|
190
|
+
|
|
197
191
|
if (!fs.existsSync(srcSkillDir)) {
|
|
198
192
|
console.error(`${c.red}Error:${c.reset} Source skill directory not found at ${srcSkillDir}`);
|
|
199
193
|
process.exit(1);
|
|
200
194
|
}
|
|
201
195
|
|
|
202
|
-
|
|
196
|
+
// Parse target flags
|
|
197
|
+
let targetAgent = null;
|
|
198
|
+
const isGlobal = options.includes('--global') || options.includes('-g');
|
|
199
|
+
const isLocal = options.includes('--local') || options.includes('-l');
|
|
200
|
+
const isAll = options.includes('--all') || options.includes('-a');
|
|
201
|
+
|
|
202
|
+
for (let i = 0; i < options.length; i++) {
|
|
203
|
+
if (options[i].startsWith('--agent=')) {
|
|
204
|
+
targetAgent = options[i].split('=')[1].toLowerCase();
|
|
205
|
+
} else if (options[i] === '--agent' && options[i + 1]) {
|
|
206
|
+
targetAgent = options[i + 1].toLowerCase();
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// If no target specified and not --all, offer interactive prompt
|
|
211
|
+
if (!targetAgent && !isAll) {
|
|
212
|
+
console.log(`${c.bold}Supported AI Coding Agent Platforms:${c.reset}`);
|
|
213
|
+
console.log(` ${c.cyan}1.${c.reset} All Agents (Antigravity, Claude, Codex, Cursor, DeepSeek, Paseo, pi.dev, Windsurf)`);
|
|
214
|
+
console.log(` ${c.cyan}2.${c.reset} Antigravity / agy (~/.gemini/config/skills)`);
|
|
215
|
+
console.log(` ${c.cyan}3.${c.reset} Claude Code (~/.claude/skills & CLAUDE.md)`);
|
|
216
|
+
console.log(` ${c.cyan}4.${c.reset} OpenAI Codex (~/.codex/skills & CODEX.md)`);
|
|
217
|
+
console.log(` ${c.cyan}5.${c.reset} Cursor (.cursor/rules & .cursorrules)`);
|
|
218
|
+
console.log(` ${c.cyan}6.${c.reset} DeepSeek Harness (~/.deepseek & DEEPSEEK.md)`);
|
|
219
|
+
console.log(` ${c.cyan}7.${c.reset} Paseo (~/.paseo & .paseo/rules.md)`);
|
|
220
|
+
console.log(` ${c.cyan}8.${c.reset} pi.dev (~/.pi & .pi/rules.md)`);
|
|
221
|
+
console.log(` ${c.cyan}9.${c.reset} Windsurf (.windsurfrules)\n`);
|
|
222
|
+
|
|
223
|
+
const selection = await prompt('Select target agent platform [1-9]', '1');
|
|
224
|
+
const mapping = {
|
|
225
|
+
'1': 'all',
|
|
226
|
+
'2': 'agy',
|
|
227
|
+
'3': 'claude',
|
|
228
|
+
'4': 'codex',
|
|
229
|
+
'5': 'cursor',
|
|
230
|
+
'6': 'deepseek',
|
|
231
|
+
'7': 'paseo',
|
|
232
|
+
'8': 'pi',
|
|
233
|
+
'9': 'windsurf'
|
|
234
|
+
};
|
|
235
|
+
targetAgent = mapping[selection] || 'all';
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const agentsToInstall = (targetAgent === 'all' || isAll)
|
|
239
|
+
? ['agy', 'claude', 'codex', 'cursor', 'deepseek', 'paseo', 'pi', 'windsurf']
|
|
240
|
+
: [targetAgent];
|
|
241
|
+
|
|
242
|
+
console.log(`\n${c.yellow}Installing skill for:${c.reset} ${c.bold}${agentsToInstall.join(', ')}${c.reset}\n`);
|
|
243
|
+
|
|
244
|
+
for (const agent of agentsToInstall) {
|
|
245
|
+
try {
|
|
246
|
+
switch (agent) {
|
|
247
|
+
case 'agy':
|
|
248
|
+
case 'antigravity': {
|
|
249
|
+
const dest = (isLocal && !isGlobal)
|
|
250
|
+
? path.join(cwd, '.agents', 'skills', 'paper-plugin-dev')
|
|
251
|
+
: path.join(homeDir, '.gemini', 'config', 'skills', 'paper-plugin-dev');
|
|
252
|
+
copyDirRecursive(srcSkillDir, dest);
|
|
253
|
+
console.log(` ${c.green}✔${c.reset} ${c.bold}Antigravity (agy):${c.reset} Installed to ${c.dim}${dest}${c.reset}`);
|
|
254
|
+
break;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
case 'claude':
|
|
258
|
+
case 'claudecode': {
|
|
259
|
+
const globalDir = path.join(homeDir, '.claude', 'skills', 'paper-plugin-dev');
|
|
260
|
+
copyDirRecursive(srcSkillDir, globalDir);
|
|
261
|
+
const claudeMdSrc = path.join(ROOT_DIR, 'CLAUDE.md');
|
|
262
|
+
if (fs.existsSync(claudeMdSrc)) {
|
|
263
|
+
fs.copyFileSync(claudeMdSrc, path.join(cwd, 'CLAUDE.md'));
|
|
264
|
+
}
|
|
265
|
+
console.log(` ${c.green}✔${c.reset} ${c.bold}Claude Code:${c.reset} Skill placed in ~/.claude/skills and workspace ${c.dim}CLAUDE.md${c.reset}`);
|
|
266
|
+
break;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
case 'codex':
|
|
270
|
+
case 'openai': {
|
|
271
|
+
const globalDir = path.join(homeDir, '.codex', 'skills', 'paper-plugin-dev');
|
|
272
|
+
copyDirRecursive(srcSkillDir, globalDir);
|
|
273
|
+
const codexMdSrc = path.join(ROOT_DIR, 'CODEX.md');
|
|
274
|
+
if (fs.existsSync(codexMdSrc)) {
|
|
275
|
+
fs.copyFileSync(codexMdSrc, path.join(cwd, 'CODEX.md'));
|
|
276
|
+
}
|
|
277
|
+
console.log(` ${c.green}✔${c.reset} ${c.bold}OpenAI Codex:${c.reset} Placed in ~/.codex/skills and workspace ${c.dim}CODEX.md${c.reset}`);
|
|
278
|
+
break;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
case 'cursor': {
|
|
282
|
+
const cursorRulesDir = path.join(cwd, '.cursor', 'rules');
|
|
283
|
+
fs.mkdirSync(cursorRulesDir, { recursive: true });
|
|
284
|
+
const mdcSrc = path.join(ROOT_DIR, '.cursor', 'rules', 'paper-plugin-dev.mdc');
|
|
285
|
+
if (fs.existsSync(mdcSrc)) {
|
|
286
|
+
fs.copyFileSync(mdcSrc, path.join(cursorRulesDir, 'paper-plugin-dev.mdc'));
|
|
287
|
+
}
|
|
288
|
+
const cursorRulesSrc = path.join(ROOT_DIR, '.cursorrules');
|
|
289
|
+
if (fs.existsSync(cursorRulesSrc)) {
|
|
290
|
+
fs.copyFileSync(cursorRulesSrc, path.join(cwd, '.cursorrules'));
|
|
291
|
+
}
|
|
292
|
+
console.log(` ${c.green}✔${c.reset} ${c.bold}Cursor:${c.reset} Created ${c.dim}.cursor/rules/paper-plugin-dev.mdc${c.reset} and ${c.dim}.cursorrules${c.reset}`);
|
|
293
|
+
break;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
case 'deepseek': {
|
|
297
|
+
const globalDir = path.join(homeDir, '.deepseek', 'skills', 'paper-plugin-dev');
|
|
298
|
+
copyDirRecursive(srcSkillDir, globalDir);
|
|
299
|
+
const deepseekSrc = path.join(ROOT_DIR, 'DEEPSEEK.md');
|
|
300
|
+
if (fs.existsSync(deepseekSrc)) {
|
|
301
|
+
fs.copyFileSync(deepseekSrc, path.join(cwd, 'DEEPSEEK.md'));
|
|
302
|
+
}
|
|
303
|
+
console.log(` ${c.green}✔${c.reset} ${c.bold}DeepSeek Harness:${c.reset} Placed in ~/.deepseek/skills and workspace ${c.dim}DEEPSEEK.md${c.reset}`);
|
|
304
|
+
break;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
case 'paseo': {
|
|
308
|
+
const globalDir = path.join(homeDir, '.paseo', 'skills', 'paper-plugin-dev');
|
|
309
|
+
copyDirRecursive(srcSkillDir, globalDir);
|
|
310
|
+
const paseoRulesDir = path.join(cwd, '.paseo');
|
|
311
|
+
fs.mkdirSync(paseoRulesDir, { recursive: true });
|
|
312
|
+
const paseoSrc = path.join(ROOT_DIR, '.paseo', 'rules.md');
|
|
313
|
+
if (fs.existsSync(paseoSrc)) {
|
|
314
|
+
fs.copyFileSync(paseoSrc, path.join(paseoRulesDir, 'rules.md'));
|
|
315
|
+
}
|
|
316
|
+
console.log(` ${c.green}✔${c.reset} ${c.bold}Paseo:${c.reset} Placed in ~/.paseo/skills and workspace ${c.dim}.paseo/rules.md${c.reset}`);
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
case 'pi':
|
|
321
|
+
case 'pidev': {
|
|
322
|
+
const globalDir = path.join(homeDir, '.pi', 'skills', 'paper-plugin-dev');
|
|
323
|
+
copyDirRecursive(srcSkillDir, globalDir);
|
|
324
|
+
const piRulesDir = path.join(cwd, '.pi');
|
|
325
|
+
fs.mkdirSync(piRulesDir, { recursive: true });
|
|
326
|
+
const piSrc = path.join(ROOT_DIR, '.pi', 'rules.md');
|
|
327
|
+
if (fs.existsSync(piSrc)) {
|
|
328
|
+
fs.copyFileSync(piSrc, path.join(piRulesDir, 'rules.md'));
|
|
329
|
+
}
|
|
330
|
+
console.log(` ${c.green}✔${c.reset} ${c.bold}pi.dev:${c.reset} Placed in ~/.pi/skills and workspace ${c.dim}.pi/rules.md${c.reset}`);
|
|
331
|
+
break;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
case 'windsurf': {
|
|
335
|
+
const windsurfSrc = path.join(ROOT_DIR, '.windsurfrules');
|
|
336
|
+
if (fs.existsSync(windsurfSrc)) {
|
|
337
|
+
fs.copyFileSync(windsurfSrc, path.join(cwd, '.windsurfrules'));
|
|
338
|
+
}
|
|
339
|
+
console.log(` ${c.green}✔${c.reset} ${c.bold}Windsurf:${c.reset} Placed ${c.dim}.windsurfrules${c.reset} in workspace root`);
|
|
340
|
+
break;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
default:
|
|
344
|
+
console.log(` ${c.yellow}⚠ Unknown agent '${agent}'. Skipped.${c.reset}`);
|
|
345
|
+
}
|
|
346
|
+
} catch (err) {
|
|
347
|
+
console.error(` ${c.red}✘ Failed installing for ${agent}:${c.reset}`, err.message);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
console.log(`\n${c.green}${c.bold}✔ Skill configuration completed successfully!${c.reset}`);
|
|
352
|
+
console.log(`Your AI coding agents now enforce modern Paper & Folia exploit-proof standards.\n`);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Scaffolder for Overpowered (OP) Recipes
|
|
356
|
+
async function handleGenerate(recipeArg, nameArg) {
|
|
357
|
+
printBanner();
|
|
358
|
+
const recipe = (recipeArg || '').toLowerCase();
|
|
359
|
+
|
|
360
|
+
const supported = ['component-item', 'display-entity', 'mace-combat', 'menu-gui'];
|
|
361
|
+
|
|
362
|
+
if (!recipe || !supported.some(s => s.includes(recipe) || recipe.includes(s.split('-')[0]))) {
|
|
363
|
+
console.log(`${c.bold}Available Overpowered (OP) Code Generators:${c.reset}\n`);
|
|
364
|
+
console.log(` ${c.cyan}1. component-item${c.reset} ${c.gray}Modern 1.20.5+ / 1.21+ / 26.x DataComponent item builder${c.reset}`);
|
|
365
|
+
console.log(` ${c.cyan}2. display-entity${c.reset} ${c.gray}Zero-lag interpolated TextDisplay & ItemDisplay hologram engine${c.reset}`);
|
|
366
|
+
console.log(` ${c.cyan}3. mace-combat${c.reset} ${c.gray}1.21 Tricky Trials Mace smash & wind burst kinetic listener${c.reset}`);
|
|
367
|
+
console.log(` ${c.cyan}4. menu-gui${c.reset} ${c.gray}Anti-dupe debounce-protected inventory menu controller${c.reset}\n`);
|
|
368
|
+
console.log(`Usage: ${c.cyan}npx papermc-skill generate <recipe> [ClassName]${c.reset}\n`);
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const cwd = process.cwd();
|
|
373
|
+
|
|
374
|
+
if (recipe.includes('item') || recipe.includes('component')) {
|
|
375
|
+
const className = nameArg || 'ModernItemBuilder';
|
|
376
|
+
const filePath = path.join(cwd, `${className}.java`);
|
|
377
|
+
const code = `package com.example.item;
|
|
378
|
+
|
|
379
|
+
import io.papermc.paper.datacomponent.DataComponentTypes;
|
|
380
|
+
import io.papermc.paper.datacomponent.item.CustomModelData;
|
|
381
|
+
import io.papermc.paper.datacomponent.item.FoodProperties;
|
|
382
|
+
import io.papermc.paper.datacomponent.item.ItemLore;
|
|
383
|
+
import net.kyori.adventure.text.Component;
|
|
384
|
+
import net.kyori.adventure.text.format.NamedTextColor;
|
|
385
|
+
import net.kyori.adventure.text.format.TextDecoration;
|
|
386
|
+
import org.bukkit.Material;
|
|
387
|
+
import org.bukkit.inventory.ItemStack;
|
|
388
|
+
|
|
389
|
+
import java.util.List;
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Modern Data Component Item Builder (MC 1.20.5 - 26.x compatible).
|
|
393
|
+
* Zero legacy NBT. Type-safe and crash-proof.
|
|
394
|
+
*/
|
|
395
|
+
public final class ${className} {
|
|
396
|
+
|
|
397
|
+
public static ItemStack createMythicRelic() {
|
|
398
|
+
ItemStack item = ItemStack.of(Material.NETHERITE_SWORD);
|
|
399
|
+
|
|
400
|
+
// Direct Adventure Item Name & Lore
|
|
401
|
+
item.setData(DataComponentTypes.ITEM_NAME, Component.text("Aegis of the Void", NamedTextColor.LIGHT_PURPLE, TextDecoration.BOLD));
|
|
402
|
+
item.setData(DataComponentTypes.LORE, ItemLore.lore(List.of(
|
|
403
|
+
Component.text("Harvested from trial chambers.", NamedTextColor.GRAY),
|
|
404
|
+
Component.empty(),
|
|
405
|
+
Component.text("✦ Ability: Kinetic Shockwave on hit", NamedTextColor.AQUA)
|
|
406
|
+
)));
|
|
407
|
+
|
|
408
|
+
// Modern Custom Model Data (floats, strings, flags)
|
|
409
|
+
item.setData(DataComponentTypes.CUSTOM_MODEL_DATA, CustomModelData.customModelData()
|
|
410
|
+
.addFloat(2.5f)
|
|
411
|
+
.addString("aegis_void_model")
|
|
412
|
+
.build()
|
|
413
|
+
);
|
|
414
|
+
|
|
415
|
+
// Component Overrides
|
|
416
|
+
item.setData(DataComponentTypes.ENCHANTMENT_GLINT_OVERRIDE, true);
|
|
417
|
+
item.setData(DataComponentTypes.MAX_STACK_SIZE, 1);
|
|
418
|
+
item.setData(DataComponentTypes.UNBREAKABLE, true);
|
|
419
|
+
|
|
420
|
+
return item;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
`;
|
|
424
|
+
fs.writeFileSync(filePath, code, 'utf-8');
|
|
425
|
+
console.log(`${c.green}✔ Generated Modern Data-Component Item Builder:${c.reset} ${c.bold}${filePath}${c.reset}\n`);
|
|
426
|
+
} else if (recipe.includes('display') || recipe.includes('hologram')) {
|
|
427
|
+
const className = nameArg || 'DisplayHologramEngine';
|
|
428
|
+
const filePath = path.join(cwd, `${className}.java`);
|
|
429
|
+
const code = `package com.example.display;
|
|
430
|
+
|
|
431
|
+
import net.kyori.adventure.text.Component;
|
|
432
|
+
import org.bukkit.Color;
|
|
433
|
+
import org.bukkit.Location;
|
|
434
|
+
import org.bukkit.entity.Display;
|
|
435
|
+
import org.bukkit.entity.TextDisplay;
|
|
436
|
+
import org.bukkit.util.Transformation;
|
|
437
|
+
import org.joml.AxisAngle4f;
|
|
438
|
+
import org.joml.Vector3f;
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* High-performance Display Entity Hologram Controller.
|
|
442
|
+
* Zero-tick entity lag. Client-side smooth JOML Matrix4f interpolation.
|
|
443
|
+
*/
|
|
444
|
+
public final class ${className} {
|
|
445
|
+
|
|
446
|
+
public static TextDisplay spawnSmoothHologram(Location loc, Component text) {
|
|
447
|
+
return loc.getWorld().spawn(loc, TextDisplay.class, display -> {
|
|
448
|
+
display.text(text);
|
|
449
|
+
display.setBillboard(Display.Billboard.CENTER);
|
|
450
|
+
display.setDefaultBackground(false);
|
|
451
|
+
display.setBackgroundColor(Color.fromARGB(150, 20, 20, 30));
|
|
452
|
+
display.setShadowed(true);
|
|
453
|
+
|
|
454
|
+
// Client-side smooth interpolation (20 ticks = 1s)
|
|
455
|
+
display.setInterpolationDuration(20);
|
|
456
|
+
display.setInterpolationDelay(0);
|
|
457
|
+
|
|
458
|
+
// Scale & Position Transformation
|
|
459
|
+
Transformation transform = new Transformation(
|
|
460
|
+
new Vector3f(0f, 0f, 0f),
|
|
461
|
+
new AxisAngle4f(0f, 0f, 1f, 0f),
|
|
462
|
+
new Vector3f(1.1f, 1.1f, 1.1f),
|
|
463
|
+
new AxisAngle4f(0f, 0f, 1f, 0f)
|
|
464
|
+
);
|
|
465
|
+
display.setTransformation(transform);
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
`;
|
|
470
|
+
fs.writeFileSync(filePath, code, 'utf-8');
|
|
471
|
+
console.log(`${c.green}✔ Generated Display Entity Hologram Controller:${c.reset} ${c.bold}${filePath}${c.reset}\n`);
|
|
472
|
+
} else if (recipe.includes('mace') || recipe.includes('combat')) {
|
|
473
|
+
const className = nameArg || 'MaceKineticCombatListener';
|
|
474
|
+
const filePath = path.join(cwd, `${className}.java`);
|
|
475
|
+
const code = `package com.example.combat;
|
|
476
|
+
|
|
477
|
+
import net.kyori.adventure.text.Component;
|
|
478
|
+
import net.kyori.adventure.text.format.NamedTextColor;
|
|
479
|
+
import org.bukkit.Location;
|
|
480
|
+
import org.bukkit.Material;
|
|
481
|
+
import org.bukkit.Particle;
|
|
482
|
+
import org.bukkit.Sound;
|
|
483
|
+
import org.bukkit.entity.LivingEntity;
|
|
484
|
+
import org.bukkit.entity.Player;
|
|
485
|
+
import org.bukkit.event.EventHandler;
|
|
486
|
+
import org.bukkit.event.EventPriority;
|
|
487
|
+
import org.bukkit.event.Listener;
|
|
488
|
+
import org.bukkit.event.entity.EntityDamageByEntityEvent;
|
|
489
|
+
import org.bukkit.util.Vector;
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* 1.21 Tricky Trials Mace Smash & Kinetic Shockwave Combat Listener.
|
|
493
|
+
*/
|
|
494
|
+
public final class ${className} implements Listener {
|
|
495
|
+
|
|
496
|
+
@EventHandler(priority = EventPriority.HIGH, ignoreCancelled = true)
|
|
497
|
+
public void onMaceSmash(EntityDamageByEntityEvent event) {
|
|
498
|
+
if (!(event.getDamager() instanceof Player attacker)) return;
|
|
499
|
+
if (!(event.getEntity() instanceof LivingEntity victim)) return;
|
|
500
|
+
|
|
501
|
+
if (attacker.getInventory().getItemInMainHand().getType() != Material.MACE) return;
|
|
502
|
+
|
|
503
|
+
float fallDistance = attacker.getFallDistance();
|
|
504
|
+
if (fallDistance > 1.5f) {
|
|
505
|
+
double extraDmg = Math.min(fallDistance * 3.5, 45.0);
|
|
506
|
+
event.setDamage(event.getDamage() + extraDmg);
|
|
507
|
+
|
|
508
|
+
Location loc = victim.getLocation();
|
|
509
|
+
loc.getWorld().spawnParticle(Particle.TRIAL_SPAWNER_DETECTION, loc.add(0, 0.5, 0), 30, 0.5, 0.2, 0.5, 0.05);
|
|
510
|
+
loc.getWorld().playSound(loc, Sound.ITEM_MACE_SMASH_GROUND, 1.2f, 1.0f);
|
|
511
|
+
|
|
512
|
+
// Kinetic shockwave knockback to nearby hostiles
|
|
513
|
+
loc.getWorld().getNearbyLivingEntities(loc, 4.5, e -> !e.equals(attacker) && !e.equals(victim))
|
|
514
|
+
.forEach(e -> {
|
|
515
|
+
Vector impulse = e.getLocation().toVector().subtract(loc.toVector()).normalize().multiply(1.3).setY(0.4);
|
|
516
|
+
e.setVelocity(impulse);
|
|
517
|
+
});
|
|
518
|
+
|
|
519
|
+
attacker.sendActionBar(Component.text("⚡ SMASH IMPACT: +" + String.format("%.1f", extraDmg) + " DMG", NamedTextColor.GOLD));
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
`;
|
|
524
|
+
fs.writeFileSync(filePath, code, 'utf-8');
|
|
525
|
+
console.log(`${c.green}✔ Generated Mace Kinetic Combat Listener:${c.reset} ${c.bold}${filePath}${c.reset}\n`);
|
|
526
|
+
} else if (recipe.includes('gui') || recipe.includes('menu')) {
|
|
527
|
+
const className = nameArg || 'SafeMenuController';
|
|
528
|
+
const filePath = path.join(cwd, `${className}.java`);
|
|
529
|
+
const code = `package com.example.gui;
|
|
530
|
+
|
|
531
|
+
import net.kyori.adventure.text.Component;
|
|
532
|
+
import org.bukkit.Bukkit;
|
|
533
|
+
import org.bukkit.entity.Player;
|
|
534
|
+
import org.bukkit.event.EventHandler;
|
|
535
|
+
import org.bukkit.event.EventPriority;
|
|
536
|
+
import org.bukkit.event.Listener;
|
|
537
|
+
import org.bukkit.event.inventory.InventoryClickEvent;
|
|
538
|
+
import org.bukkit.event.inventory.InventoryCloseEvent;
|
|
539
|
+
import org.bukkit.event.inventory.InventoryDragEvent;
|
|
540
|
+
import org.bukkit.inventory.Inventory;
|
|
541
|
+
import org.bukkit.inventory.InventoryHolder;
|
|
542
|
+
|
|
543
|
+
import java.util.Map;
|
|
544
|
+
import java.util.UUID;
|
|
545
|
+
import java.util.concurrent.ConcurrentHashMap;
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Exploit-proof Inventory Menu Controller with click debounce.
|
|
549
|
+
*/
|
|
550
|
+
public class ${className} implements InventoryHolder, Listener {
|
|
551
|
+
|
|
552
|
+
private final Inventory inventory;
|
|
553
|
+
private final Map<UUID, Long> clickDebounce = new ConcurrentHashMap<>();
|
|
554
|
+
|
|
555
|
+
public ${className}(Component title, int size) {
|
|
556
|
+
this.inventory = Bukkit.createInventory(this, size, title);
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
@Override
|
|
560
|
+
public Inventory getInventory() {
|
|
561
|
+
return inventory;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
@EventHandler(priority = EventPriority.HIGHEST, ignoreCancelled = true)
|
|
565
|
+
public void onClick(InventoryClickEvent event) {
|
|
566
|
+
if (!(event.getInventory().getHolder() instanceof ${className})) return;
|
|
567
|
+
|
|
568
|
+
event.setCancelled(true);
|
|
569
|
+
if (!(event.getWhoClicked() instanceof Player player)) return;
|
|
570
|
+
|
|
571
|
+
long now = System.currentTimeMillis();
|
|
572
|
+
long lastClick = clickDebounce.getOrDefault(player.getUniqueId(), 0L);
|
|
573
|
+
if (now - lastClick < 200) return;
|
|
574
|
+
clickDebounce.put(player.getUniqueId(), now);
|
|
575
|
+
|
|
576
|
+
if (event.getClickedInventory() == event.getInventory()) {
|
|
577
|
+
handleSlotAction(player, event.getSlot());
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
@EventHandler
|
|
582
|
+
public void onDrag(InventoryDragEvent event) {
|
|
583
|
+
if (event.getInventory().getHolder() instanceof ${className}) {
|
|
584
|
+
event.setCancelled(true);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
@EventHandler
|
|
589
|
+
public void onClose(InventoryCloseEvent event) {
|
|
590
|
+
if (event.getInventory().getHolder() instanceof ${className}) {
|
|
591
|
+
clickDebounce.remove(event.getPlayer().getUniqueId());
|
|
592
|
+
}
|
|
593
|
+
}
|
|
203
594
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
595
|
+
protected void handleSlotAction(Player player, int slot) {
|
|
596
|
+
// Slot action implementation
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
`;
|
|
600
|
+
fs.writeFileSync(filePath, code, 'utf-8');
|
|
601
|
+
console.log(`${c.green}✔ Generated Anti-Dupe Safe Menu Controller:${c.reset} ${c.bold}${filePath}${c.reset}\n`);
|
|
602
|
+
}
|
|
207
603
|
}
|
|
208
604
|
|
|
209
605
|
function handleDocs(topic) {
|
|
@@ -244,7 +640,7 @@ async function handleCheckUpdate(options = []) {
|
|
|
244
640
|
try {
|
|
245
641
|
const res = await fetch('https://fill.papermc.io/v3/projects/paper', {
|
|
246
642
|
headers: {
|
|
247
|
-
'User-Agent': 'papermc-skill/1.
|
|
643
|
+
'User-Agent': 'papermc-skill/1.2.0 (https://github.com/ardianryan/papermc-skill)'
|
|
248
644
|
}
|
|
249
645
|
});
|
|
250
646
|
|
|
@@ -263,15 +659,13 @@ async function handleCheckUpdate(options = []) {
|
|
|
263
659
|
console.log(`${c.green}${c.bold}✔ Successfully queried PaperMC Fill v3 API${c.reset}\n`);
|
|
264
660
|
console.log(`${c.bold}Available Major Version Groups:${c.reset} ${versionGroups.slice(0, 5).join(', ')}`);
|
|
265
661
|
|
|
266
|
-
// Candidate group (latest supported group)
|
|
267
662
|
const candidateGroup = versionGroups[0];
|
|
268
663
|
const latestVersions = data.versions[candidateGroup] || [];
|
|
269
664
|
const latestVersionId = latestVersions[0] || candidateGroup;
|
|
270
665
|
|
|
271
|
-
// Fetch details for candidate version
|
|
272
666
|
const verRes = await fetch(`https://fill.papermc.io/v3/projects/paper/versions/${latestVersionId}`, {
|
|
273
667
|
headers: {
|
|
274
|
-
'User-Agent': 'papermc-skill/1.
|
|
668
|
+
'User-Agent': 'papermc-skill/1.2.0 (https://github.com/ardianryan/papermc-skill)'
|
|
275
669
|
}
|
|
276
670
|
});
|
|
277
671
|
|
|
@@ -290,7 +684,6 @@ async function handleCheckUpdate(options = []) {
|
|
|
290
684
|
console.log(` Support: ${status === 'SUPPORTED' ? c.green : c.gray}${status}${c.reset}`);
|
|
291
685
|
console.log(` Minimum Java: ${c.bold}Java ${minJava}+${c.reset}\n`);
|
|
292
686
|
|
|
293
|
-
// Check template version
|
|
294
687
|
const templateBuildGradle = path.join(ROOT_DIR, 'templates', 'paper-modern-template', 'build.gradle.kts');
|
|
295
688
|
if (fs.existsSync(templateBuildGradle)) {
|
|
296
689
|
const content = fs.readFileSync(templateBuildGradle, 'utf-8');
|
|
@@ -303,7 +696,6 @@ async function handleCheckUpdate(options = []) {
|
|
|
303
696
|
let updated = content.replaceAll(currentConfigured, latestVersionId);
|
|
304
697
|
fs.writeFileSync(templateBuildGradle, updated, 'utf-8');
|
|
305
698
|
|
|
306
|
-
// Update paper-plugin.yml
|
|
307
699
|
const pluginYamlPath = path.join(ROOT_DIR, 'templates', 'paper-modern-template', 'src', 'main', 'resources', 'paper-plugin.yml');
|
|
308
700
|
if (fs.existsSync(pluginYamlPath)) {
|
|
309
701
|
let yml = fs.readFileSync(pluginYamlPath, 'utf-8');
|
|
@@ -323,31 +715,160 @@ async function handleCheckUpdate(options = []) {
|
|
|
323
715
|
}
|
|
324
716
|
}
|
|
325
717
|
|
|
718
|
+
async function handleAudit(targetDirInput) {
|
|
719
|
+
printBanner();
|
|
720
|
+
const targetDir = path.resolve(process.cwd(), targetDirInput || '.');
|
|
721
|
+
console.log(`${c.cyan}${c.bold}Running PaperMC Security & Exploit Audit on:${c.reset} ${c.dim}${targetDir}${c.reset}\n`);
|
|
722
|
+
|
|
723
|
+
if (!fs.existsSync(targetDir)) {
|
|
724
|
+
console.error(`${c.red}Directory does not exist:${c.reset} ${targetDir}`);
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
const javaFiles = [];
|
|
729
|
+
function scan(dir) {
|
|
730
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
731
|
+
for (const entry of entries) {
|
|
732
|
+
if (entry.name === '.git' || entry.name === 'node_modules' || entry.name === 'build' || entry.name === '.gradle') continue;
|
|
733
|
+
const full = path.join(dir, entry.name);
|
|
734
|
+
if (entry.isDirectory()) {
|
|
735
|
+
scan(full);
|
|
736
|
+
} else if (entry.name.endsWith('.java')) {
|
|
737
|
+
javaFiles.push(full);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
scan(targetDir);
|
|
743
|
+
|
|
744
|
+
if (javaFiles.length === 0) {
|
|
745
|
+
console.log(`${c.yellow}No .java files found to audit in this directory.${c.reset}`);
|
|
746
|
+
return;
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
console.log(`Auditing ${c.bold}${javaFiles.length}${c.reset} Java source files for exploit vectors & anti-patterns...\n`);
|
|
750
|
+
|
|
751
|
+
const checks = [
|
|
752
|
+
{
|
|
753
|
+
id: 'CHATCOLOR_LEGACY',
|
|
754
|
+
regex: /ChatColor\.[A-Z_]+/,
|
|
755
|
+
level: 'HIGH',
|
|
756
|
+
msg: 'Legacy ChatColor usage detected. Migrate to Adventure Component & MiniMessage to prevent console formatting corruption.'
|
|
757
|
+
},
|
|
758
|
+
{
|
|
759
|
+
id: 'SECTION_SIGN_COLOR',
|
|
760
|
+
regex: /["'].*?§[0-9a-fk-or].*?["']/,
|
|
761
|
+
level: 'HIGH',
|
|
762
|
+
msg: 'Hardcoded legacy section sign (§) detected. Use MiniMessage.miniMessage().deserialize(...) instead.'
|
|
763
|
+
},
|
|
764
|
+
{
|
|
765
|
+
id: 'PLAYER_STATIC_LEAK',
|
|
766
|
+
regex: /public\s+static\s+(?:final\s+)?(?:List|Set|Map|Collection)<(?:[^,>]+,\s*)?Player>/,
|
|
767
|
+
level: 'CRITICAL',
|
|
768
|
+
msg: 'Static collection holding Player instances detected! This causes massive Memory Leaks. Store UUID instead.'
|
|
769
|
+
},
|
|
770
|
+
{
|
|
771
|
+
id: 'LEGACY_NBT_USAGE',
|
|
772
|
+
regex: /net\.minecraft\.nbt\.|NBTTagCompound|NBTTagList/,
|
|
773
|
+
level: 'CRITICAL',
|
|
774
|
+
msg: 'Direct NBT manipulation detected! In MC 1.20.5+ through 26.x, use DataComponentTypes directly on ItemStack.'
|
|
775
|
+
},
|
|
776
|
+
{
|
|
777
|
+
id: 'BUKKIT_RUNNABLE',
|
|
778
|
+
regex: /new\s+BukkitRunnable\s*\(\s*\)/,
|
|
779
|
+
level: 'HIGH',
|
|
780
|
+
msg: 'BukkitRunnable is incompatible with Folia region threading. Migrate to UniversalScheduler or Entity/Region schedulers.'
|
|
781
|
+
},
|
|
782
|
+
{
|
|
783
|
+
id: 'SYNC_CHUNK_LOAD',
|
|
784
|
+
regex: /world\s*\.\s*getChunkAt\s*\(/,
|
|
785
|
+
level: 'HIGH',
|
|
786
|
+
msg: 'Synchronous chunk loading (world.getChunkAt) freezes the main server tick thread. Use getChunkAtAsync instead.'
|
|
787
|
+
},
|
|
788
|
+
{
|
|
789
|
+
id: 'INSECURE_SQL_CONCAT',
|
|
790
|
+
regex: /(?:executeQuery|executeUpdate|execute)\s*\(\s*["'].*?\+/i,
|
|
791
|
+
level: 'CRITICAL',
|
|
792
|
+
msg: 'Potential SQL Injection via string concatenation detected in query. Use PreparedStatement with ? placeholders.'
|
|
793
|
+
},
|
|
794
|
+
{
|
|
795
|
+
id: 'ARMOR_STAND_HOLOGRAM',
|
|
796
|
+
regex: /spawn\s*\([^,]+,\s*ArmorStand\.class/i,
|
|
797
|
+
level: 'INFO',
|
|
798
|
+
msg: 'ArmorStand spawned. For floating text or item pedestals, prefer modern TextDisplay/ItemDisplay for zero-lag smooth interpolation.'
|
|
799
|
+
},
|
|
800
|
+
{
|
|
801
|
+
id: 'RAW_DOUBLE_NAN_CHECK',
|
|
802
|
+
regex: /DoubleArgumentType\.doubleArg\s*\(/,
|
|
803
|
+
level: 'INFO',
|
|
804
|
+
msg: 'Double argument registered. Ensure execution validates !Double.isFinite(val) and checks for positive numbers.'
|
|
805
|
+
}
|
|
806
|
+
];
|
|
807
|
+
|
|
808
|
+
let issueCount = 0;
|
|
809
|
+
for (const file of javaFiles) {
|
|
810
|
+
const content = fs.readFileSync(file, 'utf-8');
|
|
811
|
+
const lines = content.split('\n');
|
|
812
|
+
const relPath = path.relative(targetDir, file);
|
|
813
|
+
|
|
814
|
+
lines.forEach((line, idx) => {
|
|
815
|
+
for (const check of checks) {
|
|
816
|
+
if (check.regex.test(line)) {
|
|
817
|
+
issueCount++;
|
|
818
|
+
const color = check.level === 'CRITICAL' ? c.red : (check.level === 'HIGH' ? c.yellow : c.cyan);
|
|
819
|
+
console.log(`[${color}${check.level}${c.reset}] ${c.bold}${relPath}:${idx + 1}${c.reset}`);
|
|
820
|
+
console.log(` ${c.dim}${line.trim()}${c.reset}`);
|
|
821
|
+
console.log(` ↳ ${check.msg}\n`);
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
});
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
if (issueCount === 0) {
|
|
828
|
+
console.log(`${c.green}${c.bold}✔ 0 Vulnerabilities or Anti-Patterns Found!${c.reset}`);
|
|
829
|
+
console.log(`${c.green}Codebase adheres to modern Paper & Folia exploit-proof security standards.${c.reset}\n`);
|
|
830
|
+
} else {
|
|
831
|
+
console.log(`${c.yellow}${c.bold}Found ${issueCount} potential issue(s).${c.reset} Review docs/11-security-and-exploit-prevention.md and docs/12-data-components-and-display-entities.md for guidelines.\n`);
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
|
|
326
835
|
function printHelp() {
|
|
327
836
|
printBanner();
|
|
328
837
|
console.log(`${c.bold}Usage:${c.reset}
|
|
329
838
|
npx papermc-skill <command> [options]
|
|
330
839
|
|
|
331
840
|
${c.bold}Commands:${c.reset}
|
|
332
|
-
${c.cyan}create [name]${c.reset} / ${c.cyan}init
|
|
333
|
-
${c.cyan}check-update [--apply]${c.reset} Query PaperMC Fill API for
|
|
334
|
-
${c.cyan}
|
|
841
|
+
${c.cyan}create [name]${c.reset} / ${c.cyan}init${c.reset} Scaffold a modern Paper & Folia plugin project
|
|
842
|
+
${c.cyan}check-update [--apply]${c.reset} Query PaperMC Fill API for latest Minecraft & Paper releases
|
|
843
|
+
${c.cyan}audit [path]${c.reset} Audit Java plugin codebase for security exploits & anti-patterns
|
|
844
|
+
${c.cyan}install-skill [options]${c.reset} Install skill to AI agents (agy, claude, codex, cursor, deepseek, paseo, pi, windsurf)
|
|
845
|
+
${c.cyan}generate <recipe> [name]${c.reset} Generate OP recipes (component-item, display-entity, mace-combat, menu-gui)
|
|
335
846
|
${c.cyan}docs [number|keyword]${c.reset} Browse or print documentation guides
|
|
336
847
|
${c.cyan}help${c.reset} / ${c.cyan}--help${c.reset} Display this help screen
|
|
337
848
|
${c.cyan}--version${c.reset} Show version information
|
|
338
849
|
|
|
850
|
+
${c.bold}Install Skill Options:${c.reset}
|
|
851
|
+
${c.cyan}--all${c.reset} Install to all supported agent configurations
|
|
852
|
+
${c.cyan}--agent <name>${c.reset} Target specific agent: agy, claude, codex, cursor, deepseek, paseo, pi, windsurf
|
|
853
|
+
${c.cyan}--global${c.reset} / ${c.cyan}-g${c.reset} Install globally to home directory
|
|
854
|
+
${c.cyan}--local${c.reset} / ${c.cyan}-l${c.reset} Install rules to current workspace directory
|
|
855
|
+
|
|
339
856
|
${c.bold}Examples:${c.reset}
|
|
340
|
-
${c.dim}#
|
|
341
|
-
npx papermc-skill
|
|
857
|
+
${c.dim}# Install skill to all AI coding agents${c.reset}
|
|
858
|
+
npx papermc-skill install-skill --all
|
|
859
|
+
|
|
860
|
+
${c.dim}# Install skill specifically for Claude Code and Cursor${c.reset}
|
|
861
|
+
npx papermc-skill install-skill --agent claude
|
|
862
|
+
npx papermc-skill install-skill --agent cursor
|
|
342
863
|
|
|
343
|
-
${c.dim}#
|
|
344
|
-
npx papermc-skill
|
|
864
|
+
${c.dim}# Generate modern DataComponent item builder${c.reset}
|
|
865
|
+
npx papermc-skill generate component-item EpicSwordBuilder
|
|
345
866
|
|
|
346
|
-
${c.dim}#
|
|
347
|
-
npx papermc-skill
|
|
867
|
+
${c.dim}# Generate zero-lag Display Entity hologram engine${c.reset}
|
|
868
|
+
npx papermc-skill generate display-entity HologramController
|
|
348
869
|
|
|
349
|
-
${c.dim}#
|
|
350
|
-
npx papermc-skill
|
|
870
|
+
${c.dim}# Audit current project for exploit vectors${c.reset}
|
|
871
|
+
npx papermc-skill audit
|
|
351
872
|
`);
|
|
352
873
|
}
|
|
353
874
|
|
|
@@ -371,11 +892,20 @@ async function main() {
|
|
|
371
892
|
case 'skill':
|
|
372
893
|
await handleInstallSkill(args.slice(1));
|
|
373
894
|
break;
|
|
895
|
+
case 'generate':
|
|
896
|
+
case 'gen':
|
|
897
|
+
await handleGenerate(args[1], args[2]);
|
|
898
|
+
break;
|
|
374
899
|
case 'docs':
|
|
375
900
|
case 'doc':
|
|
376
901
|
case 'guide':
|
|
377
902
|
handleDocs(args[1]);
|
|
378
903
|
break;
|
|
904
|
+
case 'audit':
|
|
905
|
+
case 'scan':
|
|
906
|
+
case 'security':
|
|
907
|
+
await handleAudit(args[1]);
|
|
908
|
+
break;
|
|
379
909
|
case '--version':
|
|
380
910
|
case '-v': {
|
|
381
911
|
const pkg = JSON.parse(fs.readFileSync(path.join(ROOT_DIR, 'package.json'), 'utf-8'));
|