@evitcastudio/kit 3.1.0 → 3.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.
Files changed (40) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +224 -123
  3. package/lib/bundle/cli/cli.js +491 -59
  4. package/lib/bundle/cli/kit-game-templates/multi/_gitignore +180 -180
  5. package/lib/bundle/cli/kit-game-templates/multi/package.json +3 -3
  6. package/lib/bundle/cli/kit-game-templates/multi/resource.json +1 -0
  7. package/lib/bundle/cli/kit-game-templates/multi/src/client/packets/s-packets.ts +4 -4
  8. package/lib/bundle/cli/kit-game-templates/multi/src/server/packets/c-packets.ts +4 -4
  9. package/lib/bundle/cli/kit-game-templates/single/_gitignore +180 -180
  10. package/lib/bundle/cli/kit-game-templates/single/package.json +3 -3
  11. package/lib/bundle/cli/kit-game-templates/single/resource.json +1 -0
  12. package/lib/cli/app-bundler.d.ts +29 -0
  13. package/lib/cli/app-bundler.d.ts.map +1 -0
  14. package/lib/cli/app-bundler.js +230 -0
  15. package/lib/cli/create.d.ts +11 -0
  16. package/lib/cli/create.d.ts.map +1 -0
  17. package/lib/cli/create.js +88 -0
  18. package/lib/cli/doctor.d.ts +9 -0
  19. package/lib/cli/doctor.d.ts.map +1 -0
  20. package/lib/cli/doctor.js +179 -0
  21. package/lib/cli/host.d.ts +49 -0
  22. package/lib/cli/host.d.ts.map +1 -0
  23. package/lib/cli/host.js +127 -0
  24. package/lib/cli/init.d.ts +2 -0
  25. package/lib/cli/init.d.ts.map +1 -1
  26. package/lib/cli/init.js +42 -15
  27. package/lib/cli/main.d.ts +22 -1
  28. package/lib/cli/main.d.ts.map +1 -1
  29. package/lib/cli/main.js +28 -1
  30. package/lib/cli/resource-builder.d.ts +1 -1
  31. package/lib/cli/resource-builder.d.ts.map +1 -1
  32. package/lib/cli/resource-builder.js +264 -19
  33. package/lib/cli/types.d.ts +12 -6
  34. package/lib/cli/types.d.ts.map +1 -1
  35. package/lib/types/vylo.d.ts +3848 -3848
  36. package/lib/vendor/vyi/index.js +26 -26
  37. package/package.json +73 -73
  38. package/lib/bundle/cli/kit-game-templates/multi/bun-build.ts +0 -109
  39. package/lib/bundle/cli/kit-game-templates/single/bun-build.ts +0 -33
  40. package/lib/bundle/cli/kit-game-templates/single/bun-serve.ts +0 -28
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /*!
4
- * @evitcastudio/kit@3.1.0 git+https://github.com/EvitcaStudio/Kit.git
5
- * Compiled Tue, 23 Jun 2026 22:30:34 UTC
4
+ * @evitcastudio/kit@3.1.1 git+https://github.com/EvitcaStudio/Kit.git
5
+ * Compiled Wed, 09 Sep 2026 07:29:03 UTC
6
6
  * Copyright (c) 2026 Jared Bates, Evitca Studio, "doubleactii"
7
7
  *
8
8
  * @evitcastudio/kit is licensed under the MIT License.
@@ -1943,8 +1943,8 @@ var {
1943
1943
  } = import__.default;
1944
1944
 
1945
1945
  // src/cli/resource-builder.ts
1946
- import { promises as fs } from "fs";
1947
- import { join, extname, basename } from "path";
1946
+ import { promises as fs, watch as fsWatch } from "fs";
1947
+ import { join, extname, basename, resolve } from "path";
1948
1948
 
1949
1949
  // node_modules/chalk/source/vendor/ansi-styles/index.js
1950
1950
  var ANSI_BACKGROUND_OFFSET = 10;
@@ -2475,19 +2475,19 @@ function v4(options, buf, offset) {
2475
2475
  }
2476
2476
  var v4_default = v4;
2477
2477
  // src/vendor/vyi/index.js
2478
- /*!
2479
- * vyi@4.1.0 https://github.com/EvitcaStudio/vyi
2480
- * Compiled Sun, 21 Jun 2026 10:57:24 UTC
2481
- * Copyright (c) 2026 Evitca Studio, "doubleactii"
2482
- *
2483
- * vyi is privately licensed.
2478
+ /*!
2479
+ * vyi@4.1.0 https://github.com/EvitcaStudio/vyi
2480
+ * Compiled Sun, 21 Jun 2026 10:57:24 UTC
2481
+ * Copyright (c) 2026 Evitca Studio, "doubleactii"
2482
+ *
2483
+ * vyi is privately licensed.
2484
2484
  */
2485
- /*!
2486
- * logger@1.0.0 https://github.com/EvitcaStudio/Logger
2487
- * Compiled Mon, 10 Nov 2025 05:36:23 UTC
2488
- * Copyright (c) 2025 Evitca Studio, "doubleactii"
2489
- *
2490
- * logger is privately licensed.
2485
+ /*!
2486
+ * logger@1.0.0 https://github.com/EvitcaStudio/Logger
2487
+ * Compiled Mon, 10 Nov 2025 05:36:23 UTC
2488
+ * Copyright (c) 2025 Evitca Studio, "doubleactii"
2489
+ *
2490
+ * logger is privately licensed.
2491
2491
  */
2492
2492
 
2493
2493
  class Logger {
@@ -2758,19 +2758,19 @@ class Frame {
2758
2758
  return frameData;
2759
2759
  }
2760
2760
  }
2761
- /*!
2762
- * icon-point@2.1.0 https://github.com/EvitcaStudio/IconPoint
2763
- * Compiled Mon, 10 Nov 2025 09:52:21 UTC
2764
- * Copyright (c) 2025 Evitca Studio, "doubleactii"
2765
- *
2766
- * icon-point is privately licensed.
2761
+ /*!
2762
+ * icon-point@2.1.0 https://github.com/EvitcaStudio/IconPoint
2763
+ * Compiled Mon, 10 Nov 2025 09:52:21 UTC
2764
+ * Copyright (c) 2025 Evitca Studio, "doubleactii"
2765
+ *
2766
+ * icon-point is privately licensed.
2767
2767
  */
2768
- /*!
2769
- * logger@1.0.0 https://github.com/EvitcaStudio/Logger
2770
- * Compiled Mon, 10 Nov 2025 05:36:23 UTC
2771
- * Copyright (c) 2025 Evitca Studio, "doubleactii"
2772
- *
2773
- * logger is privately licensed.
2768
+ /*!
2769
+ * logger@1.0.0 https://github.com/EvitcaStudio/Logger
2770
+ * Compiled Mon, 10 Nov 2025 05:36:23 UTC
2771
+ * Copyright (c) 2025 Evitca Studio, "doubleactii"
2772
+ *
2773
+ * logger is privately licensed.
2774
2774
  */
2775
2775
 
2776
2776
  class Logger2 {
@@ -7999,7 +7999,9 @@ var isVerbose = false;
7999
7999
  var ignoringSound = false;
8000
8000
  var resourceInDirectory = "";
8001
8001
  var resourceOutDirectory = "";
8002
+ var customManifestPath = undefined;
8002
8003
  var resourcesToProcess = [];
8004
+ var subdirectoriesToMirror = [];
8003
8005
  function initializeResourceJSON() {
8004
8006
  return RESOURCE_TYPES.reduce((acc, type) => {
8005
8007
  acc[type] = [];
@@ -8046,8 +8048,14 @@ async function processDirectory(pDirectoryPath) {
8046
8048
  const contents = await fs.readdir(pDirectoryPath);
8047
8049
  for (const item of contents) {
8048
8050
  const itemPath = join(pDirectoryPath, item);
8051
+ if (resolve(itemPath) === resourceOutDirectory) {
8052
+ continue;
8053
+ }
8049
8054
  const stats = await fs.stat(itemPath);
8050
8055
  if (stats.isDirectory()) {
8056
+ if (pDirectoryPath === resourceInDirectory) {
8057
+ subdirectoriesToMirror.push(item);
8058
+ }
8051
8059
  await processDirectory(itemPath);
8052
8060
  } else if (isValidExtension(extname(itemPath).slice(1))) {
8053
8061
  prepareFileForProcessing(itemPath);
@@ -8060,6 +8068,22 @@ async function processDirectory(pDirectoryPath) {
8060
8068
  function isValidExtension(pExtension) {
8061
8069
  return VALID_EXTENSIONS.includes(pExtension);
8062
8070
  }
8071
+ async function mirrorDirectory(pSourceDir, pDestDir) {
8072
+ const entries = await fs.readdir(pSourceDir, { withFileTypes: true });
8073
+ await fs.mkdir(pDestDir, { recursive: true });
8074
+ for (const entry of entries) {
8075
+ const srcPath = join(pSourceDir, entry.name);
8076
+ const destPath = join(pDestDir, entry.name);
8077
+ if (entry.isDirectory()) {
8078
+ await mirrorDirectory(srcPath, destPath);
8079
+ } else {
8080
+ const ext = extname(entry.name).slice(1);
8081
+ if (!isValidExtension(ext)) {
8082
+ await fs.copyFile(srcPath, destPath);
8083
+ }
8084
+ }
8085
+ }
8086
+ }
8063
8087
  async function processAllFiles() {
8064
8088
  try {
8065
8089
  await clearResourceTypeDirectories(`${resourceOutDirectory}/resources`);
@@ -8074,10 +8098,19 @@ async function processAllFiles() {
8074
8098
  return copyFile(filePath, destination, resourceName);
8075
8099
  });
8076
8100
  await Promise.all(copyOperations);
8101
+ for (const subDir of subdirectoriesToMirror) {
8102
+ const srcPath = join(resourceInDirectory, subDir);
8103
+ const destPath = join(resourceOutDirectory, "resources", subDir);
8104
+ await mirrorDirectory(srcPath, destPath);
8105
+ }
8077
8106
  logVerbose(`[Kit CLI] All resources have been processed.`);
8078
8107
  await saveResourceJSON();
8079
8108
  const boundsData = await buildBoundsJSON();
8080
8109
  await saveBoundsJSON(boundsData);
8110
+ const pointsData = await buildIconPointsJSON();
8111
+ await saveIconPointsJSON(pointsData);
8112
+ const sizesData = await buildSizesJSON();
8113
+ await saveSizesJSON(sizesData);
8081
8114
  } catch (pError) {
8082
8115
  const errorMessage = pError instanceof Error ? pError.message : String(pError);
8083
8116
  logError(`[Error] Processing files in batch: ${errorMessage}`);
@@ -8130,11 +8163,114 @@ async function buildBoundsJSON() {
8130
8163
  async function saveBoundsJSON(pBoundsData) {
8131
8164
  const filePath = "bounds.json";
8132
8165
  try {
8133
- await fs.writeFile(filePath, JSON.stringify(pBoundsData, null, 4));
8166
+ await fs.writeFile(filePath, JSON.stringify(pBoundsData));
8134
8167
  } catch (pError) {
8135
8168
  logError(`[Error] Saving bounds JSON: ${pError}`);
8136
8169
  }
8137
8170
  }
8171
+ async function buildIconPointsJSON() {
8172
+ const pointsData = {};
8173
+ for (const { filePath, type } of resourcesToProcess) {
8174
+ if (type !== "icon")
8175
+ continue;
8176
+ try {
8177
+ const fileBuffer = await fs.readFile(filePath);
8178
+ const vyi = new VYI().parse(fileBuffer);
8179
+ const atlasName = basename(filePath, ".vyi");
8180
+ const atlasEntry = {};
8181
+ for (const icon of vyi.getIcons()) {
8182
+ const iconName = icon.getName();
8183
+ const iconPoints = icon.getIconPointsExport();
8184
+ const hasIconPoints = iconPoints && iconPoints.length > 0;
8185
+ const iconEntry = {};
8186
+ if (hasIconPoints) {
8187
+ const pointsMap = {};
8188
+ for (const pt of iconPoints) {
8189
+ pointsMap[pt.id] = {
8190
+ width: pt.width,
8191
+ height: pt.height,
8192
+ x: pt.x,
8193
+ y: pt.y
8194
+ };
8195
+ }
8196
+ iconEntry.points = pointsMap;
8197
+ }
8198
+ const statesEntry = {};
8199
+ for (const state of icon.getStates()) {
8200
+ const stateName = state.getName();
8201
+ const statePoints = state.getIconPointsExport();
8202
+ if (statePoints && statePoints.length > 0) {
8203
+ const statePointsMap = {};
8204
+ for (const pt of statePoints) {
8205
+ statePointsMap[pt.id] = {
8206
+ width: pt.width,
8207
+ height: pt.height,
8208
+ x: pt.x,
8209
+ y: pt.y
8210
+ };
8211
+ }
8212
+ statesEntry[stateName] = {
8213
+ points: statePointsMap
8214
+ };
8215
+ }
8216
+ }
8217
+ if (Object.keys(statesEntry).length > 0) {
8218
+ iconEntry.states = statesEntry;
8219
+ }
8220
+ if (Object.keys(iconEntry).length > 0) {
8221
+ atlasEntry[iconName] = iconEntry;
8222
+ }
8223
+ }
8224
+ if (Object.keys(atlasEntry).length > 0) {
8225
+ pointsData[atlasName] = atlasEntry;
8226
+ }
8227
+ } catch (pError) {
8228
+ logError(`[Error] Failed to parse icon points from ${filePath}: ${pError}`);
8229
+ }
8230
+ }
8231
+ return pointsData;
8232
+ }
8233
+ async function saveIconPointsJSON(pPointsData) {
8234
+ const filePath = "icon-points.json";
8235
+ try {
8236
+ await fs.writeFile(filePath, JSON.stringify(pPointsData));
8237
+ } catch (pError) {
8238
+ logError(`[Error] Saving icon points JSON: ${pError}`);
8239
+ }
8240
+ }
8241
+ async function buildSizesJSON() {
8242
+ const sizesData = {};
8243
+ for (const { filePath, type } of resourcesToProcess) {
8244
+ if (type !== "icon")
8245
+ continue;
8246
+ try {
8247
+ const fileBuffer = await fs.readFile(filePath);
8248
+ const vyi = new VYI().parse(fileBuffer);
8249
+ const atlasName = basename(filePath, ".vyi");
8250
+ const atlasEntry = {};
8251
+ for (const icon of vyi.getIcons()) {
8252
+ atlasEntry[icon.getName()] = {
8253
+ width: icon.getWidth(),
8254
+ height: icon.getHeight()
8255
+ };
8256
+ }
8257
+ if (Object.keys(atlasEntry).length > 0) {
8258
+ sizesData[atlasName] = atlasEntry;
8259
+ }
8260
+ } catch (pError) {
8261
+ logError(`[Error] Failed to parse sizes from ${filePath}: ${pError}`);
8262
+ }
8263
+ }
8264
+ return sizesData;
8265
+ }
8266
+ async function saveSizesJSON(pSizesData) {
8267
+ const filePath = "sizes.json";
8268
+ try {
8269
+ await fs.writeFile(filePath, JSON.stringify(pSizesData));
8270
+ } catch (pError) {
8271
+ logError(`[Error] Saving sizes JSON: ${pError}`);
8272
+ }
8273
+ }
8138
8274
  async function clearResourceTypeDirectories(pBaseDirectory) {
8139
8275
  try {
8140
8276
  const directoryExists = await fs.stat(pBaseDirectory).then((stat) => stat.isDirectory()).catch(() => false);
@@ -8154,31 +8290,65 @@ async function copyFile(pSource, pDestinationDir, pNewName) {
8154
8290
  }
8155
8291
  }
8156
8292
  async function saveResourceJSON() {
8157
- const filePath = "resource.json";
8293
+ const jsonContent = JSON.stringify(resourceJSON, null, 4);
8294
+ const targetPath = customManifestPath || "resource.json";
8158
8295
  try {
8159
- await fs.writeFile(filePath, JSON.stringify(resourceJSON, null, 4));
8296
+ await fs.writeFile(targetPath, jsonContent, "utf8");
8160
8297
  } catch (pError) {
8161
- logError(`[Error] Saving resource JSON: ${pError}`);
8298
+ logError(`[Error] Saving resource JSON to ${targetPath}: ${pError}`);
8162
8299
  }
8163
8300
  }
8164
- async function processResources({ inDirectory, outDirectory, verbose, ignoreSound }) {
8165
- resourceInDirectory = inDirectory;
8166
- resourceOutDirectory = outDirectory;
8167
- isVerbose = verbose;
8168
- ignoringSound = ignoreSound;
8301
+ async function runBuild() {
8169
8302
  resourceJSON = initializeResourceJSON();
8170
8303
  resourcesToProcess.length = 0;
8171
- if (!resourceInDirectory || !resourceOutDirectory) {
8172
- logError("[Error] Input and output directories must be specified");
8173
- return;
8174
- }
8304
+ subdirectoriesToMirror.length = 0;
8175
8305
  await processDirectory(resourceInDirectory);
8176
- if (resourcesToProcess.length > 0) {
8306
+ if (resourcesToProcess.length > 0 || subdirectoriesToMirror.length > 0) {
8177
8307
  await processAllFiles();
8178
8308
  } else {
8179
8309
  logAlert("No resources found!");
8180
8310
  await saveResourceJSON();
8181
8311
  await saveBoundsJSON({});
8312
+ await saveIconPointsJSON({});
8313
+ await saveSizesJSON({});
8314
+ }
8315
+ logVerbose(`[Kit CLI] Resources built successfully.`);
8316
+ }
8317
+ async function runWatch() {
8318
+ await runBuild();
8319
+ console.log(source_default.cyan(`
8320
+ \uD83D\uDC40 Watching for changes in: ${source_default.bold(resourceInDirectory)}`));
8321
+ let debounceTimer = null;
8322
+ const watcher = fsWatch(resourceInDirectory, { recursive: true }, (_eventType, pFilename) => {
8323
+ if (!pFilename)
8324
+ return;
8325
+ if (debounceTimer)
8326
+ clearTimeout(debounceTimer);
8327
+ debounceTimer = setTimeout(async () => {
8328
+ console.log(source_default.dim(`
8329
+ Asset changed: ${pFilename}, rebuilding...`));
8330
+ await runBuild();
8331
+ }, 150);
8332
+ });
8333
+ process.on("SIGINT", () => {
8334
+ watcher.close();
8335
+ process.exit(0);
8336
+ });
8337
+ }
8338
+ async function processResources({ inDirectory, outDirectory, manifestPath, watch, verbose, ignoreSound }) {
8339
+ resourceInDirectory = inDirectory ? resolve(inDirectory) : "";
8340
+ resourceOutDirectory = outDirectory ? resolve(outDirectory) : "";
8341
+ customManifestPath = manifestPath;
8342
+ isVerbose = verbose;
8343
+ ignoringSound = ignoreSound;
8344
+ if (!resourceInDirectory || !resourceOutDirectory) {
8345
+ logError("[Error] Input and output directories must be specified");
8346
+ return;
8347
+ }
8348
+ if (watch) {
8349
+ await runWatch();
8350
+ } else {
8351
+ await runBuild();
8182
8352
  }
8183
8353
  }
8184
8354
  function logVerbose(pMessage) {
@@ -9246,7 +9416,7 @@ import os2 from "node:os";
9246
9416
  // package.json
9247
9417
  var package_default = {
9248
9418
  name: "@evitcastudio/kit",
9249
- version: "3.1.0",
9419
+ version: "3.1.1",
9250
9420
  author: "doubleactii 56242467+doubleactii@users.noreply.github.com (https://evitcastudio.com)",
9251
9421
  main: "./lib/index.js",
9252
9422
  types: "./lib/index.d.ts",
@@ -9400,7 +9570,9 @@ Note: Kit projects cannot build or run without Bun.`);
9400
9570
  }
9401
9571
  let projectName = pOptions.projectName;
9402
9572
  let gameType = "single";
9403
- if (!pOptions.single && !pOptions.multi && !projectName) {
9573
+ let shouldInstall = Boolean(pOptions.install);
9574
+ const isInteractive = !pOptions.single && !pOptions.multi && !projectName;
9575
+ if (isInteractive) {
9404
9576
  const name = await Zt({
9405
9577
  message: "What is the name of your project?",
9406
9578
  placeholder: "my-amazing-project",
@@ -9429,6 +9601,13 @@ Note: Kit projects cannot build or run without Bun.`);
9429
9601
  process.exit(0);
9430
9602
  }
9431
9603
  gameType = type;
9604
+ const installChoice = await Rt({
9605
+ message: "Install dependencies with Bun now?",
9606
+ initialValue: true
9607
+ });
9608
+ if (!Ct(installChoice)) {
9609
+ shouldInstall = Boolean(installChoice);
9610
+ }
9432
9611
  } else {
9433
9612
  if (pOptions.single)
9434
9613
  gameType = "single";
@@ -9441,13 +9620,19 @@ Note: Kit projects cannot build or run without Bun.`);
9441
9620
  }
9442
9621
  const projectDir = path.join(process.cwd(), projectName);
9443
9622
  if (fs2.existsSync(projectDir)) {
9444
- const overwrite = await Rt({
9445
- message: `Directory ${source_default.cyan(projectName)} already exists. Overwrite?`,
9446
- initialValue: false
9447
- });
9448
- if (Ct(overwrite) || !overwrite) {
9449
- Nt("Installation aborted.");
9450
- process.exit(0);
9623
+ if (isInteractive) {
9624
+ const overwrite = await Rt({
9625
+ message: `Directory ${source_default.cyan(projectName)} already exists. Overwrite?`,
9626
+ initialValue: false
9627
+ });
9628
+ if (Ct(overwrite) || !overwrite) {
9629
+ Nt("Installation aborted.");
9630
+ process.exit(0);
9631
+ }
9632
+ } else if (!pOptions.force) {
9633
+ console.error(source_default.red(`
9634
+ Error: Destination directory '${projectName}' already exists. Use --force (-f) to overwrite.`));
9635
+ process.exit(1);
9451
9636
  }
9452
9637
  }
9453
9638
  const s = be();
@@ -9483,11 +9668,25 @@ Note: Kit projects cannot build or run without Bun.`);
9483
9668
  } catch {
9484
9669
  }
9485
9670
  s.stop(`Project ${source_default.green(projectName)} created!`);
9671
+ if (shouldInstall) {
9672
+ const installSpinner = be();
9673
+ installSpinner.start("Installing project dependencies with Bun...");
9674
+ const installResult = spawnSync("bun", ["install"], { cwd: projectPath, stdio: "ignore", shell: true });
9675
+ if (installResult.status === 0) {
9676
+ installSpinner.stop(source_default.green("Dependencies installed successfully!"));
9677
+ } else {
9678
+ installSpinner.stop(source_default.yellow("Dependency installation finished with warnings."));
9679
+ }
9680
+ }
9486
9681
  console.log(`${source_default.cyan("│")}`);
9487
9682
  console.log(`${source_default.cyan("│")} ${source_default.white.bold("Next steps:")}`);
9488
9683
  console.log(`${source_default.cyan("│")} ${source_default.dim("1.")} cd ${source_default.cyan(projectName)}`);
9489
- console.log(`${source_default.cyan("│")} ${source_default.dim("2.")} bun install`);
9490
- console.log(`${source_default.cyan("│")} ${source_default.dim("3.")} bun run build`);
9684
+ if (!shouldInstall) {
9685
+ console.log(`${source_default.cyan("│")} ${source_default.dim("2.")} bun install`);
9686
+ console.log(`${source_default.cyan("│")} ${source_default.dim("3.")} bun run build`);
9687
+ } else {
9688
+ console.log(`${source_default.cyan("│")} ${source_default.dim("2.")} bun run build`);
9689
+ }
9491
9690
  console.log(`${source_default.cyan("│")}`);
9492
9691
  Gt(source_default.green.bold("Happy coding!"));
9493
9692
  } catch (pError) {
@@ -9503,6 +9702,198 @@ Note: Kit projects cannot build or run without Bun.`);
9503
9702
  }
9504
9703
  }
9505
9704
 
9705
+ // src/cli/doctor.ts
9706
+ import { spawnSync as spawnSync2 } from "node:child_process";
9707
+ import { existsSync, readFileSync } from "node:fs";
9708
+ import { join as join2 } from "node:path";
9709
+ async function processDoctor(pOptions = {}) {
9710
+ const isVerbose2 = Boolean(pOptions.verbose);
9711
+ const results = [];
9712
+ console.log(`
9713
+ ${source_default.cyan.bold("Kit Doctor")} ${source_default.dim("-")} Environment & Project Health Check
9714
+ `);
9715
+ const bunCheck = spawnSync2("bun", ["--version"], { encoding: "utf8", shell: true });
9716
+ if (!bunCheck.error && bunCheck.status === 0) {
9717
+ results.push({
9718
+ category: "Runtime",
9719
+ title: "Bun Runtime",
9720
+ passed: true,
9721
+ details: `Installed version v${bunCheck.stdout.trim()}`
9722
+ });
9723
+ } else {
9724
+ results.push({
9725
+ category: "Runtime",
9726
+ title: "Bun Runtime",
9727
+ passed: false,
9728
+ details: "Bun is not installed or not in PATH. Download at https://bun.sh/"
9729
+ });
9730
+ }
9731
+ const gitCheck = spawnSync2("git", ["--version"], { encoding: "utf8", shell: true });
9732
+ if (!gitCheck.error && gitCheck.status === 0) {
9733
+ results.push({
9734
+ category: "VCS",
9735
+ title: "Git Installation",
9736
+ passed: true,
9737
+ details: gitCheck.stdout.trim()
9738
+ });
9739
+ } else {
9740
+ results.push({
9741
+ category: "VCS",
9742
+ title: "Git Installation",
9743
+ passed: false,
9744
+ details: "Git was not found in your PATH."
9745
+ });
9746
+ }
9747
+ const cwd = process.cwd();
9748
+ const pkgPath = join2(cwd, "package.json");
9749
+ const hasPackageJson = existsSync(pkgPath);
9750
+ if (hasPackageJson) {
9751
+ try {
9752
+ const rawPkg = readFileSync(pkgPath, "utf8");
9753
+ const pkg = JSON.parse(rawPkg);
9754
+ const hasKitDependency = Boolean(pkg.dependencies && pkg.dependencies["@evitcastudio/kit"] || pkg.devDependencies && pkg.devDependencies["@evitcastudio/kit"] || pkg.name === "@evitcastudio/kit");
9755
+ results.push({
9756
+ category: "Project",
9757
+ title: "package.json configuration",
9758
+ passed: true,
9759
+ details: `Project name: ${pkg.name || "unnamed"}`
9760
+ });
9761
+ results.push({
9762
+ category: "Project",
9763
+ title: "Kit framework dependency",
9764
+ passed: hasKitDependency,
9765
+ details: hasKitDependency ? "Found @evitcastudio/kit in project configuration" : "Missing @evitcastudio/kit dependency in package.json"
9766
+ });
9767
+ const isKitCoreRepo = pkg.name === "@evitcastudio/kit";
9768
+ if (!isKitCoreRepo) {
9769
+ const resourcesDir = join2(cwd, "src", "resources");
9770
+ const hasResources = existsSync(resourcesDir);
9771
+ results.push({
9772
+ category: "Project",
9773
+ title: "Asset Directory (src/resources)",
9774
+ passed: hasResources,
9775
+ details: hasResources ? "Asset directory present" : "src/resources not found"
9776
+ });
9777
+ }
9778
+ } catch {
9779
+ results.push({
9780
+ category: "Project",
9781
+ title: "package.json format",
9782
+ passed: false,
9783
+ details: "package.json is malformed or invalid JSON"
9784
+ });
9785
+ }
9786
+ const buildScript = join2(cwd, "bun-build.ts");
9787
+ const hasBuildScript = existsSync(buildScript);
9788
+ results.push({
9789
+ category: "Project",
9790
+ title: "Build Pipeline (bun-build.ts)",
9791
+ passed: hasBuildScript,
9792
+ details: hasBuildScript ? "Build script present" : "bun-build.ts not found"
9793
+ });
9794
+ } else {
9795
+ results.push({
9796
+ category: "Project",
9797
+ title: "Project Context",
9798
+ passed: true,
9799
+ details: "Not currently executed inside a Node/Bun project root."
9800
+ });
9801
+ }
9802
+ let allPassed = true;
9803
+ for (const res of results) {
9804
+ const icon = res.passed ? source_default.green("✓") : source_default.red("✗");
9805
+ const titleText = res.passed ? source_default.white(res.title) : source_default.red.bold(res.title);
9806
+ console.log(` ${icon} [${source_default.cyan(res.category)}] ${titleText}`);
9807
+ if (res.details && (!res.passed || isVerbose2)) {
9808
+ console.log(` ${source_default.dim(res.details)}`);
9809
+ }
9810
+ if (!res.passed) {
9811
+ allPassed = false;
9812
+ }
9813
+ }
9814
+ console.log(`
9815
+ `);
9816
+ if (allPassed) {
9817
+ console.log(` ${source_default.green.bold("All doctor diagnostics passed!")}
9818
+ `);
9819
+ } else {
9820
+ console.log(` ${source_default.yellow.bold("Some issues were detected. Check the items above.")}
9821
+ `);
9822
+ }
9823
+ return allPassed;
9824
+ }
9825
+
9826
+ // src/cli/create.ts
9827
+ import { promises as fs3 } from "node:fs";
9828
+ import { join as join3 } from "node:path";
9829
+ function toPascalCase(pName) {
9830
+ return pName.replace(/[-_](\w)/g, (_2, c) => c.toUpperCase()).replace(/^\w/, (c) => c.toUpperCase());
9831
+ }
9832
+ function toKebabCase(pName) {
9833
+ return pName.replace(/([a-z])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
9834
+ }
9835
+ function generatePluginSource(pClassName, pPluginName) {
9836
+ return `import { KitPlugin } from '@evitcastudio/kit';
9837
+
9838
+ /**
9839
+ * ${pClassName} plugin for the Kit framework.
9840
+ */
9841
+ export class ${pClassName} extends KitPlugin {
9842
+ /**
9843
+ * Unique name identifier of the plugin.
9844
+ */
9845
+ readonly name = '${pPluginName}';
9846
+
9847
+ /**
9848
+ * Entry point for custom initialization when the plugin is registered with Kit.
9849
+ */
9850
+ onRegistered(): void {
9851
+ // Initialize plugin logic
9852
+ }
9853
+ }
9854
+ `;
9855
+ }
9856
+ async function processCreate(pOptions) {
9857
+ const { type, name, verbose } = pOptions;
9858
+ if (!type || !name) {
9859
+ console.error(source_default.red(`
9860
+ Error: Both type and name are required. Usage: kit create <type> <name>`));
9861
+ process.exit(1);
9862
+ }
9863
+ const normalizedType = type.toLowerCase();
9864
+ if (normalizedType !== "plugin") {
9865
+ console.error(source_default.red(`
9866
+ Error: Unknown create type '${type}'. Supported types: 'plugin'`));
9867
+ process.exit(1);
9868
+ }
9869
+ const className = toPascalCase(name);
9870
+ const fileName = `${toKebabCase(name)}.ts`;
9871
+ const pluginsDir = join3(process.cwd(), "src", "plugins");
9872
+ const targetPath = join3(pluginsDir, fileName);
9873
+ try {
9874
+ await fs3.mkdir(pluginsDir, { recursive: true });
9875
+ const fileExists = await fs3.stat(targetPath).then(() => true).catch(() => false);
9876
+ if (fileExists) {
9877
+ console.error(source_default.red(`
9878
+ Error: File already exists at ${targetPath}`));
9879
+ process.exit(1);
9880
+ }
9881
+ const sourceCode = generatePluginSource(className, className);
9882
+ await fs3.writeFile(targetPath, sourceCode, "utf8");
9883
+ console.log(`
9884
+ ${source_default.green("✓")} Created plugin ${source_default.cyan(className)} at ${source_default.dim(targetPath)}
9885
+ `);
9886
+ if (verbose) {
9887
+ console.log(source_default.dim(sourceCode));
9888
+ }
9889
+ } catch (pError) {
9890
+ const message = pError instanceof Error ? pError.message : String(pError);
9891
+ console.error(source_default.red(`
9892
+ Error creating plugin: ${message}`));
9893
+ process.exit(1);
9894
+ }
9895
+ }
9896
+
9506
9897
  // src/cli/main.ts
9507
9898
  class KitCLI {
9508
9899
  static async processResources(pProcessOptions) {
@@ -9511,6 +9902,12 @@ class KitCLI {
9511
9902
  static async init(pInitOptions) {
9512
9903
  await processInit(pInitOptions);
9513
9904
  }
9905
+ static async doctor(pDoctorOptions = {}) {
9906
+ return await processDoctor(pDoctorOptions);
9907
+ }
9908
+ static async create(pCreateOptions) {
9909
+ await processCreate(pCreateOptions);
9910
+ }
9514
9911
  }
9515
9912
 
9516
9913
  // bin/cli.ts
@@ -9573,17 +9970,27 @@ var formatHelp = (pCmd) => {
9573
9970
  }
9574
9971
  }
9575
9972
  const name = pCmd.name();
9576
- if (isRoot || name === "init" || name === "build") {
9973
+ if (isRoot || name === "init" || name === "build" || name === "create" || name === "doctor") {
9577
9974
  output += ` ${source_default.white.bold("Examples:")}
9578
9975
  `;
9579
9976
  if (isRoot || name === "init") {
9580
9977
  output += ` ${source_default.cyan("kit init")}
9581
9978
  `;
9582
- output += ` ${source_default.cyan("kit init my-game --single")}
9979
+ output += ` ${source_default.cyan("kit init my-game --single --install")}
9583
9980
  `;
9584
9981
  }
9585
9982
  if (isRoot || name === "build") {
9586
- output += ` ${source_default.cyan("kit build --in ./src --out ./dist")}
9983
+ output += ` ${source_default.cyan("kit build --in ./src/resources --out ./dist")}
9984
+ `;
9985
+ output += ` ${source_default.cyan("kit build --in ./src/resources --out ./dist --watch")}
9986
+ `;
9987
+ }
9988
+ if (isRoot || name === "create") {
9989
+ output += ` ${source_default.cyan("kit create plugin Inventory")}
9990
+ `;
9991
+ }
9992
+ if (isRoot || name === "doctor") {
9993
+ output += ` ${source_default.cyan("kit doctor")}
9587
9994
  `;
9588
9995
  }
9589
9996
  if (isRoot) {
@@ -9599,9 +10006,9 @@ var formatHelp = (pCmd) => {
9599
10006
  }
9600
10007
  return output;
9601
10008
  };
9602
- program2.name("cli").version(`${package_default.version}`).description(package_default["cli-description"]).option("-v, --verbose", "Enable verbose mode for debugging", false);
10009
+ program2.name("kit").version(`${package_default.version}`).description(package_default["cli-description"]).option("-v, --verbose", "Enable verbose mode for debugging", false);
9603
10010
  program2.helpInformation = () => formatHelp(program2);
9604
- var initCommand = program2.command("init [name]").description("Initialize a new Kit project").option("-s, --single", "Quick-start a single player project").option("-m, --multi", "Quick-start a multiplayer project").action(async (pName, pCmdOptions) => {
10011
+ var initCommand = program2.command("init [name]").description("Initialize a new Kit project").option("-s, --single", "Quick-start a single player project").option("-m, --multi", "Quick-start a multiplayer project").option("-f, --force", "Force overwrite if destination already exists", false).option("-i, --install", "Automatically install dependencies using Bun", false).action(async (pName, pCmdOptions) => {
9605
10012
  if (pName === "help") {
9606
10013
  console.log(initCommand.helpInformation());
9607
10014
  return;
@@ -9610,11 +10017,13 @@ var initCommand = program2.command("init [name]").description("Initialize a new
9610
10017
  projectName: pName,
9611
10018
  single: pCmdOptions.single,
9612
10019
  multi: pCmdOptions.multi,
10020
+ force: pCmdOptions.force,
10021
+ install: pCmdOptions.install,
9613
10022
  verbose: program2.opts().verbose
9614
10023
  });
9615
10024
  });
9616
10025
  initCommand.helpInformation = () => formatHelp(initCommand);
9617
- var buildCommand = program2.command("build [help]").description("Build resources from the specified directory").option("-i, --in <path>", "Input directory (required)").option("-o, --out <path>", "Output directory (required)").option("-is, --ignore-sound", "Ignore sound files", false).action((pHelp, pCmdOptions) => {
10026
+ var buildCommand = program2.command("build [help]").description("Build resources from the specified directory").option("-i, --in <path>", "Input directory (required)").option("-o, --out <path>", "Output directory (required)").option("-m, --manifest <path>", "Custom manifest file path").option("-w, --watch", "Watch input directory for changes and rebuild", false).option("-is, --ignore-sound", "Ignore sound files", false).action((pHelp, pCmdOptions) => {
9618
10027
  if (pHelp === "help") {
9619
10028
  console.log(buildCommand.helpInformation());
9620
10029
  return;
@@ -9629,12 +10038,35 @@ var buildCommand = program2.command("build [help]").description("Build resources
9629
10038
  const processOptions = {
9630
10039
  inDirectory: pCmdOptions.in,
9631
10040
  outDirectory: pCmdOptions.out,
10041
+ manifestPath: pCmdOptions.manifest,
10042
+ watch: pCmdOptions.watch || false,
9632
10043
  ignoreSound: pCmdOptions.ignoreSound || false,
9633
10044
  verbose: verbose || false
9634
10045
  };
9635
10046
  KitCLI.processResources(processOptions);
9636
10047
  });
9637
10048
  buildCommand.helpInformation = () => formatHelp(buildCommand);
10049
+ var createCommand2 = program2.command("create [type] [name]").description("Generate boilerplate files (e.g. plugin)").action(async (pType, pName) => {
10050
+ if (pType === "help" || !pType || !pName) {
10051
+ console.log(createCommand2.helpInformation());
10052
+ return;
10053
+ }
10054
+ await KitCLI.create({
10055
+ type: pType,
10056
+ name: pName,
10057
+ verbose: program2.opts().verbose
10058
+ });
10059
+ });
10060
+ createCommand2.helpInformation = () => formatHelp(createCommand2);
10061
+ var doctorCommand = program2.command("doctor").description("Inspect environment and game project diagnostics").action(async () => {
10062
+ const allPassed = await KitCLI.doctor({
10063
+ verbose: program2.opts().verbose
10064
+ });
10065
+ if (!allPassed) {
10066
+ process.exitCode = 1;
10067
+ }
10068
+ });
10069
+ doctorCommand.helpInformation = () => formatHelp(doctorCommand);
9638
10070
  program2.command("help [command]").description("Display help for a command").action((pCommandName) => {
9639
10071
  if (pCommandName) {
9640
10072
  const cmd = program2.commands.find((c) => c.name() === pCommandName);