itemscore-helper 1.2.0 → 1.2.1

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/README.md CHANGED
@@ -2,15 +2,24 @@
2
2
 
3
3
  Set up **any** AI assistant to build and edit custom Minecraft items for the [ItemsCore](https://www.coredevelopment.shop/plugins/itemscore) plugin. Works with Claude, Codex, Cursor, Gemini, and anything else that supports MCP or custom instructions. You do not need to know how to code.
4
4
 
5
- ## Easiest: let your AI set itself up
5
+ ## Easiest: paste one prompt into your AI
6
6
 
7
- Run this, copy what it prints, and paste it into your AI (Claude, Cursor, Gemini, Codex, anything):
7
+ Copy the prompt below and paste it into your AI (Claude, Cursor, Gemini, Codex, anything). It downloads and connects the ItemsCore tools itself, then asks what item you want - no terminal knowledge needed:
8
8
 
9
- ```bash
10
- npx itemscore-helper prompt
9
+ ```text
10
+ You are my coding agent and I do not know how to code, so do the whole setup yourself - actually run the commands, do not just tell me what to run.
11
+
12
+ 1. In my project folder, run this to download and connect the ItemsCore tools:
13
+ npx -y itemscore-helper install
14
+ It installs a local helper and wires an "itemscore" MCP server into this project. When it finishes, reload your MCP servers (or ask me to restart this app) so the itemscore tools load. If you truly cannot run commands, add this to your MCP config instead:
15
+ {"mcpServers":{"itemscore":{"command":"npx","args":["-y","itemscore-helper","serve"]}}}
16
+
17
+ 2. Read https://www.coredevelopment.shop/llms.txt so you know the ItemsCore item format and every method available.
18
+
19
+ 3. Tell me you are ready, then ask what item I want. Build a valid .import file (clean item JSON), validate it, and tell me to drop it in plugins/ItemsCore/imports/ and run /ic import in-game. The item stays fully editable in the in-game editor.
11
20
  ```
12
21
 
13
- Your AI reads the prompt, sets up the ItemsCore tools itself, then asks what item you want. No config files, no terminal knowledge needed.
22
+ The same prompt is on the [docs page](https://www.coredevelopment.shop/docs/itemscore) with a one-click Copy button.
14
23
 
15
24
  ## Or set it up in one command
16
25
 
@@ -32,7 +41,6 @@ The MCP server is `npx -y itemscore-helper serve`. It speaks the standard stdio
32
41
  | Command | What it does |
33
42
  |---|---|
34
43
  | `npx itemscore-helper` | Auto-detect your AI tools and connect the local MCP server |
35
- | `npx itemscore-helper prompt` | Print a prompt to paste into your AI so it sets itself up |
36
44
  | `npx itemscore-helper --dry-run` | Show what would change, without writing anything |
37
45
  | `npx itemscore-helper serve` | Run the local MCP server (this is what your AI runs) |
38
46
  | `npx itemscore-helper print` | Print the skill instructions to stdout |
@@ -81,7 +89,7 @@ After connecting, give your AI the `SKILL.md` file as its instructions (paste it
81
89
  ## How you use it
82
90
 
83
91
  1. Ask your AI for an item.
84
- 2. It writes a small `.item` (JSON) file and validates it.
92
+ 2. It writes a small `.import` (JSON) file and validates it.
85
93
  3. Put that file in `plugins/ItemsCore/imports/` on your server.
86
94
  4. Run `/ic import <name>` in-game. The item is live and stays editable in the in-game editor.
87
95
 
package/bin/cli.js CHANGED
@@ -11,16 +11,16 @@ const DOCS_URL = "https://www.coredevelopment.shop/docs/itemscore"
11
11
  const SKILL_DIR = path.join(__dirname, "..", "skill")
12
12
 
13
13
  const SETUP_PROMPT = [
14
- "Help me build a custom item for my Minecraft server with the ItemsCore plugin. Set yourself up first, then we'll build it together.",
14
+ "You are my coding agent and I do not know how to code, so do the whole setup yourself - actually run the commands, do not just tell me what to run.",
15
15
  "",
16
- "1. Fetch and read " + LLMS_URL + " - it explains how ItemsCore items work, the item JSON format, and every method you can use.",
17
- "",
18
- "2. Set up the ItemsCore tools so you can look up methods and validate items for me. In my project folder, run:",
16
+ "1. In my project folder, run this to download and connect the ItemsCore tools:",
19
17
  " npx -y itemscore-helper install",
20
- ' Then reload your MCP servers (or restart this app) so the "itemscore" tools appear. If you cannot run commands, add this to your MCP config instead:',
18
+ ' It installs a local helper and wires an "itemscore" MCP server into this project. When it finishes, reload your MCP servers (or ask me to restart this app) so the itemscore tools load. If you truly cannot run commands, add this to your MCP config instead:',
21
19
  ' {"mcpServers":{"itemscore":{"command":"npx","args":["-y","itemscore-helper","serve"]}}}',
22
20
  "",
23
- "3. Once you are set up, tell me you are ready and ask what item I want. Write a valid .item file, validate it, then tell me to drop it in plugins/ItemsCore/imports/ and run /ic import. The item stays fully editable in the in-game editor.",
21
+ "2. Read " + LLMS_URL + " so you know the ItemsCore item format and every method available.",
22
+ "",
23
+ "3. Tell me you are ready, then ask what item I want. Build a valid .import file (clean item JSON), validate it, and tell me to drop it in plugins/ItemsCore/imports/ and run /ic import in-game. The item stays fully editable in the in-game editor.",
24
24
  ].join("\n")
25
25
 
26
26
  function copyDir(src, dest) {
@@ -52,14 +52,13 @@ function printHelp() {
52
52
  "",
53
53
  "Usage",
54
54
  " npx itemscore-helper Auto-detect your AI tools and connect the local MCP server",
55
- " npx itemscore-helper prompt Print a prompt to paste into your AI so it sets itself up",
56
55
  " npx itemscore-helper --dry-run Show what would be changed, without writing anything",
57
56
  " npx itemscore-helper serve Run the local MCP server (this is what your AI runs)",
58
57
  " npx itemscore-helper print Print the skill instructions (SKILL.md) to stdout",
59
58
  " npx itemscore-helper mcp Print the MCP server config",
60
59
  " npx itemscore-helper help Show this help",
61
60
  "",
62
- "Zero terminal? Run `npx itemscore-helper prompt`, copy it, and paste it into your AI - it sets itself up.",
61
+ "Don't want to use a terminal? Copy the setup prompt from " + DOCS_URL + " and paste it into your AI - it does the rest.",
63
62
  "Supports Claude (Code & Desktop), Cursor, Gemini CLI, Codex, Windsurf and any MCP client.",
64
63
  "Docs: " + DOCS_URL,
65
64
  "",
@@ -76,10 +75,6 @@ function printSkill() {
76
75
  process.stdout.write(fs.readFileSync(path.join(SKILL_DIR, "SKILL.md"), "utf8"))
77
76
  }
78
77
 
79
- function printPrompt() {
80
- console.log(SETUP_PROMPT)
81
- }
82
-
83
78
  function manualLines() {
84
79
  return [
85
80
  " Add this to your AI client's MCP config (same for Claude, Cursor, Gemini):",
@@ -139,7 +134,6 @@ function main() {
139
134
  }
140
135
  if (args.cmd === "help") return printHelp()
141
136
  if (args.cmd === "mcp") return printMcp()
142
- if (args.cmd === "prompt") return printPrompt()
143
137
  if (args.cmd === "print") return printSkill()
144
138
  if (args.cmd !== "install") {
145
139
  console.error("Unknown command: " + args.cmd + "\nRun: npx itemscore-helper help")
package/bin/mcp.js CHANGED
@@ -106,7 +106,7 @@ async function main() {
106
106
  {
107
107
  title: "Validate an ItemsCore item",
108
108
  description:
109
- "Validate a clean item JSON object against the ItemsCore schema. Reports errors (must fix) and warnings (likely fine). Run this before giving the user a .item file.",
109
+ "Validate a clean item JSON object against the ItemsCore schema. Reports errors (must fix) and warnings (likely fine). Run this before giving the user a .import file.",
110
110
  inputSchema: { item: z.unknown().describe("The clean item JSON object to validate") },
111
111
  },
112
112
  async ({ item }) => jsonResult(M.validateItem(idx, item))
@@ -352,7 +352,7 @@
352
352
  "returnsValue": true,
353
353
  "useless": false,
354
354
  "documented": true,
355
- "description": "Builds a movement equation (using 't' for time) used to shape custom projectile paths.",
355
+ "description": "Builds a movement equation (using 't' for time in ticks) that shapes a custom projectile path. Axes are relative to the shooter's facing: X = forward (the way they look), Y = up, Z = left. A normal forward-flying projectile puts the motion on X, e.g. createEquationVector(\"t * 0.4\", \"0\", \"0\"); use Y for arc and Z to curve sideways.",
356
356
  "category": "Projectiles",
357
357
  "example": "core.createEquationVector(\"cos(t)\", \"0\", \"sin(t)\")"
358
358
  },
@@ -2509,7 +2509,7 @@
2509
2509
  "returnsValue": false,
2510
2510
  "useless": false,
2511
2511
  "documented": true,
2512
- "description": "Fires a custom ItemsCore projectile from a player, following the given path equation for a set number of ticks.",
2512
+ "description": "Fires a custom ItemsCore projectile from a player, following the given path equation for a set number of ticks. The equation axes are relative to where the player looks: X is forward, Y is up, Z is left - so a straight forward shot puts the motion on X.",
2513
2513
  "category": "Projectiles",
2514
2514
  "example": "core.shootProjectile(player, player.getEyeLocation(), \"FIREBALL\", null, 60, true, core.createEquationVector(\"t\", \"0\", \"t\"))"
2515
2515
  },
package/lib/manifest.js CHANGED
@@ -88,6 +88,123 @@ function getMethod(idx, name, binding) {
88
88
  return out
89
89
  }
90
90
 
91
+ const NUMERIC_TYPES = new Set([
92
+ "int", "long", "double", "float", "short", "byte",
93
+ "Integer", "Long", "Double", "Float", "Short", "Byte", "Number",
94
+ ])
95
+
96
+ // Object types that cause a hard ClassCastException when mismatched (cannot be coerced at runtime).
97
+ // Mismatching any of these against each other, or against a primitive/literal, is a real bug.
98
+ const HARD_OBJECT_TYPES = new Set(["Location", "ParticleDisplay", "Vector", "World"])
99
+
100
+ // Return types of the few bukkit getters agents use constantly. Lets us catch a swap even when one
101
+ // side is a raw Spigot call (e.g. player.getLocation()) that is not in the manifest.
102
+ const BUKKIT_GETTER_RETURNS = {
103
+ getlocation: "Location",
104
+ geteyelocation: "Location",
105
+ getworld: "World",
106
+ getdirection: "Vector",
107
+ getvelocity: "Vector",
108
+ }
109
+
110
+ // A handful of action variables whose type we know for sure.
111
+ const KNOWN_VARIABLE_TYPES = {
112
+ landlocation: "Location",
113
+ lastlocation: "Location",
114
+ }
115
+
116
+ function simpleType(t) {
117
+ if (!t) return ""
118
+ const dot = t.lastIndexOf(".")
119
+ return dot >= 0 ? t.slice(dot + 1) : t
120
+ }
121
+
122
+ // Returns a description of what an argument produces, as { kind, type? }.
123
+ // kind is one of: "unknown", "string", "number", "boolean", "type".
124
+ function producedType(idx, arg) {
125
+ if (arg === null) return { kind: "unknown" }
126
+ if (typeof arg === "string") return { kind: "string" }
127
+ if (typeof arg === "number") return { kind: "number" }
128
+ if (typeof arg === "boolean") return { kind: "boolean" }
129
+ if (typeof arg !== "object") return { kind: "unknown" }
130
+
131
+ if (typeof arg.var === "string") {
132
+ const known = KNOWN_VARIABLE_TYPES[arg.var.toLowerCase()]
133
+ return known ? asProduced(known) : { kind: "unknown" }
134
+ }
135
+ if (typeof arg.call === "string") {
136
+ const call = arg.call
137
+ if (call.includes(".")) {
138
+ const receiver = call.slice(0, call.indexOf("."))
139
+ const method = call.slice(call.indexOf(".") + 1)
140
+ if (idx.BINDING_NAMES.includes(receiver)) {
141
+ const found = getMethod(idx, method, receiver)
142
+ if (found.length === 1 && found[0].returns && found[0].returns !== "void") {
143
+ return asProduced(found[0].returns)
144
+ }
145
+ return { kind: "unknown" }
146
+ }
147
+ const getter = BUKKIT_GETTER_RETURNS[method.toLowerCase()]
148
+ return getter ? asProduced(getter) : { kind: "unknown" }
149
+ }
150
+ return { kind: "unknown" }
151
+ }
152
+ return { kind: "unknown" }
153
+ }
154
+
155
+ function asProduced(simpleName) {
156
+ const s = simpleType(simpleName)
157
+ if (NUMERIC_TYPES.has(s)) return { kind: "number" }
158
+ if (s === "boolean" || s === "Boolean") return { kind: "boolean" }
159
+ if (s === "String" || s === "CharSequence") return { kind: "string" }
160
+ return { kind: "type", type: s }
161
+ }
162
+
163
+ // Conservative: returns true unless the arg is a definite, uncoercible mismatch.
164
+ function argCompatible(expectedRaw, produced) {
165
+ const expected = simpleType(expectedRaw)
166
+ if (!expected || expected === "Object") return true
167
+ if (produced.kind === "unknown") return true
168
+
169
+ if (expected === "String" || expected === "CharSequence") return true
170
+ if (NUMERIC_TYPES.has(expected)) {
171
+ if (produced.kind === "type") return NUMERIC_TYPES.has(produced.type)
172
+ return true
173
+ }
174
+ if (expected === "boolean" || expected === "Boolean") {
175
+ if (produced.kind === "type") return false
176
+ return true
177
+ }
178
+ if (HARD_OBJECT_TYPES.has(expected)) {
179
+ if (produced.kind === "type") return produced.type === expected || !HARD_OBJECT_TYPES.has(produced.type)
180
+ return false
181
+ }
182
+ return true
183
+ }
184
+
185
+ function describeProduced(produced) {
186
+ if (produced.kind === "type") return produced.type
187
+ if (produced.kind === "string") return "string"
188
+ if (produced.kind === "number") return "number"
189
+ if (produced.kind === "boolean") return "boolean"
190
+ return "value"
191
+ }
192
+
193
+ function validateArgs(idx, method, args, p, errors) {
194
+ if (!method || !Array.isArray(method.params) || !Array.isArray(args)) return
195
+ for (let i = 0; i < args.length && i < method.params.length; i++) {
196
+ const param = method.params[i]
197
+ const expected = param.type || param.jvmType
198
+ const produced = producedType(idx, args[i])
199
+ if (!argCompatible(expected, produced)) {
200
+ errors.push(
201
+ p + '.args[' + i + '] (parameter "' + param.name + '") expects ' + simpleType(expected) +
202
+ " but a " + describeProduced(produced) + " was passed. Check the argument order against the method signature: " + method.signature
203
+ )
204
+ }
205
+ }
206
+ }
207
+
91
208
  function validateStep(idx, step, p, errors, warnings) {
92
209
  if (typeof step !== "object" || step === null) {
93
210
  errors.push(p + " must be an object")
@@ -105,8 +222,14 @@ function validateStep(idx, step, p, errors, warnings) {
105
222
  const found = getMethod(idx, method, receiver)
106
223
  if (found.length === 0) {
107
224
  errors.push(p + '.call "' + call + '" references unknown method "' + method + '" on binding "' + receiver + '"')
108
- } else if (found[0].useless) {
109
- warnings.push(p + '.call "' + call + '" is flagged useless (no real effect)')
225
+ } else {
226
+ if (found[0].useless) {
227
+ warnings.push(p + '.call "' + call + '" is flagged useless (no real effect)')
228
+ }
229
+ // Only type-check when the method is unambiguous (one overload) so we never guess wrong.
230
+ if (found.length === 1 && Array.isArray(step.args)) {
231
+ validateArgs(idx, found[0], step.args, p, errors)
232
+ }
110
233
  }
111
234
  } else if (!idx.KNOWN_RECEIVERS.has(receiver)) {
112
235
  warnings.push(p + '.call receiver "' + receiver + '" is not a known variable; make sure it is defined earlier or exists at runtime')
@@ -144,6 +267,9 @@ function validateItem(idx, item) {
144
267
  if (action.needBlock !== undefined && (typeof action.needBlock !== "string" || !NEED_BLOCK_VALUES.includes(action.needBlock))) {
145
268
  errors.push(ap + ".needBlock must be one of " + NEED_BLOCK_VALUES.join(", "))
146
269
  }
270
+ if (action.cooldown !== undefined && typeof action.cooldown !== "string" && typeof action.cooldown !== "number") {
271
+ warnings.push(ap + '.cooldown should be a duration like "5s", "1m", "1h" or a number of seconds')
272
+ }
147
273
  if (!Array.isArray(action.steps)) errors.push(ap + ".steps must be an array")
148
274
  else action.steps.forEach((s, si) => validateStep(idx, s, ap + ".steps[" + si + "]", errors, warnings))
149
275
  })
@@ -181,6 +307,7 @@ function itemSchema(idx) {
181
307
  action: {
182
308
  trigger: "string (required) - one of: " + idx.TRIGGER_NAMES.join(", "),
183
309
  needBlock: "BOTH | AIR | BLOCK - optional per-action override",
310
+ cooldown: 'optional - per-player reuse delay for this action. Any duration: a number plus a unit s/m/h, e.g. "5s", "45s", "2m", "90s", "1h" (a bare number means seconds). Omit or "0" for no cooldown.',
184
311
  steps: "Step[] - executed in order, combined by operatorToNext",
185
312
  },
186
313
  step: {
@@ -193,6 +320,8 @@ function itemSchema(idx) {
193
320
  },
194
321
  bukkitObjects:
195
322
  "player, shooter, victim, arrow, event, and any entity, block, world, location, or ItemStack returned by a method are real Bukkit/Spigot objects. You can call any standard Spigot method on them in a step (for example player.sendMessage, player.getHealth, player.getWorld, victim.setFireTicks), not only the ItemsCore methods. These are not in the method list below; see https://hub.spigotmc.org/javadocs/spigot/ for the full Spigot API. Prefer a core method when one exists.",
323
+ projectiles:
324
+ 'For core.shootProjectile + core.createEquationVector the equation axes are relative to where the player looks: X = forward, Y = up, Z = left. A straight forward-flying projectile puts the motion on X, e.g. core.createEquationVector("t * 0.4", "0", "0"). Use Y for arc/gravity and Z to curve sideways.',
196
325
  bindings: idx.bindings.map((b) => ({ name: b.name, description: b.description || "", methodCount: (b.methods || []).length })),
197
326
  variables: idx.variables,
198
327
  triggers: idx.triggers,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "itemscore-helper",
3
- "version": "1.2.0",
3
+ "version": "1.2.1",
4
4
  "description": "One command sets up any AI (Claude, Codex, Cursor, Gemini, and others) to build and edit custom Minecraft items for the ItemsCore plugin. Auto-detects your AI tools and connects a local MCP server that runs entirely on your machine.",
5
5
  "bin": {
6
6
  "itemscore-helper": "bin/cli.js",
@@ -31,7 +31,7 @@
31
31
  "homepage": "https://www.coredevelopment.shop/docs/itemscore",
32
32
  "repository": {
33
33
  "type": "git",
34
- "url": "https://github.com/Tc554/itemscore-helper.git"
34
+ "url": "https://github.com/Core-Pluginss/ItemsCore-Helper.git"
35
35
  },
36
36
  "license": "MIT",
37
37
  "author": "TastyCake",
@@ -31,6 +31,7 @@ This is the offline reference for the item format ItemsCore imports. When the li
31
31
  |---|---|---|
32
32
  | `trigger` | string, required | One of the triggers below |
33
33
  | `needBlock` | `BOTH` \| `AIR` \| `BLOCK` | Optional per-action override of the item default |
34
+ | `cooldown` | duration string | Optional per-player reuse delay. Any duration: a number plus a unit s/m/h, e.g. `"5s"`, `"45s"`, `"2m"`, `"90s"`, `"1h"` (a bare number means seconds) |
34
35
  | `steps` | Step[] | Run in order, combined by each step's `operatorToNext` |
35
36
 
36
37
  ### Triggers
@@ -131,6 +132,10 @@ These are not in the `core` / `particles` / `values` / `api` method list. For th
131
132
 
132
133
  Always confirm a method's exact name and parameters with `get_method` / `search_methods` before using it. Do not guess.
133
134
 
135
+ ## Projectiles
136
+
137
+ `core.shootProjectile` and `core.createEquationVector` use a movement equation whose axes are relative to where the player looks: **X = forward, Y = up, Z = left**. A straight forward-flying projectile puts the motion on X, e.g. `core.createEquationVector("t * 0.4", "0", "0")`. Use Y for arc/gravity and Z to curve left/right.
138
+
134
139
  ## Worked example: a healing ability wand
135
140
 
136
141
  ```json
package/skill/SKILL.md CHANGED
@@ -9,7 +9,7 @@ ItemsCore is a Minecraft (Bukkit/Spigot) plugin that lets a server owner create
9
9
 
10
10
  ## The one rule that matters
11
11
 
12
- Always produce a **clean item JSON** (the format described below). Never hand-write the plugin's internal `.item` YAML or its generated JavaScript `code`. When the user imports your JSON, the plugin builds **both** the runnable code **and** the in-game GUI action graph from it. That means an item you create this way still works **and** stays fully editable in the in-game editor. Hand-writing raw code produces an item the GUI cannot open, which is exactly the problem this format avoids.
12
+ Always produce a **clean item JSON** (the format described below). Never hand-write the plugin's internal item YAML or its generated JavaScript `code`. When the user imports your JSON, the plugin builds **both** the runnable code **and** the in-game GUI action graph from it. That means an item you create this way still works **and** stays fully editable in the in-game editor. Hand-writing raw code produces an item the GUI cannot open, which is exactly the problem this format avoids.
13
13
 
14
14
  ## Step 1: Get the API (do this first)
15
15
 
@@ -86,7 +86,7 @@ Run `validate_item` on your JSON. Fix every entry under `errors` before continui
86
86
 
87
87
  ## Step 4: Hand it to the user
88
88
 
89
- 1. Save the JSON as `<name>.item` (or `<name>.json`).
89
+ 1. Save the JSON as `<name>.import` (or `<name>.json`).
90
90
  2. Tell the user to put the file in `plugins/ItemsCore/imports/` on their server.
91
91
  3. Tell them to run `/ic import <name>` in-game.
92
92