@mafly/bdev-cli 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Mafly
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,226 @@
1
- # Temporary Holding Version
1
+ # bdev
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ A simple CLI tool for Minecraft Bedrock Edition addon development.
4
+
5
+ ---
6
+
7
+ ## Features
8
+
9
+ - **TypeScript Compilation**: Bundles scripts with `esbuild`, keeping `@minecraft/*` modules external.
10
+ - **In-Game Hot Reload**: Built-in WebSocket server triggers `/reload` in your active Minecraft world on file save.
11
+ - **Live Sync**: Symlinks behavior and resource packs to Bedrock `development_*_packs` folders.
12
+ - **Manifest Management**: Generates and validates `manifest.json` files with v4 UUIDs.
13
+ - **Distribution Packaging**: Compresses packs into `.mcaddon` and `.mcpack` files.
14
+ - **Diagnostics**: Checks Node.js, npm, Bun, and `com.mojang` path detection.
15
+
16
+ ---
17
+
18
+ ## Installation
19
+
20
+ ### Option 1: Via NPM (Recommended)
21
+
22
+ ```bash
23
+ npm install -g @mafly/bdev-cli
24
+ ```
25
+
26
+ Or run directly without installing:
27
+
28
+ ```bash
29
+ npx @mafly/bdev-cli --help
30
+ ```
31
+
32
+ ### Option 2: Standalone Binary
33
+
34
+ Download the compiled binary from [GitHub Releases](https://github.com/Mafly/bdev/releases).
35
+
36
+ Binary naming format: `bdev-<version>-<os>-<arch>`
37
+
38
+ | Platform | Binary |
39
+ | --------------------- | -------------------------------- |
40
+ | Windows (x64) | `bdev-<version>-windows-x64.exe` |
41
+ | Linux (x64) | `bdev-<version>-linux-x64` |
42
+ | Linux (arm64) | `bdev-<version>-linux-arm64` |
43
+ | macOS (Apple Silicon) | `bdev-<version>-macos-arm64` |
44
+ | macOS (Intel) | `bdev-<version>-macos-x64` |
45
+
46
+ On Linux/macOS:
47
+
48
+ ```bash
49
+ chmod +x bdev-<version>-linux-x64
50
+ sudo mv bdev-<version>-linux-x64 /usr/local/bin/bdev
51
+ ```
52
+
53
+ ### Option 3: From Source (Bun)
54
+
55
+ ```bash
56
+ git clone https://github.com/Mafly/bdev.git
57
+ cd bdev
58
+ bun install
59
+ bun run build
60
+ ./bdev --help
61
+ ```
62
+
63
+ ---
64
+
65
+ ## Quickstart
66
+
67
+ ```bash
68
+ # 1. Create project
69
+ bdev init my-addon
70
+ cd my-addon
71
+
72
+ # 2. (Optional) Set com.mojang path if not detected automatically
73
+ bdev set-path "C:\Users\<user>\AppData\Local\Packages\Microsoft.MinecraftUWP_8wekyb3d8bbwe\LocalState\games\com.mojang"
74
+
75
+ # 3. Start dev server
76
+ bdev dev
77
+ ```
78
+
79
+ In your Minecraft world (Cheats Enabled):
80
+
81
+ ```
82
+ /connect localhost:8000
83
+ ```
84
+
85
+ Saving any script file will rebuild and trigger `/reload` in game.
86
+
87
+ <details>
88
+ <summary><b>Project Structure</b></summary>
89
+
90
+ ```
91
+ my-addon/
92
+ ├── behavior_pack/
93
+ │ ├── manifest.json
94
+ │ └── pack_icon.png
95
+ ├── resource_pack/
96
+ │ ├── manifest.json
97
+ │ ├── pack_icon.png
98
+ │ └── texts/en_US.lang
99
+ ├── src/
100
+ │ └── main.ts
101
+ ├── bdev.json
102
+ ├── package.json
103
+ ├── tsconfig.json
104
+ └── .gitignore
105
+ ```
106
+
107
+ </details>
108
+
109
+ ---
110
+
111
+ ## Command Reference
112
+
113
+ <details>
114
+ <summary><b>bdev init [directory]</b> — Scaffold project</summary>
115
+
116
+ ```bash
117
+ bdev init my-addon [options]
118
+ ```
119
+
120
+ | Flag | Description | Default |
121
+ | ------------------- | --------------------------------------- | ----------------- |
122
+ | `-n, --name <name>` | Project and pack name | Directory name |
123
+ | `-d, --desc <desc>` | Description in manifest.json | Addon description |
124
+ | `-p, --path <path>` | Save com.mojang path to bdev.json | Auto-detected |
125
+ | `--no-rp` | Behavior pack only (skip resource pack) | Both packs |
126
+ | `--lang <ts\|js>` | Script language (`ts` or `js`) | `ts` |
127
+
128
+ </details>
129
+
130
+ <details>
131
+ <summary><b>bdev dev [directory]</b> — Live watch and Hot Reload</summary>
132
+
133
+ ```bash
134
+ bdev dev [options]
135
+ ```
136
+
137
+ Symlinks packs to `com.mojang`, starts WebSocket server, and watches for changes.
138
+
139
+ | Flag | Description | Default |
140
+ | ------------------- | -------------------------------- | ---------------------- |
141
+ | `-p, --path <path>` | Custom com.mojang directory path | Config / auto-detected |
142
+ | `-s, --save` | Save `-p` path into `bdev.json` | `false` |
143
+ | `-P, --port <port>` | WebSocket port for `/connect` | `8000` |
144
+ | `--no-ws` | Disable WebSocket server | Enabled |
145
+ | `--no-link` | Skip creating directory symlinks | Symlinked |
146
+
147
+ </details>
148
+
149
+ <details>
150
+ <summary><b>bdev build [directory]</b> — Compile TypeScript</summary>
151
+
152
+ ```bash
153
+ bdev build [options]
154
+ ```
155
+
156
+ Bundles `src/**/*.ts` into `behavior_pack/scripts/main.js` and validates `manifest.json`.
157
+
158
+ | Flag | Description | Default |
159
+ | ---------------- | ------------------------- | ------------------ |
160
+ | `-m, --minify` | Minify bundled JavaScript | `false` |
161
+ | `--no-sourcemap` | Disable sourcemap output | Sourcemaps enabled |
162
+
163
+ </details>
164
+
165
+ <details>
166
+ <summary><b>bdev package [directory]</b> — Build and export archives</summary>
167
+
168
+ ```bash
169
+ bdev package [options]
170
+ # alias: bdev deploy
171
+ ```
172
+
173
+ Creates distribution packages inside `dist/`:
174
+
175
+ - `dist/<name>.mcaddon` (contains both BP and RP)
176
+ - `dist/<name>_BP.mcpack`
177
+ - `dist/<name>_RP.mcpack`
178
+
179
+ | Flag | Description | Default |
180
+ | -------------------- | ---------------- | ------------ |
181
+ | `-o, --output <dir>` | Output directory | `dist/` |
182
+ | `-n, --name <name>` | File name prefix | Project name |
183
+
184
+ </details>
185
+
186
+ <details>
187
+ <summary><b>bdev set-path & config</b> — Manage configuration</summary>
188
+
189
+ ```bash
190
+ # Set Bedrock com.mojang path locally (bdev.json)
191
+ bdev set-path "C:\path\to\com.mojang"
192
+
193
+ # Set Bedrock path globally (~/.bdev/config.json)
194
+ bdev set-path "C:\path\to\com.mojang" -g
195
+
196
+ # View configuration
197
+ bdev config get
198
+ bdev config get mojangPath
199
+
200
+ # Set generic config key
201
+ bdev config set <key> <value> [-g]
202
+ ```
203
+
204
+ </details>
205
+
206
+ <details>
207
+ <summary><b>bdev doctor</b> — Environment diagnostics</summary>
208
+
209
+ ```bash
210
+ bdev doctor [-p <path>]
211
+ ```
212
+
213
+ Verifies:
214
+
215
+ - Operating system platform
216
+ - Node.js runtime version
217
+ - npm package manager availability
218
+ - Bun runtime availability
219
+ - Minecraft Bedrock `com.mojang` path detection
220
+ </details>
221
+
222
+ ---
223
+
224
+ ## License
225
+
226
+ [MIT](./LICENSE)
@@ -0,0 +1,6 @@
1
+ export interface BuildOptions {
2
+ minify?: boolean;
3
+ sourcemap?: boolean;
4
+ watch?: boolean;
5
+ }
6
+ export declare function buildCommand(dirArg?: string, options?: BuildOptions): Promise<boolean>;
@@ -0,0 +1,94 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { runBuild } from "../utils/bundler.js";
4
+ import { validateManifest } from "../utils/manifest.js";
5
+ import { logger } from "../utils/logger.js";
6
+ export async function buildCommand(dirArg, options = {}) {
7
+ const rootDir = path.resolve(dirArg || ".");
8
+ const bpDir = path.join(rootDir, "behavior_pack");
9
+ const bpManifestPath = path.join(bpDir, "manifest.json");
10
+ // 1. Validate Behavior Pack Manifest
11
+ if (!fs.existsSync(bpManifestPath)) {
12
+ logger.error(`Behavior pack manifest not found: ${bpManifestPath}`);
13
+ return false;
14
+ }
15
+ logger.info("Validating manifest.json files...");
16
+ const bpValidation = validateManifest(bpManifestPath);
17
+ if (!bpValidation.valid) {
18
+ logger.error("Behavior pack manifest validation failed:");
19
+ for (const err of bpValidation.errors) {
20
+ console.error(` - ${err}`);
21
+ }
22
+ return false;
23
+ }
24
+ logger.success("Behavior pack manifest valid");
25
+ // Validate Resource Pack Manifest if exists
26
+ const rpDir = path.join(rootDir, "resource_pack");
27
+ const rpManifestPath = path.join(rpDir, "manifest.json");
28
+ if (fs.existsSync(rpManifestPath)) {
29
+ const rpValidation = validateManifest(rpManifestPath);
30
+ if (!rpValidation.valid) {
31
+ logger.error("Resource pack manifest validation failed:");
32
+ for (const err of rpValidation.errors) {
33
+ console.error(` - ${err}`);
34
+ }
35
+ return false;
36
+ }
37
+ logger.success("Resource pack manifest valid");
38
+ }
39
+ // 2. Determine if TypeScript compilation is needed
40
+ const srcDir = path.join(rootDir, "src");
41
+ if (!fs.existsSync(srcDir)) {
42
+ logger.info("No 'src' directory found. Skipping script compilation.");
43
+ return true;
44
+ }
45
+ // Find entry points
46
+ let entryPoint = "";
47
+ if (fs.existsSync(path.join(srcDir, "main.ts"))) {
48
+ entryPoint = path.join(srcDir, "main.ts");
49
+ }
50
+ else if (fs.existsSync(path.join(srcDir, "index.ts"))) {
51
+ entryPoint = path.join(srcDir, "index.ts");
52
+ }
53
+ else {
54
+ // Look for any .ts file
55
+ const files = fs.readdirSync(srcDir).filter((f) => f.endsWith(".ts"));
56
+ if (files.length > 0) {
57
+ entryPoint = path.join(srcDir, files[0]);
58
+ }
59
+ }
60
+ if (!entryPoint) {
61
+ logger.warn("No TypeScript entry point (.ts) found in 'src/'. Skipping bundling.");
62
+ return true;
63
+ }
64
+ const outDir = path.join(bpDir, "scripts");
65
+ if (!fs.existsSync(outDir)) {
66
+ fs.mkdirSync(outDir, { recursive: true });
67
+ }
68
+ const outFile = path.join(outDir, "main.js");
69
+ const spinner = logger.spinner(`Bundling ${path.relative(rootDir, entryPoint)} -> ${path.relative(rootDir, outFile)}...`);
70
+ try {
71
+ const startTime = Date.now();
72
+ await runBuild({
73
+ entryPoints: [entryPoint],
74
+ outfile: outFile,
75
+ bundle: true,
76
+ format: "esm",
77
+ target: "es2020",
78
+ platform: "neutral",
79
+ minify: options.minify ?? false,
80
+ sourcemap: options.sourcemap ?? true,
81
+ external: [
82
+ "@minecraft/*",
83
+ ],
84
+ });
85
+ const duration = Date.now() - startTime;
86
+ spinner.succeed(`Build succeeded in ${duration}ms (${path.relative(rootDir, outFile)})`);
87
+ return true;
88
+ }
89
+ catch (err) {
90
+ spinner.fail("Build failed");
91
+ logger.error(err.message);
92
+ return false;
93
+ }
94
+ }
@@ -0,0 +1,7 @@
1
+ export interface ConfigOptions {
2
+ global?: boolean;
3
+ dir?: string;
4
+ }
5
+ export declare function setConfigCommand(key: string, value: string, options?: ConfigOptions): Promise<void>;
6
+ export declare function getConfigCommand(key?: string, options?: ConfigOptions): Promise<void>;
7
+ export declare function setPathCommand(inputPath: string, options?: ConfigOptions): Promise<void>;
@@ -0,0 +1,46 @@
1
+ import path from "node:path";
2
+ import fs from "node:fs";
3
+ import { loadGlobalConfig, loadMergedConfig, saveConfig, getGlobalConfigPath, getLocalConfigPath, } from "../utils/config.js";
4
+ import { logger } from "../utils/logger.js";
5
+ export async function setConfigCommand(key, value, options = {}) {
6
+ const normalizedKey = key === "path" || key === "mojang-path" ? "mojangPath" : key;
7
+ if (normalizedKey === "mojangPath") {
8
+ const resolved = path.resolve(value);
9
+ if (!fs.existsSync(resolved)) {
10
+ logger.warn(`Target path does not exist on disk: ${resolved}`);
11
+ }
12
+ value = resolved;
13
+ }
14
+ const updates = {
15
+ [normalizedKey]: value,
16
+ };
17
+ const savedFile = saveConfig(updates, {
18
+ global: options.global,
19
+ dir: options.dir,
20
+ });
21
+ logger.success(`Set ${normalizedKey} = "${value}" in ${savedFile}`);
22
+ }
23
+ export async function getConfigCommand(key, options = {}) {
24
+ const conf = options.global
25
+ ? loadGlobalConfig()
26
+ : loadMergedConfig(options.dir);
27
+ if (key) {
28
+ const normalizedKey = key === "path" || key === "mojang-path" ? "mojangPath" : key;
29
+ const val = conf[normalizedKey];
30
+ if (val !== undefined) {
31
+ console.log(val);
32
+ }
33
+ else {
34
+ logger.info(`Key '${key}' is not set.`);
35
+ }
36
+ }
37
+ else {
38
+ logger.info("Current configuration:");
39
+ console.log(JSON.stringify(conf, null, 2));
40
+ logger.info(`Local config file: ${getLocalConfigPath(options.dir)}`);
41
+ logger.info(`Global config file: ${getGlobalConfigPath()}`);
42
+ }
43
+ }
44
+ export async function setPathCommand(inputPath, options = {}) {
45
+ await setConfigCommand("mojangPath", inputPath, options);
46
+ }
@@ -0,0 +1,8 @@
1
+ export interface DevOptions {
2
+ path?: string;
3
+ link?: boolean;
4
+ save?: boolean;
5
+ port?: number;
6
+ ws?: boolean;
7
+ }
8
+ export declare function devCommand(dirArg?: string, options?: DevOptions): Promise<void>;
@@ -0,0 +1,165 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import chokidar from "chokidar";
4
+ import pc from "picocolors";
5
+ import { buildCommand } from "./build.js";
6
+ import { findMojangPath, linkPack } from "../utils/mojang.js";
7
+ import { saveConfig } from "../utils/config.js";
8
+ import { BedrockHotReloadServer } from "../utils/wsserver.js";
9
+ import { logger } from "../utils/logger.js";
10
+ import { validateManifest } from "../utils/manifest.js";
11
+ export async function devCommand(dirArg, options = {}) {
12
+ const rootDir = path.resolve(dirArg || ".");
13
+ const bpDir = path.join(rootDir, "behavior_pack");
14
+ const rpDir = path.join(rootDir, "resource_pack");
15
+ const hasBp = fs.existsSync(bpDir);
16
+ const hasRp = fs.existsSync(rpDir);
17
+ if (!hasBp && !hasRp) {
18
+ logger.error(`No behavior_pack or resource_pack found in ${rootDir}. Run 'bdev init' first.`);
19
+ process.exit(1);
20
+ }
21
+ const folderName = path.basename(rootDir);
22
+ // If --save and path provided, save to bdev.json
23
+ if (options.save && options.path) {
24
+ const resolvedPath = path.resolve(options.path);
25
+ saveConfig({ mojangPath: resolvedPath }, { dir: rootDir });
26
+ logger.success(`Saved mojang path to bdev.json: ${resolvedPath}`);
27
+ }
28
+ // 1. Initial build
29
+ logger.info("Executing initial build...");
30
+ const initialBuildOk = await buildCommand(rootDir);
31
+ if (!initialBuildOk) {
32
+ logger.warn("Initial build had warnings or errors. Continuing dev server...");
33
+ }
34
+ // 2. Setup symlinks to com.mojang
35
+ const shouldLink = options.link !== false;
36
+ if (shouldLink) {
37
+ const mojang = findMojangPath(options.path, rootDir);
38
+ if (!mojang) {
39
+ logger.warn("Minecraft com.mojang directory not found.");
40
+ logger.warn("Tip: Set with 'bdev set-path <path>' or pass '-p <path>' or 'BEDROCK_MOJANG_PATH'.");
41
+ logger.warn("Continuing in watch-only mode (without symlinking).");
42
+ }
43
+ else {
44
+ logger.info(`Found com.mojang location (${mojang.type}): ${mojang.root}`);
45
+ if (hasBp) {
46
+ const destBp = path.join(mojang.devBehaviorPacks, `${folderName}_BP`);
47
+ try {
48
+ linkPack(bpDir, destBp);
49
+ logger.success(`Linked Behavior Pack -> ${destBp}`);
50
+ }
51
+ catch (err) {
52
+ logger.error(`Failed to link Behavior Pack: ${err.message}`);
53
+ }
54
+ }
55
+ if (hasRp) {
56
+ const destRp = path.join(mojang.devResourcePacks, `${folderName}_RP`);
57
+ try {
58
+ linkPack(rpDir, destRp);
59
+ logger.success(`Linked Resource Pack -> ${destRp}`);
60
+ }
61
+ catch (err) {
62
+ logger.error(`Failed to link Resource Pack: ${err.message}`);
63
+ }
64
+ }
65
+ }
66
+ }
67
+ // 3. Start WebSocket Hot Reload server
68
+ const enableWs = options.ws !== false;
69
+ let hotReloadServer = null;
70
+ let wsPort = options.port ? Number(options.port) : 8000;
71
+ if (enableWs) {
72
+ try {
73
+ hotReloadServer = new BedrockHotReloadServer(wsPort);
74
+ wsPort = await hotReloadServer.start();
75
+ console.log();
76
+ logger.info(pc.bold(`Hot Reload WebSocket server active on ${pc.cyan(`ws://localhost:${wsPort}`)}`));
77
+ logger.info(pc.yellow(`To connect in Minecraft (cheats on): ${pc.bold(`/connect localhost:${wsPort}`)}`));
78
+ console.log();
79
+ }
80
+ catch (err) {
81
+ logger.error(`Failed to start WebSocket hot reload server: ${err.message}`);
82
+ }
83
+ }
84
+ // 4. Start chokidar watcher
85
+ const watchPaths = [];
86
+ const srcDir = path.join(rootDir, "src");
87
+ if (fs.existsSync(srcDir)) {
88
+ watchPaths.push(srcDir);
89
+ }
90
+ if (hasBp) {
91
+ watchPaths.push(bpDir);
92
+ }
93
+ if (hasRp) {
94
+ watchPaths.push(rpDir);
95
+ }
96
+ logger.info(`Starting file watcher on: ${watchPaths.map((p) => path.relative(rootDir, p)).join(", ")}`);
97
+ logger.info("Press Ctrl+C to stop.");
98
+ let isRebuilding = false;
99
+ let rebuildTimeout = null;
100
+ const watcher = chokidar.watch(watchPaths, {
101
+ ignored: [
102
+ /(^|[/\\])\../, // dotfiles
103
+ /node_modules/,
104
+ /dist/,
105
+ /behavior_pack[/\\]scripts[/\\]main\.js(\.map)?$/, // ignore build output
106
+ ],
107
+ persistent: true,
108
+ ignoreInitial: true,
109
+ });
110
+ const triggerRebuild = (filePath) => {
111
+ const rel = path.relative(rootDir, filePath);
112
+ logger.info(`Change detected in ${rel}`);
113
+ if (rebuildTimeout) {
114
+ clearTimeout(rebuildTimeout);
115
+ }
116
+ rebuildTimeout = setTimeout(async () => {
117
+ if (isRebuilding)
118
+ return;
119
+ isRebuilding = true;
120
+ try {
121
+ if (filePath.endsWith("manifest.json")) {
122
+ const res = validateManifest(filePath);
123
+ if (res.valid) {
124
+ logger.success(`Validated ${rel}`);
125
+ }
126
+ else {
127
+ logger.error(`Invalid manifest in ${rel}:`);
128
+ res.errors.forEach((e) => console.error(` - ${e}`));
129
+ }
130
+ }
131
+ // Run build if src changed or if script output needed
132
+ if (fs.existsSync(srcDir)) {
133
+ const buildOk = await buildCommand(rootDir);
134
+ if (buildOk && hotReloadServer) {
135
+ if (hotReloadServer.clientCount > 0) {
136
+ hotReloadServer.reload();
137
+ logger.success("Sent /reload to Minecraft via WebSocket");
138
+ }
139
+ else {
140
+ logger.info(`Hot reload skipped (no Minecraft client connected; run /connect localhost:${wsPort} in game)`);
141
+ }
142
+ }
143
+ }
144
+ }
145
+ catch (err) {
146
+ logger.error(`Error during rebuild: ${err.message}`);
147
+ }
148
+ finally {
149
+ isRebuilding = false;
150
+ }
151
+ }, 100);
152
+ };
153
+ watcher.on("change", triggerRebuild);
154
+ watcher.on("add", triggerRebuild);
155
+ watcher.on("unlink", triggerRebuild);
156
+ // Handle graceful exit
157
+ process.on("SIGINT", async () => {
158
+ logger.info("Stopping dev server and watcher...");
159
+ await watcher.close();
160
+ if (hotReloadServer) {
161
+ await hotReloadServer.stop();
162
+ }
163
+ process.exit(0);
164
+ });
165
+ }
@@ -0,0 +1 @@
1
+ export declare function doctorCommand(customPath?: string): Promise<void>;
@@ -0,0 +1,84 @@
1
+ import { execSync } from "node:child_process";
2
+ import os from "node:os";
3
+ import pc from "picocolors";
4
+ import { findMojangPath } from "../utils/mojang.js";
5
+ import { logger } from "../utils/logger.js";
6
+ function checkCommand(cmd) {
7
+ try {
8
+ const out = execSync(cmd, { stdio: ["pipe", "pipe", "pipe"], encoding: "utf-8" });
9
+ return out.trim();
10
+ }
11
+ catch {
12
+ return null;
13
+ }
14
+ }
15
+ export async function doctorCommand(customPath) {
16
+ console.log(pc.bold("\n Bedrock Dev (bdev) Environment Doctor\n"));
17
+ const checks = [];
18
+ // OS Info
19
+ const platform = os.platform();
20
+ const release = os.release();
21
+ checks.push({
22
+ name: "Operating System",
23
+ ok: true,
24
+ details: `${platform} (${release})`,
25
+ });
26
+ // Node.js
27
+ const nodeVersion = process.version;
28
+ checks.push({
29
+ name: "Node.js Runtime",
30
+ ok: Boolean(nodeVersion),
31
+ details: nodeVersion || "Not found",
32
+ });
33
+ // npm
34
+ const npmVersion = checkCommand("npm --version");
35
+ checks.push({
36
+ name: "npm Package Manager",
37
+ ok: Boolean(npmVersion),
38
+ details: npmVersion ? `v${npmVersion}` : "Not found in PATH",
39
+ });
40
+ // Bun
41
+ const bunVersion = checkCommand("bun --version");
42
+ checks.push({
43
+ name: "Bun Runtime",
44
+ ok: Boolean(bunVersion),
45
+ details: bunVersion ? `v${bunVersion}` : "Not found in PATH (optional)",
46
+ });
47
+ // Minecraft Bedrock com.mojang folder
48
+ const mojang = findMojangPath(customPath);
49
+ if (mojang) {
50
+ checks.push({
51
+ name: "Minecraft Bedrock (com.mojang)",
52
+ ok: true,
53
+ details: `Found (${mojang.type}): ${mojang.root}`,
54
+ });
55
+ }
56
+ else {
57
+ checks.push({
58
+ name: "Minecraft Bedrock (com.mojang)",
59
+ ok: false,
60
+ details: "Directory not found. Expected in Windows UWP LocalAppData or specify via BEDROCK_MOJANG_PATH",
61
+ });
62
+ }
63
+ // Display results
64
+ let allPassed = true;
65
+ for (const item of checks) {
66
+ const symbol = item.ok ? pc.green("✔") : pc.red("✖");
67
+ const nameStr = pc.bold(item.name.padEnd(32));
68
+ console.log(` ${symbol} ${nameStr} ${item.details}`);
69
+ if (!item.ok && item.name !== "Minecraft Bedrock (com.mojang)") {
70
+ allPassed = false;
71
+ }
72
+ }
73
+ console.log();
74
+ if (!mojang) {
75
+ logger.warn("Notice: com.mojang was not detected automatically. If you develop on Windows or use a custom path, export BEDROCK_MOJANG_PATH='/path/to/com.mojang'.");
76
+ }
77
+ if (allPassed) {
78
+ logger.success("Core development environment is ready!");
79
+ }
80
+ else {
81
+ logger.warn("Some environment checks did not pass. Review the items marked with ✖ above.");
82
+ }
83
+ console.log();
84
+ }
@@ -0,0 +1,8 @@
1
+ export interface InitOptions {
2
+ name?: string;
3
+ desc?: string;
4
+ rp: boolean;
5
+ lang: "ts" | "js";
6
+ path?: string;
7
+ }
8
+ export declare function initCommand(targetDirArg: string | undefined, options: InitOptions): Promise<void>;