@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
@@ -0,0 +1,88 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import chalk from 'chalk';
4
+ /**
5
+ * Formats a given name into a PascalCase class name.
6
+ * @param pName - Raw identifier input.
7
+ */
8
+ function toPascalCase(pName) {
9
+ return pName
10
+ .replace(/[-_](\w)/g, (_, c) => c.toUpperCase())
11
+ .replace(/^\w/, c => c.toUpperCase());
12
+ }
13
+ /**
14
+ * Formats a given name into a kebab-case file name.
15
+ * @param pName - Raw identifier input.
16
+ */
17
+ function toKebabCase(pName) {
18
+ return pName
19
+ .replace(/([a-z])([A-Z])/g, '$1-$2')
20
+ .replace(/[\s_]+/g, '-')
21
+ .toLowerCase();
22
+ }
23
+ /**
24
+ * Generates the source code for a custom KitPlugin.
25
+ * @param pClassName - PascalCase class name.
26
+ * @param pPluginName - Plugin string identifier.
27
+ */
28
+ function generatePluginSource(pClassName, pPluginName) {
29
+ return `import { KitPlugin } from '@evitcastudio/kit';
30
+
31
+ /**
32
+ * ${pClassName} plugin for the Kit framework.
33
+ */
34
+ export class ${pClassName} extends KitPlugin {
35
+ /**
36
+ * Unique name identifier of the plugin.
37
+ */
38
+ readonly name = '${pPluginName}';
39
+
40
+ /**
41
+ * Entry point for custom initialization when the plugin is registered with Kit.
42
+ */
43
+ onRegistered(): void {
44
+ // Initialize plugin logic
45
+ }
46
+ }
47
+ `;
48
+ }
49
+ /**
50
+ * Handles creation of new boilerplate elements in a Kit project.
51
+ * @param pOptions - Creation configuration options.
52
+ */
53
+ export async function processCreate(pOptions) {
54
+ const { type, name, verbose } = pOptions;
55
+ if (!type || !name) {
56
+ console.error(chalk.red('\nError: Both type and name are required. Usage: kit create <type> <name>'));
57
+ process.exit(1);
58
+ }
59
+ const normalizedType = type.toLowerCase();
60
+ if (normalizedType !== 'plugin') {
61
+ console.error(chalk.red(`\nError: Unknown create type '${type}'. Supported types: 'plugin'`));
62
+ process.exit(1);
63
+ }
64
+ const className = toPascalCase(name);
65
+ const fileName = `${toKebabCase(name)}.ts`;
66
+ // Standard location for plugins in a game or library project
67
+ const pluginsDir = join(process.cwd(), 'src', 'plugins');
68
+ const targetPath = join(pluginsDir, fileName);
69
+ try {
70
+ await fs.mkdir(pluginsDir, { recursive: true });
71
+ const fileExists = await fs.stat(targetPath).then(() => true).catch(() => false);
72
+ if (fileExists) {
73
+ console.error(chalk.red(`\nError: File already exists at ${targetPath}`));
74
+ process.exit(1);
75
+ }
76
+ const sourceCode = generatePluginSource(className, className);
77
+ await fs.writeFile(targetPath, sourceCode, 'utf8');
78
+ console.log(`\n ${chalk.green('✓')} Created plugin ${chalk.cyan(className)} at ${chalk.dim(targetPath)}\n`);
79
+ if (verbose) {
80
+ console.log(chalk.dim(sourceCode));
81
+ }
82
+ }
83
+ catch (pError) {
84
+ const message = pError instanceof Error ? pError.message : String(pError);
85
+ console.error(chalk.red(`\nError creating plugin: ${message}`));
86
+ process.exit(1);
87
+ }
88
+ }
@@ -0,0 +1,9 @@
1
+ export interface DoctorOptions {
2
+ verbose?: boolean;
3
+ }
4
+ /**
5
+ * Runs diagnostics on the local development environment and project health.
6
+ * @param pOptions - Options passed from the CLI.
7
+ */
8
+ export declare function processDoctor(pOptions?: DoctorOptions): Promise<boolean>;
9
+ //# sourceMappingURL=doctor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/cli/doctor.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,aAAa;IAC1B,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AASD;;;GAGG;AACH,wBAAsB,aAAa,CAAC,QAAQ,GAAE,aAAkB,GAAG,OAAO,CAAC,OAAO,CAAC,CAiLlF"}
@@ -0,0 +1,179 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import chalk from 'chalk';
5
+ /**
6
+ * Runs diagnostics on the local development environment and project health.
7
+ * @param pOptions - Options passed from the CLI.
8
+ */
9
+ export async function processDoctor(pOptions = {}) {
10
+ const isVerbose = Boolean(pOptions.verbose);
11
+ const results = [];
12
+ console.log(`\n ${chalk.cyan.bold('Kit Doctor')} ${chalk.dim('-')} Environment & Project Health Check\n`);
13
+ // 1. Runtime Check: Bun
14
+ const bunCheck = spawnSync('bun', ['--version'], { encoding: 'utf8', shell: true });
15
+ if (!bunCheck.error && bunCheck.status === 0) {
16
+ results.push({
17
+ category: 'Runtime',
18
+ title: 'Bun Runtime',
19
+ passed: true,
20
+ details: `Installed version v${bunCheck.stdout.trim()}`
21
+ });
22
+ }
23
+ else {
24
+ results.push({
25
+ category: 'Runtime',
26
+ title: 'Bun Runtime',
27
+ passed: false,
28
+ details: 'Bun is not installed or not in PATH. Download at https://bun.sh/'
29
+ });
30
+ }
31
+ // 2. Version Control Check: Git
32
+ const gitCheck = spawnSync('git', ['--version'], { encoding: 'utf8', shell: true });
33
+ if (!gitCheck.error && gitCheck.status === 0) {
34
+ results.push({
35
+ category: 'VCS',
36
+ title: 'Git Installation',
37
+ passed: true,
38
+ details: gitCheck.stdout.trim()
39
+ });
40
+ }
41
+ else {
42
+ results.push({
43
+ category: 'VCS',
44
+ title: 'Git Installation',
45
+ passed: false,
46
+ details: 'Git was not found in your PATH.'
47
+ });
48
+ }
49
+ // 3. Project Environment Check
50
+ const cwd = process.cwd();
51
+ const pkgPath = join(cwd, 'package.json');
52
+ const hasPackageJson = existsSync(pkgPath);
53
+ if (hasPackageJson) {
54
+ try {
55
+ const rawPkg = readFileSync(pkgPath, 'utf8');
56
+ const pkg = JSON.parse(rawPkg);
57
+ const hasKitDependency = Boolean((pkg.dependencies && pkg.dependencies['@evitcastudio/kit']) ||
58
+ (pkg.devDependencies && pkg.devDependencies['@evitcastudio/kit']) ||
59
+ pkg.name === '@evitcastudio/kit');
60
+ results.push({
61
+ category: 'Project',
62
+ title: 'package.json configuration',
63
+ passed: true,
64
+ details: `Project name: ${pkg.name || 'unnamed'}`
65
+ });
66
+ results.push({
67
+ category: 'Project',
68
+ title: 'Kit framework dependency',
69
+ passed: hasKitDependency,
70
+ details: hasKitDependency
71
+ ? 'Found @evitcastudio/kit in project configuration'
72
+ : 'Missing @evitcastudio/kit dependency in package.json'
73
+ });
74
+ // If running inside a consumer game project, check game asset directory structure
75
+ const isKitCoreRepo = pkg.name === '@evitcastudio/kit';
76
+ if (!isKitCoreRepo) {
77
+ const resourcesDir = join(cwd, 'src', 'resources');
78
+ const hasResources = existsSync(resourcesDir);
79
+ results.push({
80
+ category: 'Project',
81
+ title: 'Asset Directory (src/resources)',
82
+ passed: hasResources,
83
+ details: hasResources ? 'Asset directory present' : 'src/resources not found'
84
+ });
85
+ }
86
+ // Detect project type (multiplayer vs single-player)
87
+ const hasClientEntry = existsSync(join(cwd, 'src', 'client', 'index.ts'));
88
+ const hasServerEntry = existsSync(join(cwd, 'src', 'server', 'index.ts'));
89
+ const hasSingleEntry = existsSync(join(cwd, 'src', 'index.ts'));
90
+ let projectType = 'Unknown';
91
+ if (hasClientEntry && hasServerEntry) {
92
+ projectType = 'Multiplayer (Client & Server)';
93
+ }
94
+ else if (hasClientEntry) {
95
+ projectType = 'Multiplayer (Client)';
96
+ }
97
+ else if (hasServerEntry) {
98
+ projectType = 'Dedicated Server';
99
+ }
100
+ else if (hasSingleEntry) {
101
+ projectType = 'Singleplayer';
102
+ }
103
+ results.push({
104
+ category: 'Project',
105
+ title: 'Project Architecture',
106
+ passed: projectType !== 'Unknown' || isKitCoreRepo,
107
+ details: isKitCoreRepo ? 'Kit Core Framework Repository' : `Detected Type: ${projectType}`
108
+ });
109
+ }
110
+ catch {
111
+ results.push({
112
+ category: 'Project',
113
+ title: 'package.json format',
114
+ passed: false,
115
+ details: 'package.json is malformed or invalid JSON'
116
+ });
117
+ }
118
+ // Check for build script: either bun-build.ts exists, or package.json has a build script using kit build
119
+ const buildScript = join(cwd, 'bun-build.ts');
120
+ const hasBuildScript = existsSync(buildScript);
121
+ let hasKitBuildScript = false;
122
+ try {
123
+ const rawPkg = readFileSync(pkgPath, 'utf8');
124
+ const pkg = JSON.parse(rawPkg);
125
+ if (pkg.scripts && typeof pkg.scripts.build === 'string' && pkg.scripts.build.includes('kit build')) {
126
+ hasKitBuildScript = true;
127
+ }
128
+ }
129
+ catch {
130
+ // Ignored, handled above
131
+ }
132
+ const buildPipelinePassed = hasBuildScript || hasKitBuildScript;
133
+ let buildDetails = 'No build pipeline configured';
134
+ if (hasKitBuildScript && hasBuildScript) {
135
+ buildDetails = 'Kit CLI build pipeline with bun-build.ts present';
136
+ }
137
+ else if (hasKitBuildScript) {
138
+ buildDetails = 'Kit CLI native build pipeline configured';
139
+ }
140
+ else if (hasBuildScript) {
141
+ buildDetails = 'Legacy bun-build.ts present';
142
+ }
143
+ results.push({
144
+ category: 'Project',
145
+ title: 'Build Pipeline',
146
+ passed: buildPipelinePassed,
147
+ details: buildDetails
148
+ });
149
+ }
150
+ else {
151
+ results.push({
152
+ category: 'Project',
153
+ title: 'Project Context',
154
+ passed: true,
155
+ details: 'Not currently executed inside a Node/Bun project root.'
156
+ });
157
+ }
158
+ // Render results
159
+ let allPassed = true;
160
+ for (const res of results) {
161
+ const icon = res.passed ? chalk.green('✓') : chalk.red('✗');
162
+ const titleText = res.passed ? chalk.white(res.title) : chalk.red.bold(res.title);
163
+ console.log(` ${icon} [${chalk.cyan(res.category)}] ${titleText}`);
164
+ if (res.details && (!res.passed || isVerbose)) {
165
+ console.log(` ${chalk.dim(res.details)}`);
166
+ }
167
+ if (!res.passed) {
168
+ allPassed = false;
169
+ }
170
+ }
171
+ console.log('\n');
172
+ if (allPassed) {
173
+ console.log(` ${chalk.green.bold('All doctor diagnostics passed!')}\n`);
174
+ }
175
+ else {
176
+ console.log(` ${chalk.yellow.bold('Some issues were detected. Check the items above.')}\n`);
177
+ }
178
+ return allPassed;
179
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Options for hosting the project.
3
+ */
4
+ export interface HostOptions {
5
+ /**
6
+ * Port to bind the server to (default: 8090).
7
+ */
8
+ port?: number;
9
+ /**
10
+ * Directory containing built files to serve (default: ./dist).
11
+ */
12
+ directory?: string;
13
+ /**
14
+ * Whether to trigger a build before hosting (default: false).
15
+ */
16
+ build?: boolean;
17
+ /**
18
+ * Whether to log detailed server events.
19
+ */
20
+ verbose?: boolean;
21
+ }
22
+ /**
23
+ * Result of the host process.
24
+ */
25
+ export interface HostResult {
26
+ /**
27
+ * Whether the host process started successfully.
28
+ */
29
+ success: boolean;
30
+ /**
31
+ * Explanation of the result or error message.
32
+ */
33
+ message: string;
34
+ /**
35
+ * Server instance if static HTTP server was started.
36
+ */
37
+ server?: {
38
+ stop(): void;
39
+ port: number;
40
+ };
41
+ }
42
+ /**
43
+ * Hosts the game application via local HTTP server (for singleplayer/client games)
44
+ * or launches the multiplayer node server. Handles missing files gracefully.
45
+ * @param pOptions - Host options.
46
+ * @returns HostResult indicating success or failure message.
47
+ */
48
+ export declare function processHost(pOptions?: HostOptions): Promise<HostResult>;
49
+ //# sourceMappingURL=host.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"host.d.ts","sourceRoot":"","sources":["../../src/cli/host.ts"],"names":[],"mappings":"AAMA;;GAEG;AACH,MAAM,WAAW,WAAW;IACxB;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;OAEG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;OAEG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACvB;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;OAEG;IACH,MAAM,CAAC,EAAE;QAAE,IAAI,IAAI,IAAI,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3C;AAoBD;;;;;GAKG;AACH,wBAAsB,WAAW,CAAC,QAAQ,GAAE,WAAgB,GAAG,OAAO,CAAC,UAAU,CAAC,CAiHjF"}
@@ -0,0 +1,127 @@
1
+ import { existsSync, readFileSync } from 'fs';
2
+ import { join, resolve } from 'path';
3
+ import { networkInterfaces } from 'os';
4
+ import chalk from 'chalk';
5
+ import { detectArchitecture } from './app-bundler';
6
+ /**
7
+ * Retrieves the primary local IPv4 address for local network access.
8
+ * @returns Primary LAN IPv4 address or localhost fallback.
9
+ */
10
+ function getNetworkAddress() {
11
+ const interfaces = networkInterfaces();
12
+ for (const name of Object.keys(interfaces)) {
13
+ const netList = interfaces[name];
14
+ if (!netList)
15
+ continue;
16
+ for (const net of netList) {
17
+ if (net.family === 'IPv4' && !net.internal) {
18
+ return net.address;
19
+ }
20
+ }
21
+ }
22
+ return 'localhost';
23
+ }
24
+ /**
25
+ * Hosts the game application via local HTTP server (for singleplayer/client games)
26
+ * or launches the multiplayer node server. Handles missing files gracefully.
27
+ * @param pOptions - Host options.
28
+ * @returns HostResult indicating success or failure message.
29
+ */
30
+ export async function processHost(pOptions = {}) {
31
+ const cwd = process.cwd();
32
+ const defaultPort = 8090;
33
+ const port = pOptions.port || defaultPort;
34
+ const distDir = pOptions.directory ? resolve(pOptions.directory) : join(cwd, 'dist');
35
+ const architecture = detectArchitecture(join(cwd, 'src'));
36
+ if (!existsSync(distDir)) {
37
+ const message = `Target directory "${distDir}" does not exist. Run "kit build" or use "kit host -b" first.`;
38
+ console.error(chalk.red(`\n[Kit Host] ${message}\n`));
39
+ return { success: false, message };
40
+ }
41
+ // Multiplayer Hosting
42
+ if (architecture === 'multi') {
43
+ const serverJsPath = join(distDir, 'server.js');
44
+ if (!existsSync(serverJsPath)) {
45
+ const message = `Cannot host multiplayer project: "${serverJsPath}" was not found. Please compile the server first using "kit build".`;
46
+ console.error(chalk.red(`\n[Kit Host] ${message}\n`));
47
+ return { success: false, message };
48
+ }
49
+ console.log(chalk.cyan(`\n🎮 Starting Multiplayer Server from ${chalk.bold(distDir)}...\n`));
50
+ let serverSettingsPort = port;
51
+ const settingsPath = join(distDir, 'settings.json');
52
+ if (existsSync(settingsPath)) {
53
+ try {
54
+ const settings = JSON.parse(readFileSync(settingsPath, 'utf8'));
55
+ if (settings.port)
56
+ serverSettingsPort = Number(settings.port);
57
+ }
58
+ catch {
59
+ // Ignore fallback to default port
60
+ }
61
+ }
62
+ const proc = Bun.spawn(['node', 'server.js'], {
63
+ cwd: distDir,
64
+ stdout: 'inherit',
65
+ stderr: 'inherit',
66
+ stdin: 'inherit'
67
+ });
68
+ console.log(chalk.green(`✓ Multiplayer server process spawned (configured port: ${serverSettingsPort})`));
69
+ process.on('SIGINT', () => {
70
+ proc.kill();
71
+ process.exit(0);
72
+ });
73
+ process.on('SIGTERM', () => {
74
+ proc.kill();
75
+ process.exit(0);
76
+ });
77
+ await proc.exited;
78
+ return { success: true, message: 'Multiplayer server finished running.' };
79
+ }
80
+ // Singleplayer / Client static web hosting
81
+ const indexPath = join(distDir, 'index.html');
82
+ if (!existsSync(indexPath)) {
83
+ const message = `Missing entrypoint: "${indexPath}" was not found in dist. Run "kit build" or use "kit host -b" to compile.`;
84
+ console.error(chalk.red(`\n[Kit Host] ${message}\n`));
85
+ return { success: false, message };
86
+ }
87
+ const lanIp = getNetworkAddress();
88
+ const server = Bun.serve({
89
+ port,
90
+ async fetch(pReq) {
91
+ const path = new URL(pReq.url).pathname;
92
+ const target = path === '/' ? '/index.html' : decodeURIComponent(path);
93
+ const file = Bun.file(join(distDir, target));
94
+ if (!await file.exists()) {
95
+ if (pOptions.verbose) {
96
+ console.warn(chalk.yellow(`[Kit Host] 404 Not Found: ${target}`));
97
+ }
98
+ return new Response('Not Found', { status: 404 });
99
+ }
100
+ return new Response(file);
101
+ }
102
+ });
103
+ console.log(chalk.cyan('\n🎮 Kit Game Host Server\n'));
104
+ console.log(` ${chalk.bold('Local:')} ${chalk.green(`http://localhost:${server.port}`)}`);
105
+ if (lanIp !== 'localhost') {
106
+ console.log(` ${chalk.bold('Network:')} ${chalk.green(`http://${lanIp}:${server.port}`)}`);
107
+ }
108
+ console.log(chalk.dim(`\nServing files from: ${distDir}`));
109
+ console.log(chalk.dim('Press Ctrl+C to stop the server\n'));
110
+ process.on('SIGINT', () => {
111
+ console.log(chalk.yellow('\nShutting down host server...'));
112
+ server.stop();
113
+ process.exit(0);
114
+ });
115
+ process.on('SIGTERM', () => {
116
+ server.stop();
117
+ process.exit(0);
118
+ });
119
+ return {
120
+ success: true,
121
+ message: `Serving files on port ${server.port}`,
122
+ server: {
123
+ stop: () => server.stop(),
124
+ port: server.port
125
+ }
126
+ };
127
+ }
package/lib/cli/init.d.ts CHANGED
@@ -5,6 +5,8 @@ export interface InitOptions {
5
5
  projectName?: string;
6
6
  single?: boolean;
7
7
  multi?: boolean;
8
+ force?: boolean;
9
+ install?: boolean;
8
10
  verbose?: boolean;
9
11
  }
10
12
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"AASA;;GAEG;AACH,MAAM,WAAW,WAAW;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAyFD;;;GAGG;AACH,wBAAsB,WAAW,CAAC,QAAQ,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CA8JtE"}
1
+ {"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/cli/init.ts"],"names":[],"mappings":"AASA;;GAEG;AACH,MAAM,WAAW,WAAW;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAyFD;;;GAGG;AACH,wBAAsB,WAAW,CAAC,QAAQ,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAwLtE"}
package/lib/cli/init.js CHANGED
@@ -115,8 +115,10 @@ export async function processInit(pOptions) {
115
115
  }
116
116
  let projectName = pOptions.projectName;
117
117
  let gameType = 'single';
118
+ let shouldInstall = Boolean(pOptions.install);
119
+ const isInteractive = !pOptions.single && !pOptions.multi && !projectName;
118
120
  // Interactive Walkthrough
119
- if (!pOptions.single && !pOptions.multi && !projectName) {
121
+ if (isInteractive) {
120
122
  const name = await text({
121
123
  message: 'What is the name of your project?',
122
124
  placeholder: 'my-amazing-project',
@@ -145,6 +147,13 @@ export async function processInit(pOptions) {
145
147
  process.exit(0);
146
148
  }
147
149
  gameType = type;
150
+ const installChoice = await confirm({
151
+ message: 'Install dependencies with Bun now?',
152
+ initialValue: true,
153
+ });
154
+ if (!isCancel(installChoice)) {
155
+ shouldInstall = Boolean(installChoice);
156
+ }
148
157
  }
149
158
  else {
150
159
  // Handle flags
@@ -159,13 +168,19 @@ export async function processInit(pOptions) {
159
168
  }
160
169
  const projectDir = path.join(process.cwd(), projectName);
161
170
  if (fs.existsSync(projectDir)) {
162
- const overwrite = await confirm({
163
- message: `Directory ${chalk.cyan(projectName)} already exists. Overwrite?`,
164
- initialValue: false,
165
- });
166
- if (isCancel(overwrite) || !overwrite) {
167
- cancel('Installation aborted.');
168
- process.exit(0);
171
+ if (isInteractive) {
172
+ const overwrite = await confirm({
173
+ message: `Directory ${chalk.cyan(projectName)} already exists. Overwrite?`,
174
+ initialValue: false,
175
+ });
176
+ if (isCancel(overwrite) || !overwrite) {
177
+ cancel('Installation aborted.');
178
+ process.exit(0);
179
+ }
180
+ }
181
+ else if (!pOptions.force) {
182
+ console.error(chalk.red(`\nError: Destination directory '${projectName}' already exists. Use --force (-f) to overwrite.`));
183
+ process.exit(1);
169
184
  }
170
185
  }
171
186
  const s = spinner();
@@ -173,8 +188,6 @@ export async function processInit(pOptions) {
173
188
  try {
174
189
  const projectPath = path.join(process.cwd(), projectName);
175
190
  if (fs.existsSync(projectPath)) {
176
- // This case should ideally be handled by the confirm prompt above,
177
- // but good to have a fallback for non-interactive mode or race conditions.
178
191
  fs.rmSync(projectPath, { recursive: true, force: true });
179
192
  }
180
193
  fs.mkdirSync(projectPath, { recursive: true });
@@ -183,12 +196,9 @@ export async function processInit(pOptions) {
183
196
  if (gameType === 'both')
184
197
  templateType = 'multi'; // Use multi for both for now
185
198
  // Resolve template path
186
- // When running from lib/bundle/cli/cli.js, templates are in ./kit-game-templates/
187
199
  const templatesDir = path.join(path.dirname(fileURLToPath(import.meta.url)), 'kit-game-templates', templateType);
188
200
  const author = getGitUser();
189
201
  if (!fs.existsSync(templatesDir)) {
190
- // Fallback for local development (src/cli/init.ts)
191
- // Go up to root then templates/pType
192
202
  const localTemplatesDir = path.join(process.cwd(), 'kit-game-templates', templateType);
193
203
  if (!fs.existsSync(localTemplatesDir)) {
194
204
  console.error(chalk.red(`Error: Templates not found at ${templatesDir} or ${localTemplatesDir}`));
@@ -212,11 +222,28 @@ export async function processInit(pOptions) {
212
222
  // Silently fail if git is not installed or config is missing
213
223
  }
214
224
  s.stop(`Project ${chalk.green(projectName)} created!`);
225
+ // Automatically install dependencies if requested
226
+ if (shouldInstall) {
227
+ const installSpinner = spinner();
228
+ installSpinner.start('Installing project dependencies with Bun...');
229
+ const installResult = spawnSync('bun', ['install'], { cwd: projectPath, stdio: 'ignore', shell: true });
230
+ if (installResult.status === 0) {
231
+ installSpinner.stop(chalk.green('Dependencies installed successfully!'));
232
+ }
233
+ else {
234
+ installSpinner.stop(chalk.yellow('Dependency installation finished with warnings.'));
235
+ }
236
+ }
215
237
  console.log(`${chalk.cyan('│')}`);
216
238
  console.log(`${chalk.cyan('│')} ${chalk.white.bold('Next steps:')}`);
217
239
  console.log(`${chalk.cyan('│')} ${chalk.dim('1.')} cd ${chalk.cyan(projectName)}`);
218
- console.log(`${chalk.cyan('│')} ${chalk.dim('2.')} bun install`);
219
- console.log(`${chalk.cyan('│')} ${chalk.dim('3.')} bun run build`);
240
+ if (!shouldInstall) {
241
+ console.log(`${chalk.cyan('│')} ${chalk.dim('2.')} bun install`);
242
+ console.log(`${chalk.cyan('│')} ${chalk.dim('3.')} bun run build`);
243
+ }
244
+ else {
245
+ console.log(`${chalk.cyan('│')} ${chalk.dim('2.')} bun run build`);
246
+ }
220
247
  console.log(`${chalk.cyan('│')}`);
221
248
  outro(chalk.green.bold('Happy coding!'));
222
249
  }
package/lib/cli/main.d.ts CHANGED
@@ -1,13 +1,34 @@
1
1
  import type { ProcessOptions } from './types';
2
2
  import { type InitOptions } from './init';
3
+ import { type DoctorOptions } from './doctor';
4
+ import { type CreateOptions } from './create';
5
+ import { type HostOptions, type HostResult } from './host';
3
6
  export declare class KitCLI {
4
7
  /**
5
- * Start resource builder.
8
+ * Start resource builder or watcher.
9
+ * @param pProcessOptions - Configuration options for resource processing.
6
10
  */
7
11
  static processResources(pProcessOptions: ProcessOptions): Promise<void>;
8
12
  /**
9
13
  * Start project initialization.
14
+ * @param pInitOptions - Configuration options for project initialization.
10
15
  */
11
16
  static init(pInitOptions: InitOptions): Promise<void>;
17
+ /**
18
+ * Run environment and project diagnostics.
19
+ * @param pDoctorOptions - Configuration options for diagnostics.
20
+ */
21
+ static doctor(pDoctorOptions?: DoctorOptions): Promise<boolean>;
22
+ /**
23
+ * Create boilerplate code (e.g. plugins).
24
+ * @param pCreateOptions - Configuration options for code generation.
25
+ */
26
+ static create(pCreateOptions: CreateOptions): Promise<void>;
27
+ /**
28
+ * Host game project locally.
29
+ * @param pHostOptions - Configuration options for hosting.
30
+ * @returns HostResult indicating status and server reference if applicable.
31
+ */
32
+ static host(pHostOptions?: HostOptions): Promise<HostResult>;
12
33
  }
13
34
  //# sourceMappingURL=main.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,QAAQ,CAAC;AAEvD,qBAAa,MAAM;IACf;;OAEG;WACU,gBAAgB,CAAC,eAAe,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7E;;OAEG;WACU,IAAI,CAAC,YAAY,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CAG9D"}
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAE9C,OAAO,EAAe,KAAK,WAAW,EAAE,MAAM,QAAQ,CAAC;AACvD,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAC7D,OAAO,EAAiB,KAAK,aAAa,EAAE,MAAM,UAAU,CAAC;AAC7D,OAAO,EAAe,KAAK,WAAW,EAAE,KAAK,UAAU,EAAE,MAAM,QAAQ,CAAC;AAExE,qBAAa,MAAM;IACf;;;OAGG;WACU,gBAAgB,CAAC,eAAe,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAI7E;;;OAGG;WACU,IAAI,CAAC,YAAY,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAI3D;;;OAGG;WACU,MAAM,CAAC,cAAc,GAAE,aAAkB,GAAG,OAAO,CAAC,OAAO,CAAC;IAIzE;;;OAGG;WACU,MAAM,CAAC,cAAc,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAIjE;;;;OAIG;WACU,IAAI,CAAC,YAAY,GAAE,WAAgB,GAAG,OAAO,CAAC,UAAU,CAAC;CAGzE"}
package/lib/cli/main.js CHANGED
@@ -1,16 +1,43 @@
1
1
  import { processResources } from './resource-builder';
2
2
  import { processInit } from './init';
3
+ import { processDoctor } from './doctor';
4
+ import { processCreate } from './create';
5
+ import { processHost } from './host';
3
6
  export class KitCLI {
4
7
  /**
5
- * Start resource builder.
8
+ * Start resource builder or watcher.
9
+ * @param pProcessOptions - Configuration options for resource processing.
6
10
  */
7
11
  static async processResources(pProcessOptions) {
8
12
  await processResources(pProcessOptions);
9
13
  }
10
14
  /**
11
15
  * Start project initialization.
16
+ * @param pInitOptions - Configuration options for project initialization.
12
17
  */
13
18
  static async init(pInitOptions) {
14
19
  await processInit(pInitOptions);
15
20
  }
21
+ /**
22
+ * Run environment and project diagnostics.
23
+ * @param pDoctorOptions - Configuration options for diagnostics.
24
+ */
25
+ static async doctor(pDoctorOptions = {}) {
26
+ return await processDoctor(pDoctorOptions);
27
+ }
28
+ /**
29
+ * Create boilerplate code (e.g. plugins).
30
+ * @param pCreateOptions - Configuration options for code generation.
31
+ */
32
+ static async create(pCreateOptions) {
33
+ await processCreate(pCreateOptions);
34
+ }
35
+ /**
36
+ * Host game project locally.
37
+ * @param pHostOptions - Configuration options for hosting.
38
+ * @returns HostResult indicating status and server reference if applicable.
39
+ */
40
+ static async host(pHostOptions = {}) {
41
+ return await processHost(pHostOptions);
42
+ }
16
43
  }