yolobox 0.1.4 → 0.1.6

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 (2) hide show
  1. package/dist/index.js +318 -54
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -7,12 +7,74 @@ import { defineCommand } from "citty";
7
7
  // src/lib/docker.ts
8
8
  import { execSync, spawn, spawnSync } from "child_process";
9
9
  import { existsSync } from "fs";
10
+ import { join as join2 } from "path";
11
+
12
+ // src/lib/debug.ts
13
+ import { appendFileSync, writeFileSync } from "fs";
10
14
  import { join } from "path";
15
+ import pc from "picocolors";
16
+ var _enabled = false;
17
+ var _logPath = null;
18
+ function enable() {
19
+ _enabled = true;
20
+ _logPath = join(process.cwd(), "yolobox-debug.log");
21
+ writeFileSync(
22
+ _logPath,
23
+ `yolobox debug log \u2014 ${(/* @__PURE__ */ new Date()).toISOString()}
24
+ ${"=".repeat(60)}
25
+
26
+ `
27
+ );
28
+ return _logPath;
29
+ }
30
+ function isEnabled() {
31
+ return _enabled;
32
+ }
33
+ function getLogPath() {
34
+ return _logPath;
35
+ }
36
+ function timestamp() {
37
+ return (/* @__PURE__ */ new Date()).toTimeString().slice(0, 8);
38
+ }
39
+ function log(message) {
40
+ if (!_enabled || !_logPath) return;
41
+ appendFileSync(_logPath, `[${timestamp()}] ${message}
42
+ `);
43
+ }
44
+ function error(message) {
45
+ if (!_enabled || !_logPath) return;
46
+ const ts = timestamp();
47
+ appendFileSync(_logPath, `[${ts}] ERROR: ${message}
48
+ `);
49
+ process.stderr.write(`${pc.dim(`[${ts}]`)} ${pc.red(message)}
50
+ `);
51
+ }
52
+ function logCommand(cmd, args, result) {
53
+ if (!_enabled || !_logPath) return;
54
+ const lines = [`$ ${cmd} ${args.join(" ")}`, `exit code: ${result.status}`];
55
+ if (result.stdout.trim()) lines.push(`stdout:
56
+ ${result.stdout.trim()}`);
57
+ if (result.stderr.trim()) lines.push(`stderr:
58
+ ${result.stderr.trim()}`);
59
+ log(lines.join("\n"));
60
+ if (result.status !== 0) {
61
+ const errorOutput = result.stderr.trim() || result.stdout.trim();
62
+ if (errorOutput) {
63
+ process.stderr.write(`${pc.dim("[debug]")} ${pc.red(errorOutput)}
64
+ `);
65
+ }
66
+ }
67
+ }
68
+
69
+ // src/lib/docker.ts
11
70
  function isDockerRunning() {
71
+ log("Checking Docker status...");
12
72
  try {
13
73
  execSync("docker info", { stdio: ["pipe", "pipe", "pipe"] });
74
+ log("Docker is running");
14
75
  return true;
15
76
  } catch {
77
+ error("Docker is not running or not installed");
16
78
  return false;
17
79
  }
18
80
  }
@@ -35,12 +97,15 @@ function resolveDockerImage(options) {
35
97
  };
36
98
  }
37
99
  function imageExists(imageName) {
100
+ log(`Checking if image exists: ${imageName}`);
38
101
  try {
39
102
  execSync(`docker image inspect ${imageName}`, {
40
103
  stdio: ["pipe", "pipe", "pipe"]
41
104
  });
105
+ log(`Image found: ${imageName}`);
42
106
  return true;
43
107
  } catch {
108
+ log(`Image not found locally: ${imageName}`);
44
109
  return false;
45
110
  }
46
111
  }
@@ -79,12 +144,42 @@ function pullImage(imageName, onProgress) {
79
144
  proc.stdout?.on("data", handleOutput);
80
145
  proc.stderr?.on("data", handleOutput);
81
146
  proc.on("close", (code) => {
147
+ log(
148
+ `Image pull ${code === 0 ? "succeeded" : "failed"}: ${imageName}`
149
+ );
82
150
  resolve(code === 0);
83
151
  });
84
152
  });
85
153
  }
86
154
  function isYoloboxDevRepo(repoRoot) {
87
- return existsSync(join(repoRoot, "docker", "Dockerfile"));
155
+ return existsSync(join2(repoRoot, "docker", "Dockerfile"));
156
+ }
157
+ function canDockerAccessPath(hostFilePath, image) {
158
+ log(`Checking Docker file access for: ${hostFilePath}`);
159
+ try {
160
+ const result = spawnSync(
161
+ "docker",
162
+ [
163
+ "run",
164
+ "--rm",
165
+ "-v",
166
+ `${hostFilePath}:/test-file:ro`,
167
+ image,
168
+ "cat",
169
+ "/test-file"
170
+ ],
171
+ { stdio: ["pipe", "pipe", "pipe"], timeout: 1e4 }
172
+ );
173
+ const ok = result.status === 0;
174
+ if (!ok) {
175
+ log(
176
+ `Docker cannot access path: ${result.stderr?.toString().trim()}`
177
+ );
178
+ }
179
+ return ok;
180
+ } catch {
181
+ return false;
182
+ }
88
183
  }
89
184
  function buildDockerArgs(opts) {
90
185
  const args = [
@@ -123,20 +218,46 @@ function buildExecArgs(id, command) {
123
218
  }
124
219
  function startContainer(opts) {
125
220
  const args = buildDockerArgs(opts);
221
+ log(`Starting container yolobox-${opts.id}`);
126
222
  const result = spawnSync("docker", args, { stdio: ["pipe", "pipe", "pipe"] });
223
+ const stdout = result.stdout?.toString() || "";
224
+ const stderr = result.stderr?.toString() || "";
225
+ logCommand("docker", args, {
226
+ status: result.status,
227
+ stdout,
228
+ stderr
229
+ });
127
230
  return result.status === 0;
128
231
  }
129
232
  function restartContainer(id) {
130
- const result = spawnSync("docker", ["start", `yolobox-${id}`], {
233
+ log(`Restarting container yolobox-${id}`);
234
+ const args = ["start", `yolobox-${id}`];
235
+ const result = spawnSync("docker", args, {
131
236
  stdio: ["pipe", "pipe", "pipe"]
132
237
  });
238
+ const stdout = result.stdout?.toString() || "";
239
+ const stderr = result.stderr?.toString() || "";
240
+ logCommand("docker", args, { status: result.status, stdout, stderr });
133
241
  return result.status === 0;
134
242
  }
135
243
  function execInContainer(id, command) {
136
244
  const args = buildExecArgs(id, command);
245
+ log(`Exec in container: docker ${args.join(" ")}`);
137
246
  const result = spawnSync("docker", args, { stdio: "inherit" });
247
+ log(`Exec exited with code ${result.status}`);
138
248
  return result.status ?? 1;
139
249
  }
250
+ function execInContainerNonInteractive(id, command) {
251
+ const args = ["exec", `yolobox-${id}`, ...command];
252
+ log(`Exec (non-interactive) in container: docker ${args.join(" ")}`);
253
+ const result = spawnSync("docker", args, { stdio: ["pipe", "pipe", "pipe"] });
254
+ logCommand("docker", args, {
255
+ status: result.status,
256
+ stdout: result.stdout?.toString() || "",
257
+ stderr: result.stderr?.toString() || ""
258
+ });
259
+ return result.status === 0;
260
+ }
140
261
  function timeAgo(dateStr) {
141
262
  const date = new Date(dateStr);
142
263
  const seconds = Math.floor((Date.now() - date.getTime()) / 1e3);
@@ -150,7 +271,7 @@ function timeAgo(dateStr) {
150
271
  }
151
272
  function getWorktreeBranch(repoPath, id) {
152
273
  try {
153
- const worktreePath = join(repoPath, ".yolobox", id);
274
+ const worktreePath = join2(repoPath, ".yolobox", id);
154
275
  return execSync(`git -C "${worktreePath}" rev-parse --abbrev-ref HEAD`, {
155
276
  encoding: "utf-8"
156
277
  }).trim();
@@ -180,30 +301,43 @@ function listContainers() {
180
301
  }
181
302
  }
182
303
  function killContainer(id) {
183
- const stop = spawnSync("docker", ["stop", `yolobox-${id}`], {
304
+ log(`Killing container yolobox-${id}`);
305
+ const stopArgs = ["stop", `yolobox-${id}`];
306
+ const stop = spawnSync("docker", stopArgs, {
184
307
  stdio: ["pipe", "pipe", "pipe"]
185
308
  });
309
+ logCommand("docker", stopArgs, {
310
+ status: stop.status,
311
+ stdout: stop.stdout?.toString() || "",
312
+ stderr: stop.stderr?.toString() || ""
313
+ });
186
314
  if (stop.status !== 0) return false;
187
- const rm = spawnSync("docker", ["rm", `yolobox-${id}`], {
315
+ const rmArgs = ["rm", `yolobox-${id}`];
316
+ const rm = spawnSync("docker", rmArgs, {
188
317
  stdio: ["pipe", "pipe", "pipe"]
189
318
  });
319
+ logCommand("docker", rmArgs, {
320
+ status: rm.status,
321
+ stdout: rm.stdout?.toString() || "",
322
+ stderr: rm.stderr?.toString() || ""
323
+ });
190
324
  return rm.status === 0;
191
325
  }
192
326
 
193
327
  // src/lib/ui.ts
194
328
  import * as p from "@clack/prompts";
195
- import pc from "picocolors";
329
+ import pc2 from "picocolors";
196
330
  function intro2() {
197
- p.intro(pc.bgCyan(pc.black(` yolobox v${"0.1.4"} `)));
331
+ p.intro(pc2.bgCyan(pc2.black(` yolobox v${"0.1.6"} `)));
198
332
  }
199
333
  function success(message) {
200
334
  p.log.success(message);
201
335
  }
202
- function error(message) {
203
- p.log.error(pc.red(message));
336
+ function error2(message) {
337
+ p.log.error(pc2.red(message));
204
338
  }
205
339
  function warn(message) {
206
- p.log.warn(pc.yellow(message));
340
+ p.log.warn(pc2.yellow(message));
207
341
  }
208
342
  function info(message) {
209
343
  p.log.info(message);
@@ -227,14 +361,14 @@ var attach_default = defineCommand({
227
361
  },
228
362
  run: async ({ args }) => {
229
363
  if (!isDockerRunning()) {
230
- error("Docker is not running.");
364
+ error2("Docker is not running.");
231
365
  return process.exit(1);
232
366
  }
233
367
  let id = args.id;
234
368
  if (!id) {
235
369
  const containers = listContainers().filter((c) => c.status === "running");
236
370
  if (containers.length === 0) {
237
- error("No running yolobox containers found.");
371
+ error2("No running yolobox containers found.");
238
372
  return process.exit(1);
239
373
  }
240
374
  if (containers.length === 1) {
@@ -255,13 +389,13 @@ var attach_default = defineCommand({
255
389
  const containers = listContainers();
256
390
  const match = containers.find((c) => c.id === id);
257
391
  if (!match) {
258
- error(`No yolobox container found with ID "${id}".`);
392
+ error2(`No yolobox container found with ID "${id}".`);
259
393
  return process.exit(1);
260
394
  }
261
395
  if (match.status !== "running") {
262
396
  info(`Restarting stopped container "${id}"...`);
263
397
  if (!restartContainer(id)) {
264
- error(`Failed to restart container "${id}".`);
398
+ error2(`Failed to restart container "${id}".`);
265
399
  return process.exit(1);
266
400
  }
267
401
  }
@@ -281,17 +415,17 @@ import {
281
415
  mkdirSync,
282
416
  readFileSync,
283
417
  unlinkSync,
284
- writeFileSync
418
+ writeFileSync as writeFileSync2
285
419
  } from "fs";
286
420
  import { homedir } from "os";
287
- import { join as join2 } from "path";
421
+ import { join as join3 } from "path";
288
422
  var AUTH_DIR_NAME = ".yolobox";
289
423
  var AUTH_FILE_NAME = "auth.json";
290
424
  function getAuthDir() {
291
- return join2(homedir(), AUTH_DIR_NAME);
425
+ return join3(homedir(), AUTH_DIR_NAME);
292
426
  }
293
427
  function getAuthFilePath() {
294
- return join2(getAuthDir(), AUTH_FILE_NAME);
428
+ return join3(getAuthDir(), AUTH_FILE_NAME);
295
429
  }
296
430
  function saveToken(token) {
297
431
  const dir = getAuthDir();
@@ -299,7 +433,7 @@ function saveToken(token) {
299
433
  mkdirSync(dir, { mode: 448, recursive: true });
300
434
  }
301
435
  const data = JSON.stringify({ claudeOauthToken: token }, null, 2);
302
- writeFileSync(getAuthFilePath(), `${data}
436
+ writeFileSync2(getAuthFilePath(), `${data}
303
437
  `, { mode: 384 });
304
438
  }
305
439
  function loadToken() {
@@ -380,7 +514,7 @@ var auth_default = defineCommand2({
380
514
  if (args.token) {
381
515
  const token = args.token;
382
516
  if (!isValidToken(token)) {
383
- error(
517
+ error2(
384
518
  'Invalid token. Expected a token starting with "sk-ant-".\nRun `claude setup-token` to generate a valid token.'
385
519
  );
386
520
  process.exit(1);
@@ -393,7 +527,7 @@ var auth_default = defineCommand2({
393
527
  const envToken = process.env.CLAUDE_CODE_OAUTH_TOKEN;
394
528
  if (envToken) {
395
529
  if (!isValidToken(envToken)) {
396
- error(
530
+ error2(
397
531
  "CLAUDE_CODE_OAUTH_TOKEN is set but does not look like a valid token."
398
532
  );
399
533
  process.exit(1);
@@ -446,10 +580,11 @@ function showStatus() {
446
580
  import { defineCommand as defineCommand3 } from "citty";
447
581
 
448
582
  // src/lib/container-setup.ts
583
+ import fs2 from "fs";
449
584
  import path2 from "path";
450
585
 
451
586
  // src/lib/git.ts
452
- import { execSync as execSync2 } from "child_process";
587
+ import { execSync as execSync2, spawnSync as spawnSync2 } from "child_process";
453
588
  function exec(cmd) {
454
589
  return execSync2(cmd, {
455
590
  encoding: "utf-8",
@@ -468,7 +603,7 @@ function getRepoRoot() {
468
603
  return exec("git rev-parse --show-toplevel");
469
604
  }
470
605
  function getGitDir() {
471
- return exec("git rev-parse --git-dir");
606
+ return exec("git rev-parse --absolute-git-dir");
472
607
  }
473
608
  function getBranches() {
474
609
  const output = exec('git branch --list --format="%(refname:short)"');
@@ -514,6 +649,28 @@ function getGitIdentity() {
514
649
  return { name: "", email: "" };
515
650
  }
516
651
  }
652
+ function listUntrackedFiles(repoRoot, pathspec) {
653
+ const result = spawnSync2(
654
+ "git",
655
+ [
656
+ "-C",
657
+ repoRoot,
658
+ "ls-files",
659
+ "--others",
660
+ "--exclude-standard",
661
+ "--",
662
+ pathspec
663
+ ],
664
+ {
665
+ encoding: "utf-8",
666
+ stdio: ["pipe", "pipe", "pipe"]
667
+ }
668
+ );
669
+ if (result.status !== 0) return [];
670
+ const output = result.stdout?.trim() ?? "";
671
+ if (!output) return [];
672
+ return output.split("\n").filter(Boolean);
673
+ }
517
674
 
518
675
  // src/lib/id.ts
519
676
  var ADJECTIVES = [
@@ -1285,10 +1442,51 @@ function getExistingWorktreeIds(repoRoot) {
1285
1442
  }
1286
1443
 
1287
1444
  // src/lib/container-setup.ts
1445
+ function copyUntrackedFilesFromCwd(options) {
1446
+ const { repoRoot, worktreePath, cwd } = options;
1447
+ const relativeCwd = path2.relative(repoRoot, cwd);
1448
+ if (relativeCwd.startsWith("..") || path2.isAbsolute(relativeCwd)) {
1449
+ log(`Skipping untracked copy: cwd outside repo (${cwd})`);
1450
+ return 0;
1451
+ }
1452
+ if (relativeCwd.split(path2.sep).includes(".yolobox")) {
1453
+ log(`Skipping untracked copy: cwd inside .yolobox (${cwd})`);
1454
+ return 0;
1455
+ }
1456
+ const pathspec = relativeCwd === "" ? "." : relativeCwd;
1457
+ const files = listUntrackedFiles(repoRoot, pathspec);
1458
+ if (files.length === 0) return 0;
1459
+ let copied = 0;
1460
+ for (const file of files) {
1461
+ const src = path2.join(repoRoot, file);
1462
+ const dest = path2.join(worktreePath, file);
1463
+ try {
1464
+ if (fs2.existsSync(dest)) continue;
1465
+ fs2.mkdirSync(path2.dirname(dest), { recursive: true });
1466
+ const stat = fs2.lstatSync(src);
1467
+ if (stat.isSymbolicLink()) {
1468
+ const target = fs2.readlinkSync(src);
1469
+ fs2.symlinkSync(target, dest);
1470
+ } else if (stat.isDirectory()) {
1471
+ fs2.mkdirSync(dest, { recursive: true });
1472
+ } else {
1473
+ fs2.copyFileSync(src, dest);
1474
+ }
1475
+ copied++;
1476
+ } catch (err) {
1477
+ const message = err instanceof Error ? err.message : String(err);
1478
+ log(`Failed to copy untracked file "${file}": ${message}`);
1479
+ }
1480
+ }
1481
+ return copied;
1482
+ }
1288
1483
  async function setupContainer(options = {}) {
1289
1484
  intro2();
1485
+ if (isEnabled()) {
1486
+ info(`Debug log: ${getLogPath()}`);
1487
+ }
1290
1488
  if (!isDockerRunning()) {
1291
- error("Docker is not running. Start Docker and try again.");
1489
+ error2("Docker is not running. Start Docker and try again.");
1292
1490
  process.exit(1);
1293
1491
  }
1294
1492
  if (!isInsideGitRepo()) {
@@ -1296,7 +1494,7 @@ async function setupContainer(options = {}) {
1296
1494
  message: "No git repo found. Initialize one here?"
1297
1495
  });
1298
1496
  if (p.isCancel(shouldInit) || !shouldInit) {
1299
- error("yolobox needs a git repo for worktrees.");
1497
+ error2("yolobox needs a git repo for worktrees.");
1300
1498
  process.exit(1);
1301
1499
  }
1302
1500
  initRepo();
@@ -1308,6 +1506,8 @@ async function setupContainer(options = {}) {
1308
1506
  }
1309
1507
  const repoRoot = getRepoRoot();
1310
1508
  const gitDir = getGitDir();
1509
+ log(`Repo root: ${repoRoot}`);
1510
+ log(`Git dir: ${gitDir}`);
1311
1511
  let id;
1312
1512
  if (options.name) {
1313
1513
  id = options.name;
@@ -1321,7 +1521,7 @@ async function setupContainer(options = {}) {
1321
1521
  const containers = listContainers();
1322
1522
  const existing = containers.find((c) => c.id === id);
1323
1523
  if (existing && existing.status === "running") {
1324
- error(
1524
+ error2(
1325
1525
  `Container "${id}" is already running. Use "yolobox attach ${id}" to connect.`
1326
1526
  );
1327
1527
  process.exit(1);
@@ -1343,10 +1543,26 @@ async function setupContainer(options = {}) {
1343
1543
  success(`Created worktree .yolobox/${id} (branch: yolo/${id})`);
1344
1544
  }
1345
1545
  ensureGitignore(repoRoot);
1546
+ if (!worktreeAlreadyExists && !branchAlreadyExists) {
1547
+ const copied = copyUntrackedFilesFromCwd({
1548
+ repoRoot,
1549
+ worktreePath,
1550
+ cwd: process.cwd()
1551
+ });
1552
+ if (copied > 0) {
1553
+ const cwdRel = path2.relative(repoRoot, process.cwd()) || ".";
1554
+ const label = copied === 1 ? "file" : "files";
1555
+ info(`Copied ${copied} untracked ${label} from ${cwdRel}`);
1556
+ }
1557
+ }
1346
1558
  const gitIdentity = getGitIdentity();
1559
+ log("Resolving Docker image...");
1347
1560
  const imageResolution = resolveDockerImage({
1348
1561
  envImage: process.env.YOLOBOX_IMAGE
1349
1562
  });
1563
+ log(
1564
+ `Resolved image: ${imageResolution.image} (source: ${imageResolution.source})`
1565
+ );
1350
1566
  if (imageResolution.source === "env") {
1351
1567
  info(`Using custom Docker image: ${imageResolution.image}`);
1352
1568
  } else if (imageResolution.source === "local") {
@@ -1366,17 +1582,35 @@ async function setupContainer(options = {}) {
1366
1582
  );
1367
1583
  if (!pulled) {
1368
1584
  spinner.stop("Failed to pull image", 1);
1369
- error("Failed to pull Docker image.");
1585
+ error2("Failed to pull Docker image.");
1370
1586
  process.exit(1);
1371
1587
  }
1372
1588
  spinner.stop("Docker image pulled");
1373
1589
  }
1590
+ const testFile = path2.join(gitDir, "HEAD");
1591
+ if (!canDockerAccessPath(testFile, imageResolution.image)) {
1592
+ error2("Docker cannot access files in this directory.");
1593
+ if (process.platform === "darwin" && repoRoot.includes("/Library/Mobile Documents/")) {
1594
+ error2(
1595
+ 'This repo is in iCloud Drive. Docker Desktop needs "Full Disk Access" to read these files.\n \u2192 Open System Settings > Privacy & Security > Full Disk Access\n \u2192 Enable Docker Desktop, then restart Docker.'
1596
+ );
1597
+ } else if (process.platform === "darwin") {
1598
+ error2(
1599
+ 'Docker Desktop may need "Full Disk Access" to read files in this location.\n \u2192 Open System Settings > Privacy & Security > Full Disk Access\n \u2192 Enable Docker Desktop, then restart Docker.'
1600
+ );
1601
+ }
1602
+ process.exit(1);
1603
+ }
1374
1604
  const claudeOauthToken = resolveToken();
1375
1605
  if (claudeOauthToken) {
1376
1606
  success("Claude auth token configured");
1377
1607
  } else {
1378
1608
  warn('No Claude auth token. Run "yolobox auth" to set up.');
1379
1609
  }
1610
+ log(`Starting container with id=${id}, image=${imageResolution.image}`);
1611
+ log(`Worktree path: ${worktreePath}`);
1612
+ log(`Git identity: ${gitIdentity.name} <${gitIdentity.email}>`);
1613
+ log(`Claude token: ${claudeOauthToken ? "set" : "not set"}`);
1380
1614
  const started = startContainer({
1381
1615
  id,
1382
1616
  worktreePath,
@@ -1387,9 +1621,26 @@ async function setupContainer(options = {}) {
1387
1621
  claudeOauthToken: claudeOauthToken ?? void 0
1388
1622
  });
1389
1623
  if (!started) {
1390
- error("Failed to start container.");
1624
+ if (isEnabled()) {
1625
+ error2(
1626
+ `Failed to start container. Check ${getLogPath()} for details.`
1627
+ );
1628
+ } else {
1629
+ error2("Failed to start container. Run with --debug for details.");
1630
+ }
1391
1631
  process.exit(1);
1392
1632
  }
1633
+ const safeDirOk = execInContainerNonInteractive(id, [
1634
+ "git",
1635
+ "config",
1636
+ "--global",
1637
+ "--add",
1638
+ "safe.directory",
1639
+ "/workspace"
1640
+ ]);
1641
+ if (!safeDirOk) {
1642
+ log("Failed to mark /workspace as a safe git directory");
1643
+ }
1393
1644
  return { id, repoRoot };
1394
1645
  }
1395
1646
 
@@ -1422,7 +1673,7 @@ var claude_default = defineCommand3({
1422
1673
  });
1423
1674
 
1424
1675
  // src/commands/help.ts
1425
- import { spawnSync as spawnSync2 } from "child_process";
1676
+ import { spawnSync as spawnSync3 } from "child_process";
1426
1677
  import { defineCommand as defineCommand4 } from "citty";
1427
1678
  var help_default = defineCommand4({
1428
1679
  meta: {
@@ -1430,7 +1681,7 @@ var help_default = defineCommand4({
1430
1681
  description: "Show help information"
1431
1682
  },
1432
1683
  run: async () => {
1433
- const result = spawnSync2(process.argv[0], [process.argv[1], "--help"], {
1684
+ const result = spawnSync3(process.argv[0], [process.argv[1], "--help"], {
1434
1685
  stdio: "inherit"
1435
1686
  });
1436
1687
  process.exit(result.status || 0);
@@ -1453,14 +1704,14 @@ var kill_default = defineCommand5({
1453
1704
  },
1454
1705
  run: async ({ args }) => {
1455
1706
  if (!isDockerRunning()) {
1456
- error("Docker is not running.");
1707
+ error2("Docker is not running.");
1457
1708
  return process.exit(1);
1458
1709
  }
1459
1710
  let id = args.id;
1460
1711
  if (!id) {
1461
1712
  const containers = listContainers();
1462
1713
  if (containers.length === 0) {
1463
- error("No yolobox containers found.");
1714
+ error2("No yolobox containers found.");
1464
1715
  return process.exit(1);
1465
1716
  }
1466
1717
  const selected = await p.select({
@@ -1486,13 +1737,13 @@ var kill_default = defineCommand5({
1486
1737
  const containers = listContainers();
1487
1738
  const match = containers.find((c) => c.id === id);
1488
1739
  if (!match) {
1489
- error(`No yolobox container found with ID "${id}".`);
1740
+ error2(`No yolobox container found with ID "${id}".`);
1490
1741
  return process.exit(1);
1491
1742
  }
1492
1743
  }
1493
1744
  const killed = killContainer(id);
1494
1745
  if (!killed) {
1495
- error(`Failed to kill yolobox-${id}. Is it running?`);
1746
+ error2(`Failed to kill yolobox-${id}. Is it running?`);
1496
1747
  process.exit(1);
1497
1748
  }
1498
1749
  success(`Killed yolobox-${id}`);
@@ -1522,7 +1773,7 @@ var ls_default = defineCommand6({
1522
1773
  },
1523
1774
  run: async () => {
1524
1775
  if (!isDockerRunning()) {
1525
- error("Docker is not running.");
1776
+ error2("Docker is not running.");
1526
1777
  process.exit(1);
1527
1778
  }
1528
1779
  const containers = listContainers();
@@ -1536,12 +1787,12 @@ var ls_default = defineCommand6({
1536
1787
  const statusW = Math.max(8, ...containers.map((c) => c.status.length)) + 2;
1537
1788
  const createdW = Math.max(9, ...containers.map((c) => c.created.length)) + 2;
1538
1789
  const header = `${"ID".padEnd(idW)}${"BRANCH".padEnd(branchW)}${"STATUS".padEnd(statusW)}${"CREATED".padEnd(createdW)}PATH`;
1539
- console.log(pc.dim(header));
1790
+ console.log(pc2.dim(header));
1540
1791
  for (let i = 0; i < containers.length; i++) {
1541
1792
  const c = containers[i];
1542
- const statusColor = c.status === "running" ? pc.green : pc.yellow;
1793
+ const statusColor = c.status === "running" ? pc2.green : pc2.yellow;
1543
1794
  console.log(
1544
- `${c.id.padEnd(idW)}${c.branch.padEnd(branchW)}${statusColor(c.status.padEnd(statusW))}${pc.dim(c.created.padEnd(createdW))}${pc.dim(paths[i])}`
1795
+ `${c.id.padEnd(idW)}${c.branch.padEnd(branchW)}${statusColor(c.status.padEnd(statusW))}${pc2.dim(c.created.padEnd(createdW))}${pc2.dim(paths[i])}`
1545
1796
  );
1546
1797
  }
1547
1798
  }
@@ -1604,7 +1855,7 @@ var nuke_default = defineCommand7({
1604
1855
  const targets = [...targetMap.values()];
1605
1856
  if (targets.length === 0) {
1606
1857
  if (!dockerRunning) {
1607
- error(
1858
+ error2(
1608
1859
  "Docker is not running and no local branches or worktrees found."
1609
1860
  );
1610
1861
  } else {
@@ -1622,18 +1873,18 @@ Found ${targets.length} yolobox resource${targets.length === 1 ? "" : "s"}:
1622
1873
  for (const target of targets) {
1623
1874
  const parts = [];
1624
1875
  if (target.container) {
1625
- const statusColor = target.container.status === "running" ? pc.green : pc.dim;
1876
+ const statusColor = target.container.status === "running" ? pc2.green : pc2.dim;
1626
1877
  parts.push(`container ${statusColor(target.container.status)}`);
1627
1878
  }
1628
1879
  if (target.hasBranch) {
1629
- parts.push(`branch ${pc.cyan(`yolo/${target.id}`)}`);
1880
+ parts.push(`branch ${pc2.cyan(`yolo/${target.id}`)}`);
1630
1881
  }
1631
1882
  if (target.hasWorktree) {
1632
1883
  parts.push("worktree");
1633
1884
  }
1634
- const pathSuffix = args.all && target.container ? ` ${pc.dim(target.container.path)}` : "";
1885
+ const pathSuffix = args.all && target.container ? ` ${pc2.dim(target.container.path)}` : "";
1635
1886
  console.log(
1636
- ` ${pc.bold(target.id)} ${parts.join(pc.dim(" \xB7 "))}${pathSuffix}`
1887
+ ` ${pc2.bold(target.id)} ${parts.join(pc2.dim(" \xB7 "))}${pathSuffix}`
1637
1888
  );
1638
1889
  }
1639
1890
  console.log();
@@ -1676,10 +1927,10 @@ Found ${targets.length} yolobox resource${targets.length === 1 ? "" : "s"}:
1676
1927
  if (!target.container) continue;
1677
1928
  const success2 = killContainer(target.id);
1678
1929
  if (success2) {
1679
- success(`Killed container ${pc.bold(target.id)}`);
1930
+ success(`Killed container ${pc2.bold(target.id)}`);
1680
1931
  containersKilled++;
1681
1932
  } else {
1682
- error(`Failed to kill container ${target.id}`);
1933
+ error2(`Failed to kill container ${target.id}`);
1683
1934
  killFailures++;
1684
1935
  }
1685
1936
  }
@@ -1692,7 +1943,7 @@ Found ${targets.length} yolobox resource${targets.length === 1 ? "" : "s"}:
1692
1943
  const removed = removeWorktree(repoRoot, target.id);
1693
1944
  if (removed) {
1694
1945
  success(
1695
- `Removed worktree ${pc.bold(`.yolobox/${target.id}`)}`
1946
+ `Removed worktree ${pc2.bold(`.yolobox/${target.id}`)}`
1696
1947
  );
1697
1948
  worktreesRemoved++;
1698
1949
  } else {
@@ -1703,7 +1954,7 @@ Found ${targets.length} yolobox resource${targets.length === 1 ? "" : "s"}:
1703
1954
  const branchName = `yolo/${target.id}`;
1704
1955
  const deleted = deleteBranch(branchName);
1705
1956
  if (deleted) {
1706
- success(`Deleted branch ${pc.bold(branchName)}`);
1957
+ success(`Deleted branch ${pc2.bold(branchName)}`);
1707
1958
  branchesDeleted++;
1708
1959
  } else {
1709
1960
  warn(`Could not delete branch ${branchName}`);
@@ -1733,7 +1984,7 @@ Found ${targets.length} yolobox resource${targets.length === 1 ? "" : "s"}:
1733
1984
  summaryParts.push(
1734
1985
  `${killFailures} container${s(killFailures)} failed to stop`
1735
1986
  );
1736
- error(`${summaryParts.join(", ")}.`);
1987
+ error2(`${summaryParts.join(", ")}.`);
1737
1988
  process.exit(1);
1738
1989
  } else {
1739
1990
  const msg = summaryParts.join(", ");
@@ -1759,7 +2010,7 @@ var rm_default = defineCommand8({
1759
2010
  },
1760
2011
  run: async ({ args }) => {
1761
2012
  if (!isDockerRunning()) {
1762
- error("Docker is not running.");
2013
+ error2("Docker is not running.");
1763
2014
  return process.exit(1);
1764
2015
  }
1765
2016
  const repoRoot = getRepoRoot();
@@ -1767,7 +2018,7 @@ var rm_default = defineCommand8({
1767
2018
  if (!id) {
1768
2019
  const containers = listContainers();
1769
2020
  if (containers.length === 0) {
1770
- error("No yolobox containers found.");
2021
+ error2("No yolobox containers found.");
1771
2022
  return process.exit(1);
1772
2023
  }
1773
2024
  const selected = await p.select({
@@ -1794,7 +2045,7 @@ var rm_default = defineCommand8({
1794
2045
  const hasWorktree = getExistingWorktreeIds(repoRoot).includes(id);
1795
2046
  const hasBranch = getBranches().includes(`yolo/${id}`);
1796
2047
  if (!hasContainer && !hasWorktree && !hasBranch) {
1797
- error(`No yolobox found with ID "${id}".`);
2048
+ error2(`No yolobox found with ID "${id}".`);
1798
2049
  return process.exit(1);
1799
2050
  }
1800
2051
  }
@@ -1831,7 +2082,7 @@ var start_default = defineCommand9({
1831
2082
  run: async ({ args }) => {
1832
2083
  if (args.name) {
1833
2084
  if (!isDockerRunning()) {
1834
- error("Docker is not running.");
2085
+ error2("Docker is not running.");
1835
2086
  return process.exit(1);
1836
2087
  }
1837
2088
  const containers = listContainers();
@@ -1844,7 +2095,9 @@ var start_default = defineCommand9({
1844
2095
  }
1845
2096
  info(`Restarting stopped container "${args.name}"...`);
1846
2097
  if (!restartContainer(args.name)) {
1847
- error(`Failed to restart container "${args.name}".`);
2098
+ error2(
2099
+ `Failed to restart container "${args.name}". Run with --debug for details.`
2100
+ );
1848
2101
  return process.exit(1);
1849
2102
  }
1850
2103
  outro2(`Launching shell in ${args.name}...`);
@@ -1861,10 +2114,21 @@ var start_default = defineCommand9({
1861
2114
  });
1862
2115
 
1863
2116
  // src/index.ts
2117
+ var debugIndex = process.argv.indexOf("--debug");
2118
+ if (debugIndex !== -1) {
2119
+ process.argv.splice(debugIndex, 1);
2120
+ const logPath = enable();
2121
+ log(`yolobox v${"0.1.6"}`);
2122
+ log(`args: ${process.argv.slice(2).join(" ")}`);
2123
+ log(`cwd: ${process.cwd()}`);
2124
+ log(`node: ${process.version}`);
2125
+ log(`platform: ${process.platform} ${process.arch}`);
2126
+ log(`log file: ${logPath}`);
2127
+ }
1864
2128
  var main = defineCommand10({
1865
2129
  meta: {
1866
2130
  name: "yolobox",
1867
- version: "0.1.4",
2131
+ version: "0.1.6",
1868
2132
  description: "Run Claude Code in Docker containers. YOLO safely."
1869
2133
  },
1870
2134
  subCommands: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yolobox",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "Run Claude Code in a Docker container with --dangerously-skip-permissions. YOLO safely.",
5
5
  "type": "module",
6
6
  "bin": {