@bike4mind/cli 0.2.64-worktree-refactor-extract-search-query-builders.21815 → 0.2.64

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 (85) hide show
  1. package/bin/bike4mind-cli.mjs +6 -6
  2. package/dist/BubblewrapRuntime-BHbtqvLx.mjs +72 -0
  3. package/dist/ConfigStore-CllM6jOf.mjs +8614 -0
  4. package/dist/ImageStore-DaKT_Ew8.mjs +202 -0
  5. package/dist/ProxyManager-Dl2nFk-A.mjs +259 -0
  6. package/dist/ProxyManager-kiOD1X8-.mjs +3 -0
  7. package/dist/SandboxOrchestrator-BEW3rqYi.mjs +159 -0
  8. package/dist/SandboxOrchestrator-CHZgSR3P.mjs +3 -0
  9. package/dist/SandboxRuntimeAdapter-C1B4t20N.mjs +57 -0
  10. package/dist/SandboxRuntimeAdapter-D7UAG13n.mjs +3 -0
  11. package/dist/SeatbeltRuntime-D4m0VOcD.mjs +116 -0
  12. package/dist/StderrViolationParser-D0afQ3-1.mjs +70 -0
  13. package/dist/ViolationLogStore-CZl35HcA.mjs +96 -0
  14. package/dist/bashExecute-BTkdqlSs-5foM20Lb.mjs +466 -0
  15. package/dist/commands/doctorCommand.mjs +101 -0
  16. package/dist/commands/headlessCommand.mjs +319 -0
  17. package/dist/commands/mcpCommand.mjs +218 -0
  18. package/dist/commands/updateCommand.mjs +40 -0
  19. package/dist/createFile-yQfh8uvk-I-yM5DxC.mjs +63 -0
  20. package/dist/deleteFile-DKHfnyny-G3b1Kj2T.mjs +66 -0
  21. package/dist/globFiles-D1en6joM-8jekiXdX.mjs +100 -0
  22. package/dist/grepSearch-aMamoBn_-DCJcY8JS.mjs +173 -0
  23. package/dist/index.mjs +6722 -0
  24. package/dist/pathValidation-Cgjh5WQO-DiCZTcq6.mjs +63 -0
  25. package/dist/store-Dw1nZX2Y.mjs +128 -0
  26. package/dist/store-nZExNOWX.mjs +3 -0
  27. package/dist/terminalSetup-rmr1P8KF.mjs +254 -0
  28. package/dist/tools-C6M5aW8W.mjs +20907 -0
  29. package/dist/treeSitterEngine-DCSXcm_3.mjs +309 -0
  30. package/dist/types-DBEjF9YS.mjs +59 -0
  31. package/dist/types-DK3P88Px.mjs +3 -0
  32. package/dist/updateChecker-Cu9dkHxV.mjs +120 -0
  33. package/package.json +10 -10
  34. package/dist/BubblewrapRuntime-PMIOLWKR.js +0 -71
  35. package/dist/HydrationEngine-YL2HWJ3V.js +0 -9
  36. package/dist/ImageStore-MMUOUPI2.js +0 -224
  37. package/dist/ProxyManager-HEB4TLVX.js +0 -7
  38. package/dist/SandboxOrchestrator-UIJ5GYBB.js +0 -8
  39. package/dist/SandboxRuntimeAdapter-FQ56MAB2.js +0 -13
  40. package/dist/SeatbeltRuntime-EE3TTLEP.js +0 -98
  41. package/dist/StderrViolationParser-7OYPM2DJ.js +0 -59
  42. package/dist/ViolationLogStore-RIIUVURH.js +0 -104
  43. package/dist/artifactExtractor-R7DIP2XO.js +0 -180
  44. package/dist/bashExecute-GLGLD3JD.js +0 -379
  45. package/dist/chunk-4BIBE3J7.js +0 -48
  46. package/dist/chunk-5LZS5CVJ.js +0 -161
  47. package/dist/chunk-BDQBOLYG.js +0 -120
  48. package/dist/chunk-BPFEGDC7.js +0 -192
  49. package/dist/chunk-EPIYC3LA.js +0 -13770
  50. package/dist/chunk-G4ZGEQFT.js +0 -250
  51. package/dist/chunk-GQGOWACU.js +0 -770
  52. package/dist/chunk-J6ZBI6TI.js +0 -1079
  53. package/dist/chunk-JW3JRHH7.js +0 -12433
  54. package/dist/chunk-KQAMBXAW.js +0 -163
  55. package/dist/chunk-KUVV2NAB.js +0 -19125
  56. package/dist/chunk-LTLJRF6I.js +0 -44
  57. package/dist/chunk-PFBYGCOW.js +0 -449
  58. package/dist/chunk-QWB6ZYY4.js +0 -48
  59. package/dist/chunk-SGPRXN4C.js +0 -245
  60. package/dist/chunk-UZUHPHZC.js +0 -95
  61. package/dist/chunk-WBE7SQUB.js +0 -241
  62. package/dist/chunk-Y4WOJJM3.js +0 -147
  63. package/dist/commands/doctorCommand.js +0 -87
  64. package/dist/commands/headlessCommand.js +0 -380
  65. package/dist/commands/mcpCommand.js +0 -203
  66. package/dist/commands/updateCommand.js +0 -42
  67. package/dist/create-C4VEEEYR.js +0 -12
  68. package/dist/createFile-6PSPLW6R.js +0 -71
  69. package/dist/deleteFile-AUSRLWIK.js +0 -73
  70. package/dist/formatConverter-5QEJDW24.js +0 -7
  71. package/dist/globFiles-TSRN64N2.js +0 -120
  72. package/dist/grepSearch-634XWZOJ.js +0 -216
  73. package/dist/index.js +0 -6779
  74. package/dist/llmMarkdownGenerator-Z6NB26TT.js +0 -371
  75. package/dist/markdownGenerator-SK2ZQQL4.js +0 -269
  76. package/dist/mementoService-N4IM6QAC.js +0 -12
  77. package/dist/notificationDeduplicator-HUC53NEW.js +0 -9
  78. package/dist/src-F4KZCAA2.js +0 -319
  79. package/dist/src-ISX322I7.js +0 -1101
  80. package/dist/store-CAB6BV3P.js +0 -11
  81. package/dist/subtractCredits-D4KEM6VU.js +0 -12
  82. package/dist/terminalSetup-C5FHMLC3.js +0 -214
  83. package/dist/treeSitterEngine-4SGFQDY3.js +0 -330
  84. package/dist/types-KB5NP6T4.js +0 -7
  85. package/dist/utils-JCHWDM4Z.js +0 -31
@@ -0,0 +1,466 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from "child_process";
3
+ import path from "path";
4
+ //#region ../../b4m-core/packages/services/dist/bashExecute-BTkdqlSs.mjs
5
+ const DEFAULT_TIMEOUT_MS = 6e4;
6
+ const MAX_OUTPUT_SIZE = 100 * 1024;
7
+ /**
8
+ * Dangerous command patterns that should be blocked or warned about.
9
+ * These patterns are checked against the full command string.
10
+ */
11
+ const DANGEROUS_PATTERNS = [
12
+ {
13
+ pattern: /\brm\s+(-[a-zA-Z]*r[a-zA-Z]*\s+|.*\s+-[a-zA-Z]*r).*\//i,
14
+ reason: "Recursive delete with path",
15
+ block: true
16
+ },
17
+ {
18
+ pattern: /\brm\s+(-[a-zA-Z]*f[a-zA-Z]*\s+|.*\s+-[a-zA-Z]*f).*--no-preserve-root/i,
19
+ reason: "Force delete without preserve root",
20
+ block: true
21
+ },
22
+ {
23
+ pattern: /\brm\s+-[a-zA-Z]*r[a-zA-Z]*f[a-zA-Z]*\s+\//i,
24
+ reason: "Recursive force delete on root paths",
25
+ block: true
26
+ },
27
+ {
28
+ pattern: /\brm\s+-[a-zA-Z]*f[a-zA-Z]*r[a-zA-Z]*\s+\//i,
29
+ reason: "Force recursive delete on root paths",
30
+ block: true
31
+ },
32
+ {
33
+ pattern: /\bsudo\b/i,
34
+ reason: "Elevated privileges (sudo)",
35
+ block: true
36
+ },
37
+ {
38
+ pattern: /\bsu\s+(-|root)/i,
39
+ reason: "Switch to root user",
40
+ block: true
41
+ },
42
+ {
43
+ pattern: /\bchmod\s+777\b/i,
44
+ reason: "Overly permissive chmod",
45
+ block: false
46
+ },
47
+ {
48
+ pattern: /\bchown\s+-R\s+root/i,
49
+ reason: "Recursive chown to root",
50
+ block: true
51
+ },
52
+ {
53
+ pattern: /\bmkfs\b/i,
54
+ reason: "Filesystem creation",
55
+ block: true
56
+ },
57
+ {
58
+ pattern: /\bfdisk\b/i,
59
+ reason: "Disk partitioning",
60
+ block: true
61
+ },
62
+ {
63
+ pattern: /\bdd\s+.*of=\/dev\//i,
64
+ reason: "Direct disk write",
65
+ block: true
66
+ },
67
+ {
68
+ pattern: /\b(nc|netcat)\s+.*-e\s+\/bin\/(ba)?sh/i,
69
+ reason: "Reverse shell attempt",
70
+ block: true
71
+ },
72
+ {
73
+ pattern: /\bcurl\s+.*\|\s*(ba)?sh/i,
74
+ reason: "Piping remote script to shell",
75
+ block: true
76
+ },
77
+ {
78
+ pattern: /\bwget\s+.*\|\s*(ba)?sh/i,
79
+ reason: "Piping remote script to shell",
80
+ block: true
81
+ },
82
+ {
83
+ pattern: /:\(\)\s*\{\s*:\|:&\s*\}\s*;/i,
84
+ reason: "Fork bomb",
85
+ block: true
86
+ },
87
+ {
88
+ pattern: /\bwhile\s+true.*do.*done.*&/i,
89
+ reason: "Infinite loop in background",
90
+ block: false
91
+ },
92
+ {
93
+ pattern: /\/etc\/shadow/i,
94
+ reason: "Access to shadow file",
95
+ block: true
96
+ },
97
+ {
98
+ pattern: /\/etc\/passwd.*>/i,
99
+ reason: "Modifying passwd file",
100
+ block: true
101
+ },
102
+ {
103
+ pattern: /\baws\s+.*--profile\s+/i,
104
+ reason: "AWS profile access",
105
+ block: false
106
+ },
107
+ {
108
+ pattern: /\bhistory\s+-c\b/i,
109
+ reason: "Clearing shell history",
110
+ block: false
111
+ },
112
+ {
113
+ pattern: />\s*\/var\/log\//i,
114
+ reason: "Overwriting system logs",
115
+ block: true
116
+ },
117
+ {
118
+ pattern: />\s*\/dev\/sda/i,
119
+ reason: "Writing to block device",
120
+ block: true
121
+ },
122
+ {
123
+ pattern: />\s*\/dev\/null.*2>&1.*</i,
124
+ reason: "Potentially hiding output",
125
+ block: false
126
+ },
127
+ {
128
+ pattern: /\bexport\s+PATH\s*=\s*[^$]/i,
129
+ reason: "Overwriting PATH",
130
+ block: false
131
+ },
132
+ {
133
+ pattern: /\bexport\s+LD_PRELOAD/i,
134
+ reason: "LD_PRELOAD manipulation",
135
+ block: true
136
+ },
137
+ {
138
+ pattern: /\bkill\s+-9\s+(-1|1)\b/i,
139
+ reason: "Killing all processes",
140
+ block: true
141
+ },
142
+ {
143
+ pattern: /\bkillall\s+-9\b/i,
144
+ reason: "Force killing processes",
145
+ block: false
146
+ },
147
+ {
148
+ pattern: /\bshutdown\b/i,
149
+ reason: "System shutdown",
150
+ block: true
151
+ },
152
+ {
153
+ pattern: /\breboot\b/i,
154
+ reason: "System reboot",
155
+ block: true
156
+ },
157
+ {
158
+ pattern: /\binit\s+[06]\b/i,
159
+ reason: "System runlevel change",
160
+ block: true
161
+ }
162
+ ];
163
+ /**
164
+ * Commands that are generally safe and commonly used in development
165
+ */
166
+ const SAFE_COMMAND_PREFIXES = [
167
+ "ls",
168
+ "cat",
169
+ "head",
170
+ "tail",
171
+ "grep",
172
+ "find",
173
+ "echo",
174
+ "pwd",
175
+ "whoami",
176
+ "date",
177
+ "cal",
178
+ "wc",
179
+ "sort",
180
+ "uniq",
181
+ "cut",
182
+ "tr",
183
+ "sed",
184
+ "awk",
185
+ "git",
186
+ "npm",
187
+ "pnpm",
188
+ "yarn",
189
+ "node",
190
+ "npx",
191
+ "tsx",
192
+ "ts-node",
193
+ "python",
194
+ "python3",
195
+ "pip",
196
+ "pip3",
197
+ "docker",
198
+ "docker-compose",
199
+ "curl",
200
+ "wget",
201
+ "mkdir",
202
+ "touch",
203
+ "cp",
204
+ "mv",
205
+ "which",
206
+ "whereis",
207
+ "type",
208
+ "file",
209
+ "stat",
210
+ "env",
211
+ "printenv",
212
+ "set",
213
+ "man",
214
+ "help",
215
+ "info",
216
+ "diff",
217
+ "comm",
218
+ "cmp",
219
+ "tar",
220
+ "zip",
221
+ "unzip",
222
+ "gzip",
223
+ "gunzip",
224
+ "ssh",
225
+ "scp",
226
+ "rsync",
227
+ "make",
228
+ "cmake",
229
+ "cargo",
230
+ "go",
231
+ "rustc",
232
+ "gcc",
233
+ "g++",
234
+ "jest",
235
+ "vitest",
236
+ "mocha",
237
+ "pytest",
238
+ "eslint",
239
+ "prettier",
240
+ "tsc"
241
+ ];
242
+ /**
243
+ * Check if a command matches any dangerous patterns
244
+ */
245
+ function checkDangerousPatterns(command) {
246
+ for (const { pattern, reason, block } of DANGEROUS_PATTERNS) if (pattern.test(command)) return {
247
+ blocked: block,
248
+ reason
249
+ };
250
+ return { blocked: false };
251
+ }
252
+ /**
253
+ * Get the base command from a command string
254
+ */
255
+ function getBaseCommand(command) {
256
+ const match = command.trim().match(/^(\S+)/);
257
+ return match ? match[1] : "";
258
+ }
259
+ /**
260
+ * Check if command starts with a safe prefix
261
+ */
262
+ function isSafeCommandPrefix(command) {
263
+ const baseCommand = getBaseCommand(command);
264
+ return SAFE_COMMAND_PREFIXES.some((safe) => baseCommand === safe || baseCommand.endsWith(`/${safe}`));
265
+ }
266
+ /**
267
+ * Execute a bash command with safety checks
268
+ */
269
+ async function executeBashCommand(params) {
270
+ const { command, cwd: relativeCwd, timeout = DEFAULT_TIMEOUT_MS } = params;
271
+ if (!command || command.trim().length === 0) return {
272
+ stdout: "",
273
+ stderr: "Error: Command cannot be empty",
274
+ exitCode: 1,
275
+ timedOut: false,
276
+ blocked: true,
277
+ blockedReason: "Empty command"
278
+ };
279
+ const dangerCheck = checkDangerousPatterns(command);
280
+ if (dangerCheck.blocked) return {
281
+ stdout: "",
282
+ stderr: `Command blocked for safety: ${dangerCheck.reason}`,
283
+ exitCode: 1,
284
+ timedOut: false,
285
+ blocked: true,
286
+ blockedReason: dangerCheck.reason
287
+ };
288
+ const baseCwd = process.cwd();
289
+ const targetCwd = relativeCwd ? path.resolve(baseCwd, relativeCwd) : baseCwd;
290
+ const effectiveTimeout = Math.min(timeout, 300 * 1e3);
291
+ return new Promise((resolve) => {
292
+ let stdout = "";
293
+ let stderr = "";
294
+ let timedOut = false;
295
+ const proc = spawn("bash", ["-c", command], {
296
+ cwd: targetCwd,
297
+ env: {
298
+ ...process.env,
299
+ NO_COLOR: "1",
300
+ FORCE_COLOR: "0"
301
+ },
302
+ stdio: [
303
+ "ignore",
304
+ "pipe",
305
+ "pipe"
306
+ ]
307
+ });
308
+ const timeoutId = setTimeout(() => {
309
+ timedOut = true;
310
+ proc.kill("SIGTERM");
311
+ setTimeout(() => {
312
+ if (!proc.killed) proc.kill("SIGKILL");
313
+ }, 5e3);
314
+ }, effectiveTimeout);
315
+ proc.stdout.on("data", (data) => {
316
+ if (stdout.length < MAX_OUTPUT_SIZE) {
317
+ stdout += data.toString();
318
+ if (stdout.length > MAX_OUTPUT_SIZE) stdout = stdout.slice(0, MAX_OUTPUT_SIZE) + "\n... [output truncated]";
319
+ }
320
+ });
321
+ proc.stderr.on("data", (data) => {
322
+ if (stderr.length < MAX_OUTPUT_SIZE) {
323
+ stderr += data.toString();
324
+ if (stderr.length > MAX_OUTPUT_SIZE) stderr = stderr.slice(0, MAX_OUTPUT_SIZE) + "\n... [output truncated]";
325
+ }
326
+ });
327
+ proc.on("close", (exitCode) => {
328
+ clearTimeout(timeoutId);
329
+ resolve({
330
+ stdout: stdout.trim(),
331
+ stderr: stderr.trim(),
332
+ exitCode,
333
+ timedOut,
334
+ blocked: false
335
+ });
336
+ });
337
+ proc.on("error", (error) => {
338
+ clearTimeout(timeoutId);
339
+ resolve({
340
+ stdout: "",
341
+ stderr: `Failed to execute command: ${error.message}`,
342
+ exitCode: 1,
343
+ timedOut: false,
344
+ blocked: false
345
+ });
346
+ });
347
+ });
348
+ }
349
+ /**
350
+ * Format the result for display
351
+ */
352
+ function formatResult(result, command) {
353
+ const parts = [];
354
+ parts.push(`$ ${command}`);
355
+ parts.push("");
356
+ if (result.blocked) {
357
+ parts.push(`BLOCKED: ${result.blockedReason}`);
358
+ parts.push("");
359
+ parts.push("This command was blocked for safety reasons.");
360
+ parts.push("If you believe this is a false positive, please run the command manually.");
361
+ return parts.join("\n");
362
+ }
363
+ if (result.timedOut) {
364
+ parts.push("WARNING: Command timed out and was terminated.");
365
+ parts.push("");
366
+ }
367
+ if (result.stdout) parts.push(result.stdout);
368
+ if (result.stderr) {
369
+ if (result.stdout) parts.push("");
370
+ parts.push("STDERR:");
371
+ parts.push(result.stderr);
372
+ }
373
+ if (result.exitCode !== 0 && result.exitCode !== null) {
374
+ parts.push("");
375
+ parts.push(`Exit code: ${result.exitCode}`);
376
+ }
377
+ if (!result.stdout && !result.stderr && !result.timedOut) parts.push("(command completed with no output)");
378
+ return parts.join("\n");
379
+ }
380
+ const bashExecuteTool = {
381
+ name: "bash_execute",
382
+ implementation: (context) => ({
383
+ toolFn: async (value) => {
384
+ const params = value;
385
+ const isSafe = isSafeCommandPrefix(params.command);
386
+ context.logger.info("Bash: Executing command", {
387
+ command: params.command,
388
+ cwd: params.cwd || ".",
389
+ timeout: params.timeout || DEFAULT_TIMEOUT_MS,
390
+ isSafeCommand: isSafe
391
+ });
392
+ if (context.onStart) await context.onStart("bash_execute", {
393
+ command: params.command,
394
+ cwd: params.cwd
395
+ });
396
+ try {
397
+ const result = await executeBashCommand(params);
398
+ const formattedResult = formatResult(result, params.command);
399
+ context.logger.info("Bash: Command completed", {
400
+ exitCode: result.exitCode,
401
+ timedOut: result.timedOut,
402
+ blocked: result.blocked
403
+ });
404
+ if (context.onFinish) await context.onFinish("bash_execute", {
405
+ command: params.command,
406
+ exitCode: result.exitCode,
407
+ blocked: result.blocked
408
+ });
409
+ return formattedResult;
410
+ } catch (error) {
411
+ context.logger.error("Bash: Command failed", error);
412
+ if (context.onFinish) await context.onFinish("bash_execute", {
413
+ command: params.command,
414
+ error: error instanceof Error ? error.message : "Unknown error"
415
+ });
416
+ throw error;
417
+ }
418
+ },
419
+ toolSchema: {
420
+ name: "bash_execute",
421
+ description: `Execute a bash command in the terminal. Use this for running shell commands, scripts, build tools, git operations, and other CLI tasks.
422
+
423
+ SAFETY NOTES:
424
+ - Commands are executed in the current working directory (or specified cwd)
425
+ - Dangerous commands (sudo, rm -rf /, etc.) are automatically blocked
426
+ - Commands have a default timeout of 60 seconds (max 5 minutes)
427
+ - Output is limited to prevent overwhelming responses
428
+ - This tool ALWAYS requires user permission before execution
429
+
430
+ COMMON USE CASES:
431
+ - Running build commands: npm run build, make, cargo build
432
+ - Git operations: git status, git log, git diff
433
+ - Viewing system info: ls, pwd, cat, head, tail
434
+ - Running tests: npm test, pytest, cargo test
435
+ - Package management: npm install, pip install
436
+ - File operations: mkdir, cp, mv (with permission)
437
+
438
+ BLOCKED OPERATIONS:
439
+ - sudo and privilege escalation
440
+ - Recursive deletes on system paths
441
+ - Direct disk operations
442
+ - Fork bombs and resource exhaustion
443
+ - Piping remote scripts to shell`,
444
+ parameters: {
445
+ type: "object",
446
+ properties: {
447
+ command: {
448
+ type: "string",
449
+ description: "The bash command to execute. Can include pipes, redirects, and chained commands."
450
+ },
451
+ cwd: {
452
+ type: "string",
453
+ description: "Working directory for the command (optional). Can be relative or absolute path. Defaults to current directory."
454
+ },
455
+ timeout: {
456
+ type: "number",
457
+ description: "Timeout in milliseconds (optional, default: 60000, max: 300000). Command will be terminated if it exceeds this time."
458
+ }
459
+ },
460
+ required: ["command"]
461
+ }
462
+ }
463
+ })
464
+ };
465
+ //#endregion
466
+ export { bashExecuteTool };
@@ -0,0 +1,101 @@
1
+ #!/usr/bin/env node
2
+ import { i as version, n as fetchLatestVersion, r as forceCheckForUpdate } from "../updateChecker-Cu9dkHxV.mjs";
3
+ import { execSync } from "child_process";
4
+ import { constants, existsSync, promises } from "fs";
5
+ import { homedir } from "os";
6
+ import path from "path";
7
+ //#region src/commands/doctorCommand.ts
8
+ /**
9
+ * External doctor command (b4m doctor)
10
+ * Runs diagnostic checks on the CLI installation.
11
+ * Runs outside the interactive CLI session.
12
+ */
13
+ async function handleDoctorCommand() {
14
+ console.log("B4M CLI Doctor\n");
15
+ console.log("Running diagnostics...\n");
16
+ const results = [];
17
+ const nodeVersion = process.version;
18
+ if (parseInt(nodeVersion.slice(1).split(".")[0], 10) >= 18) results.push({
19
+ name: "Node.js version",
20
+ status: "pass",
21
+ message: `${nodeVersion} (>= 18 required)`
22
+ });
23
+ else results.push({
24
+ name: "Node.js version",
25
+ status: "fail",
26
+ message: `${nodeVersion} (>= 18 required, please upgrade)`
27
+ });
28
+ const latestVersion = await fetchLatestVersion();
29
+ if (latestVersion) results.push({
30
+ name: "NPM registry",
31
+ status: "pass",
32
+ message: `Accessible (latest: v${latestVersion})`
33
+ });
34
+ else results.push({
35
+ name: "NPM registry",
36
+ status: "fail",
37
+ message: "Not accessible — check your internet connection"
38
+ });
39
+ const currentVersion = version;
40
+ const updateResult = await forceCheckForUpdate(currentVersion);
41
+ if (updateResult) if (updateResult.updateAvailable) results.push({
42
+ name: "Version",
43
+ status: "warn",
44
+ message: `v${currentVersion} installed, v${updateResult.latestVersion} available. Run: b4m update`
45
+ });
46
+ else results.push({
47
+ name: "Version",
48
+ status: "pass",
49
+ message: `v${currentVersion} (latest)`
50
+ });
51
+ try {
52
+ const npmPrefix = execSync("npm config get prefix", {
53
+ encoding: "utf-8",
54
+ timeout: 1e4
55
+ }).trim();
56
+ try {
57
+ await promises.access(npmPrefix, constants.W_OK);
58
+ results.push({
59
+ name: "Global npm path",
60
+ status: "pass",
61
+ message: `${npmPrefix} (writable)`
62
+ });
63
+ } catch {
64
+ results.push({
65
+ name: "Global npm path",
66
+ status: "warn",
67
+ message: `${npmPrefix} (not writable — may need sudo for updates)`
68
+ });
69
+ }
70
+ } catch {
71
+ results.push({
72
+ name: "Global npm path",
73
+ status: "warn",
74
+ message: "Could not determine npm prefix"
75
+ });
76
+ }
77
+ const configFile = path.join(homedir(), ".bike4mind", "config.json");
78
+ if (existsSync(configFile)) results.push({
79
+ name: "Config file",
80
+ status: "pass",
81
+ message: configFile
82
+ });
83
+ else results.push({
84
+ name: "Config file",
85
+ status: "warn",
86
+ message: `Not found at ${configFile}`
87
+ });
88
+ console.log("Results:\n");
89
+ for (const result of results) {
90
+ const icon = result.status === "pass" ? "\x1B[32m✓\x1B[0m" : result.status === "warn" ? "\x1B[33m!\x1B[0m" : "\x1B[31m✗\x1B[0m";
91
+ console.log(` ${icon} ${result.name}: ${result.message}`);
92
+ }
93
+ const failures = results.filter((r) => r.status === "fail");
94
+ const warnings = results.filter((r) => r.status === "warn");
95
+ console.log("");
96
+ if (failures.length > 0) console.log(`${failures.length} issue(s) found.`);
97
+ else if (warnings.length > 0) console.log(`All checks passed with ${warnings.length} warning(s).`);
98
+ else console.log("All checks passed.");
99
+ }
100
+ //#endregion
101
+ export { handleDoctorCommand };