@evitcastudio/kit 3.1.1 → 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 +379 -56
  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 +139 -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
@@ -2,7 +2,7 @@
2
2
 
3
3
  /*!
4
4
  * @evitcastudio/kit@3.1.1 git+https://github.com/EvitcaStudio/Kit.git
5
- * Compiled Fri, 26 Jun 2026 23:30:10 UTC
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,6 +8098,11 @@ 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();
@@ -8261,26 +8290,20 @@ async function copyFile(pSource, pDestinationDir, pNewName) {
8261
8290
  }
8262
8291
  }
8263
8292
  async function saveResourceJSON() {
8264
- const filePath = "resource.json";
8293
+ const jsonContent = JSON.stringify(resourceJSON, null, 4);
8294
+ const targetPath = customManifestPath || "resource.json";
8265
8295
  try {
8266
- await fs.writeFile(filePath, JSON.stringify(resourceJSON, null, 4));
8296
+ await fs.writeFile(targetPath, jsonContent, "utf8");
8267
8297
  } catch (pError) {
8268
- logError(`[Error] Saving resource JSON: ${pError}`);
8298
+ logError(`[Error] Saving resource JSON to ${targetPath}: ${pError}`);
8269
8299
  }
8270
8300
  }
8271
- async function processResources({ inDirectory, outDirectory, verbose, ignoreSound }) {
8272
- resourceInDirectory = inDirectory;
8273
- resourceOutDirectory = outDirectory;
8274
- isVerbose = verbose;
8275
- ignoringSound = ignoreSound;
8301
+ async function runBuild() {
8276
8302
  resourceJSON = initializeResourceJSON();
8277
8303
  resourcesToProcess.length = 0;
8278
- if (!resourceInDirectory || !resourceOutDirectory) {
8279
- logError("[Error] Input and output directories must be specified");
8280
- return;
8281
- }
8304
+ subdirectoriesToMirror.length = 0;
8282
8305
  await processDirectory(resourceInDirectory);
8283
- if (resourcesToProcess.length > 0) {
8306
+ if (resourcesToProcess.length > 0 || subdirectoriesToMirror.length > 0) {
8284
8307
  await processAllFiles();
8285
8308
  } else {
8286
8309
  logAlert("No resources found!");
@@ -8289,6 +8312,44 @@ async function processResources({ inDirectory, outDirectory, verbose, ignoreSoun
8289
8312
  await saveIconPointsJSON({});
8290
8313
  await saveSizesJSON({});
8291
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();
8352
+ }
8292
8353
  }
8293
8354
  function logVerbose(pMessage) {
8294
8355
  if (isVerbose)
@@ -9509,7 +9570,9 @@ Note: Kit projects cannot build or run without Bun.`);
9509
9570
  }
9510
9571
  let projectName = pOptions.projectName;
9511
9572
  let gameType = "single";
9512
- if (!pOptions.single && !pOptions.multi && !projectName) {
9573
+ let shouldInstall = Boolean(pOptions.install);
9574
+ const isInteractive = !pOptions.single && !pOptions.multi && !projectName;
9575
+ if (isInteractive) {
9513
9576
  const name = await Zt({
9514
9577
  message: "What is the name of your project?",
9515
9578
  placeholder: "my-amazing-project",
@@ -9538,6 +9601,13 @@ Note: Kit projects cannot build or run without Bun.`);
9538
9601
  process.exit(0);
9539
9602
  }
9540
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
+ }
9541
9611
  } else {
9542
9612
  if (pOptions.single)
9543
9613
  gameType = "single";
@@ -9550,13 +9620,19 @@ Note: Kit projects cannot build or run without Bun.`);
9550
9620
  }
9551
9621
  const projectDir = path.join(process.cwd(), projectName);
9552
9622
  if (fs2.existsSync(projectDir)) {
9553
- const overwrite = await Rt({
9554
- message: `Directory ${source_default.cyan(projectName)} already exists. Overwrite?`,
9555
- initialValue: false
9556
- });
9557
- if (Ct(overwrite) || !overwrite) {
9558
- Nt("Installation aborted.");
9559
- 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);
9560
9636
  }
9561
9637
  }
9562
9638
  const s = be();
@@ -9592,11 +9668,25 @@ Note: Kit projects cannot build or run without Bun.`);
9592
9668
  } catch {
9593
9669
  }
9594
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
+ }
9595
9681
  console.log(`${source_default.cyan("│")}`);
9596
9682
  console.log(`${source_default.cyan("│")} ${source_default.white.bold("Next steps:")}`);
9597
9683
  console.log(`${source_default.cyan("│")} ${source_default.dim("1.")} cd ${source_default.cyan(projectName)}`);
9598
- console.log(`${source_default.cyan("│")} ${source_default.dim("2.")} bun install`);
9599
- 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
+ }
9600
9690
  console.log(`${source_default.cyan("│")}`);
9601
9691
  Gt(source_default.green.bold("Happy coding!"));
9602
9692
  } catch (pError) {
@@ -9612,6 +9702,198 @@ Note: Kit projects cannot build or run without Bun.`);
9612
9702
  }
9613
9703
  }
9614
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
+
9615
9897
  // src/cli/main.ts
9616
9898
  class KitCLI {
9617
9899
  static async processResources(pProcessOptions) {
@@ -9620,6 +9902,12 @@ class KitCLI {
9620
9902
  static async init(pInitOptions) {
9621
9903
  await processInit(pInitOptions);
9622
9904
  }
9905
+ static async doctor(pDoctorOptions = {}) {
9906
+ return await processDoctor(pDoctorOptions);
9907
+ }
9908
+ static async create(pCreateOptions) {
9909
+ await processCreate(pCreateOptions);
9910
+ }
9623
9911
  }
9624
9912
 
9625
9913
  // bin/cli.ts
@@ -9682,17 +9970,27 @@ var formatHelp = (pCmd) => {
9682
9970
  }
9683
9971
  }
9684
9972
  const name = pCmd.name();
9685
- if (isRoot || name === "init" || name === "build") {
9973
+ if (isRoot || name === "init" || name === "build" || name === "create" || name === "doctor") {
9686
9974
  output += ` ${source_default.white.bold("Examples:")}
9687
9975
  `;
9688
9976
  if (isRoot || name === "init") {
9689
9977
  output += ` ${source_default.cyan("kit init")}
9690
9978
  `;
9691
- output += ` ${source_default.cyan("kit init my-game --single")}
9979
+ output += ` ${source_default.cyan("kit init my-game --single --install")}
9692
9980
  `;
9693
9981
  }
9694
9982
  if (isRoot || name === "build") {
9695
- 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")}
9696
9994
  `;
9697
9995
  }
9698
9996
  if (isRoot) {
@@ -9708,9 +10006,9 @@ var formatHelp = (pCmd) => {
9708
10006
  }
9709
10007
  return output;
9710
10008
  };
9711
- 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);
9712
10010
  program2.helpInformation = () => formatHelp(program2);
9713
- 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) => {
9714
10012
  if (pName === "help") {
9715
10013
  console.log(initCommand.helpInformation());
9716
10014
  return;
@@ -9719,11 +10017,13 @@ var initCommand = program2.command("init [name]").description("Initialize a new
9719
10017
  projectName: pName,
9720
10018
  single: pCmdOptions.single,
9721
10019
  multi: pCmdOptions.multi,
10020
+ force: pCmdOptions.force,
10021
+ install: pCmdOptions.install,
9722
10022
  verbose: program2.opts().verbose
9723
10023
  });
9724
10024
  });
9725
10025
  initCommand.helpInformation = () => formatHelp(initCommand);
9726
- 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) => {
9727
10027
  if (pHelp === "help") {
9728
10028
  console.log(buildCommand.helpInformation());
9729
10029
  return;
@@ -9738,12 +10038,35 @@ var buildCommand = program2.command("build [help]").description("Build resources
9738
10038
  const processOptions = {
9739
10039
  inDirectory: pCmdOptions.in,
9740
10040
  outDirectory: pCmdOptions.out,
10041
+ manifestPath: pCmdOptions.manifest,
10042
+ watch: pCmdOptions.watch || false,
9741
10043
  ignoreSound: pCmdOptions.ignoreSound || false,
9742
10044
  verbose: verbose || false
9743
10045
  };
9744
10046
  KitCLI.processResources(processOptions);
9745
10047
  });
9746
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);
9747
10070
  program2.command("help [command]").description("Display help for a command").action((pCommandName) => {
9748
10071
  if (pCommandName) {
9749
10072
  const cmd = program2.commands.find((c) => c.name() === pCommandName);