yolobox 0.1.3 → 0.1.5

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 +220 -51
  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,18 +218,33 @@ 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
  }
140
250
  function timeAgo(dateStr) {
@@ -150,7 +260,7 @@ function timeAgo(dateStr) {
150
260
  }
151
261
  function getWorktreeBranch(repoPath, id) {
152
262
  try {
153
- const worktreePath = join(repoPath, ".yolobox", id);
263
+ const worktreePath = join2(repoPath, ".yolobox", id);
154
264
  return execSync(`git -C "${worktreePath}" rev-parse --abbrev-ref HEAD`, {
155
265
  encoding: "utf-8"
156
266
  }).trim();
@@ -180,30 +290,43 @@ function listContainers() {
180
290
  }
181
291
  }
182
292
  function killContainer(id) {
183
- const stop = spawnSync("docker", ["stop", `yolobox-${id}`], {
293
+ log(`Killing container yolobox-${id}`);
294
+ const stopArgs = ["stop", `yolobox-${id}`];
295
+ const stop = spawnSync("docker", stopArgs, {
184
296
  stdio: ["pipe", "pipe", "pipe"]
185
297
  });
298
+ logCommand("docker", stopArgs, {
299
+ status: stop.status,
300
+ stdout: stop.stdout?.toString() || "",
301
+ stderr: stop.stderr?.toString() || ""
302
+ });
186
303
  if (stop.status !== 0) return false;
187
- const rm = spawnSync("docker", ["rm", `yolobox-${id}`], {
304
+ const rmArgs = ["rm", `yolobox-${id}`];
305
+ const rm = spawnSync("docker", rmArgs, {
188
306
  stdio: ["pipe", "pipe", "pipe"]
189
307
  });
308
+ logCommand("docker", rmArgs, {
309
+ status: rm.status,
310
+ stdout: rm.stdout?.toString() || "",
311
+ stderr: rm.stderr?.toString() || ""
312
+ });
190
313
  return rm.status === 0;
191
314
  }
192
315
 
193
316
  // src/lib/ui.ts
194
317
  import * as p from "@clack/prompts";
195
- import pc from "picocolors";
318
+ import pc2 from "picocolors";
196
319
  function intro2() {
197
- p.intro(pc.bgCyan(pc.black(` yolobox v${"0.1.3"} `)));
320
+ p.intro(pc2.bgCyan(pc2.black(` yolobox v${"0.1.5"} `)));
198
321
  }
199
322
  function success(message) {
200
323
  p.log.success(message);
201
324
  }
202
- function error(message) {
203
- p.log.error(pc.red(message));
325
+ function error2(message) {
326
+ p.log.error(pc2.red(message));
204
327
  }
205
328
  function warn(message) {
206
- p.log.warn(pc.yellow(message));
329
+ p.log.warn(pc2.yellow(message));
207
330
  }
208
331
  function info(message) {
209
332
  p.log.info(message);
@@ -227,14 +350,14 @@ var attach_default = defineCommand({
227
350
  },
228
351
  run: async ({ args }) => {
229
352
  if (!isDockerRunning()) {
230
- error("Docker is not running.");
353
+ error2("Docker is not running.");
231
354
  return process.exit(1);
232
355
  }
233
356
  let id = args.id;
234
357
  if (!id) {
235
358
  const containers = listContainers().filter((c) => c.status === "running");
236
359
  if (containers.length === 0) {
237
- error("No running yolobox containers found.");
360
+ error2("No running yolobox containers found.");
238
361
  return process.exit(1);
239
362
  }
240
363
  if (containers.length === 1) {
@@ -255,13 +378,13 @@ var attach_default = defineCommand({
255
378
  const containers = listContainers();
256
379
  const match = containers.find((c) => c.id === id);
257
380
  if (!match) {
258
- error(`No yolobox container found with ID "${id}".`);
381
+ error2(`No yolobox container found with ID "${id}".`);
259
382
  return process.exit(1);
260
383
  }
261
384
  if (match.status !== "running") {
262
385
  info(`Restarting stopped container "${id}"...`);
263
386
  if (!restartContainer(id)) {
264
- error(`Failed to restart container "${id}".`);
387
+ error2(`Failed to restart container "${id}".`);
265
388
  return process.exit(1);
266
389
  }
267
390
  }
@@ -281,17 +404,17 @@ import {
281
404
  mkdirSync,
282
405
  readFileSync,
283
406
  unlinkSync,
284
- writeFileSync
407
+ writeFileSync as writeFileSync2
285
408
  } from "fs";
286
409
  import { homedir } from "os";
287
- import { join as join2 } from "path";
410
+ import { join as join3 } from "path";
288
411
  var AUTH_DIR_NAME = ".yolobox";
289
412
  var AUTH_FILE_NAME = "auth.json";
290
413
  function getAuthDir() {
291
- return join2(homedir(), AUTH_DIR_NAME);
414
+ return join3(homedir(), AUTH_DIR_NAME);
292
415
  }
293
416
  function getAuthFilePath() {
294
- return join2(getAuthDir(), AUTH_FILE_NAME);
417
+ return join3(getAuthDir(), AUTH_FILE_NAME);
295
418
  }
296
419
  function saveToken(token) {
297
420
  const dir = getAuthDir();
@@ -299,7 +422,7 @@ function saveToken(token) {
299
422
  mkdirSync(dir, { mode: 448, recursive: true });
300
423
  }
301
424
  const data = JSON.stringify({ claudeOauthToken: token }, null, 2);
302
- writeFileSync(getAuthFilePath(), `${data}
425
+ writeFileSync2(getAuthFilePath(), `${data}
303
426
  `, { mode: 384 });
304
427
  }
305
428
  function loadToken() {
@@ -380,7 +503,7 @@ var auth_default = defineCommand2({
380
503
  if (args.token) {
381
504
  const token = args.token;
382
505
  if (!isValidToken(token)) {
383
- error(
506
+ error2(
384
507
  'Invalid token. Expected a token starting with "sk-ant-".\nRun `claude setup-token` to generate a valid token.'
385
508
  );
386
509
  process.exit(1);
@@ -393,7 +516,7 @@ var auth_default = defineCommand2({
393
516
  const envToken = process.env.CLAUDE_CODE_OAUTH_TOKEN;
394
517
  if (envToken) {
395
518
  if (!isValidToken(envToken)) {
396
- error(
519
+ error2(
397
520
  "CLAUDE_CODE_OAUTH_TOKEN is set but does not look like a valid token."
398
521
  );
399
522
  process.exit(1);
@@ -468,7 +591,7 @@ function getRepoRoot() {
468
591
  return exec("git rev-parse --show-toplevel");
469
592
  }
470
593
  function getGitDir() {
471
- return exec("git rev-parse --git-dir");
594
+ return exec("git rev-parse --absolute-git-dir");
472
595
  }
473
596
  function getBranches() {
474
597
  const output = exec('git branch --list --format="%(refname:short)"');
@@ -1287,8 +1410,11 @@ function getExistingWorktreeIds(repoRoot) {
1287
1410
  // src/lib/container-setup.ts
1288
1411
  async function setupContainer(options = {}) {
1289
1412
  intro2();
1413
+ if (isEnabled()) {
1414
+ info(`Debug log: ${getLogPath()}`);
1415
+ }
1290
1416
  if (!isDockerRunning()) {
1291
- error("Docker is not running. Start Docker and try again.");
1417
+ error2("Docker is not running. Start Docker and try again.");
1292
1418
  process.exit(1);
1293
1419
  }
1294
1420
  if (!isInsideGitRepo()) {
@@ -1296,7 +1422,7 @@ async function setupContainer(options = {}) {
1296
1422
  message: "No git repo found. Initialize one here?"
1297
1423
  });
1298
1424
  if (p.isCancel(shouldInit) || !shouldInit) {
1299
- error("yolobox needs a git repo for worktrees.");
1425
+ error2("yolobox needs a git repo for worktrees.");
1300
1426
  process.exit(1);
1301
1427
  }
1302
1428
  initRepo();
@@ -1308,6 +1434,8 @@ async function setupContainer(options = {}) {
1308
1434
  }
1309
1435
  const repoRoot = getRepoRoot();
1310
1436
  const gitDir = getGitDir();
1437
+ log(`Repo root: ${repoRoot}`);
1438
+ log(`Git dir: ${gitDir}`);
1311
1439
  let id;
1312
1440
  if (options.name) {
1313
1441
  id = options.name;
@@ -1321,7 +1449,7 @@ async function setupContainer(options = {}) {
1321
1449
  const containers = listContainers();
1322
1450
  const existing = containers.find((c) => c.id === id);
1323
1451
  if (existing && existing.status === "running") {
1324
- error(
1452
+ error2(
1325
1453
  `Container "${id}" is already running. Use "yolobox attach ${id}" to connect.`
1326
1454
  );
1327
1455
  process.exit(1);
@@ -1344,9 +1472,13 @@ async function setupContainer(options = {}) {
1344
1472
  }
1345
1473
  ensureGitignore(repoRoot);
1346
1474
  const gitIdentity = getGitIdentity();
1475
+ log("Resolving Docker image...");
1347
1476
  const imageResolution = resolveDockerImage({
1348
1477
  envImage: process.env.YOLOBOX_IMAGE
1349
1478
  });
1479
+ log(
1480
+ `Resolved image: ${imageResolution.image} (source: ${imageResolution.source})`
1481
+ );
1350
1482
  if (imageResolution.source === "env") {
1351
1483
  info(`Using custom Docker image: ${imageResolution.image}`);
1352
1484
  } else if (imageResolution.source === "local") {
@@ -1366,17 +1498,35 @@ async function setupContainer(options = {}) {
1366
1498
  );
1367
1499
  if (!pulled) {
1368
1500
  spinner.stop("Failed to pull image", 1);
1369
- error("Failed to pull Docker image.");
1501
+ error2("Failed to pull Docker image.");
1370
1502
  process.exit(1);
1371
1503
  }
1372
1504
  spinner.stop("Docker image pulled");
1373
1505
  }
1506
+ const testFile = path2.join(gitDir, "HEAD");
1507
+ if (!canDockerAccessPath(testFile, imageResolution.image)) {
1508
+ error2("Docker cannot access files in this directory.");
1509
+ if (process.platform === "darwin" && repoRoot.includes("/Library/Mobile Documents/")) {
1510
+ error2(
1511
+ '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.'
1512
+ );
1513
+ } else if (process.platform === "darwin") {
1514
+ error2(
1515
+ '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.'
1516
+ );
1517
+ }
1518
+ process.exit(1);
1519
+ }
1374
1520
  const claudeOauthToken = resolveToken();
1375
1521
  if (claudeOauthToken) {
1376
1522
  success("Claude auth token configured");
1377
1523
  } else {
1378
1524
  warn('No Claude auth token. Run "yolobox auth" to set up.');
1379
1525
  }
1526
+ log(`Starting container with id=${id}, image=${imageResolution.image}`);
1527
+ log(`Worktree path: ${worktreePath}`);
1528
+ log(`Git identity: ${gitIdentity.name} <${gitIdentity.email}>`);
1529
+ log(`Claude token: ${claudeOauthToken ? "set" : "not set"}`);
1380
1530
  const started = startContainer({
1381
1531
  id,
1382
1532
  worktreePath,
@@ -1387,7 +1537,13 @@ async function setupContainer(options = {}) {
1387
1537
  claudeOauthToken: claudeOauthToken ?? void 0
1388
1538
  });
1389
1539
  if (!started) {
1390
- error("Failed to start container.");
1540
+ if (isEnabled()) {
1541
+ error2(
1542
+ `Failed to start container. Check ${getLogPath()} for details.`
1543
+ );
1544
+ } else {
1545
+ error2("Failed to start container. Run with --debug for details.");
1546
+ }
1391
1547
  process.exit(1);
1392
1548
  }
1393
1549
  return { id, repoRoot };
@@ -1453,14 +1609,14 @@ var kill_default = defineCommand5({
1453
1609
  },
1454
1610
  run: async ({ args }) => {
1455
1611
  if (!isDockerRunning()) {
1456
- error("Docker is not running.");
1612
+ error2("Docker is not running.");
1457
1613
  return process.exit(1);
1458
1614
  }
1459
1615
  let id = args.id;
1460
1616
  if (!id) {
1461
1617
  const containers = listContainers();
1462
1618
  if (containers.length === 0) {
1463
- error("No yolobox containers found.");
1619
+ error2("No yolobox containers found.");
1464
1620
  return process.exit(1);
1465
1621
  }
1466
1622
  const selected = await p.select({
@@ -1486,13 +1642,13 @@ var kill_default = defineCommand5({
1486
1642
  const containers = listContainers();
1487
1643
  const match = containers.find((c) => c.id === id);
1488
1644
  if (!match) {
1489
- error(`No yolobox container found with ID "${id}".`);
1645
+ error2(`No yolobox container found with ID "${id}".`);
1490
1646
  return process.exit(1);
1491
1647
  }
1492
1648
  }
1493
1649
  const killed = killContainer(id);
1494
1650
  if (!killed) {
1495
- error(`Failed to kill yolobox-${id}. Is it running?`);
1651
+ error2(`Failed to kill yolobox-${id}. Is it running?`);
1496
1652
  process.exit(1);
1497
1653
  }
1498
1654
  success(`Killed yolobox-${id}`);
@@ -1522,7 +1678,7 @@ var ls_default = defineCommand6({
1522
1678
  },
1523
1679
  run: async () => {
1524
1680
  if (!isDockerRunning()) {
1525
- error("Docker is not running.");
1681
+ error2("Docker is not running.");
1526
1682
  process.exit(1);
1527
1683
  }
1528
1684
  const containers = listContainers();
@@ -1536,12 +1692,12 @@ var ls_default = defineCommand6({
1536
1692
  const statusW = Math.max(8, ...containers.map((c) => c.status.length)) + 2;
1537
1693
  const createdW = Math.max(9, ...containers.map((c) => c.created.length)) + 2;
1538
1694
  const header = `${"ID".padEnd(idW)}${"BRANCH".padEnd(branchW)}${"STATUS".padEnd(statusW)}${"CREATED".padEnd(createdW)}PATH`;
1539
- console.log(pc.dim(header));
1695
+ console.log(pc2.dim(header));
1540
1696
  for (let i = 0; i < containers.length; i++) {
1541
1697
  const c = containers[i];
1542
- const statusColor = c.status === "running" ? pc.green : pc.yellow;
1698
+ const statusColor = c.status === "running" ? pc2.green : pc2.yellow;
1543
1699
  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])}`
1700
+ `${c.id.padEnd(idW)}${c.branch.padEnd(branchW)}${statusColor(c.status.padEnd(statusW))}${pc2.dim(c.created.padEnd(createdW))}${pc2.dim(paths[i])}`
1545
1701
  );
1546
1702
  }
1547
1703
  }
@@ -1604,7 +1760,7 @@ var nuke_default = defineCommand7({
1604
1760
  const targets = [...targetMap.values()];
1605
1761
  if (targets.length === 0) {
1606
1762
  if (!dockerRunning) {
1607
- error(
1763
+ error2(
1608
1764
  "Docker is not running and no local branches or worktrees found."
1609
1765
  );
1610
1766
  } else {
@@ -1622,18 +1778,18 @@ Found ${targets.length} yolobox resource${targets.length === 1 ? "" : "s"}:
1622
1778
  for (const target of targets) {
1623
1779
  const parts = [];
1624
1780
  if (target.container) {
1625
- const statusColor = target.container.status === "running" ? pc.green : pc.dim;
1781
+ const statusColor = target.container.status === "running" ? pc2.green : pc2.dim;
1626
1782
  parts.push(`container ${statusColor(target.container.status)}`);
1627
1783
  }
1628
1784
  if (target.hasBranch) {
1629
- parts.push(`branch ${pc.cyan(`yolo/${target.id}`)}`);
1785
+ parts.push(`branch ${pc2.cyan(`yolo/${target.id}`)}`);
1630
1786
  }
1631
1787
  if (target.hasWorktree) {
1632
1788
  parts.push("worktree");
1633
1789
  }
1634
- const pathSuffix = args.all && target.container ? ` ${pc.dim(target.container.path)}` : "";
1790
+ const pathSuffix = args.all && target.container ? ` ${pc2.dim(target.container.path)}` : "";
1635
1791
  console.log(
1636
- ` ${pc.bold(target.id)} ${parts.join(pc.dim(" \xB7 "))}${pathSuffix}`
1792
+ ` ${pc2.bold(target.id)} ${parts.join(pc2.dim(" \xB7 "))}${pathSuffix}`
1637
1793
  );
1638
1794
  }
1639
1795
  console.log();
@@ -1676,10 +1832,10 @@ Found ${targets.length} yolobox resource${targets.length === 1 ? "" : "s"}:
1676
1832
  if (!target.container) continue;
1677
1833
  const success2 = killContainer(target.id);
1678
1834
  if (success2) {
1679
- success(`Killed container ${pc.bold(target.id)}`);
1835
+ success(`Killed container ${pc2.bold(target.id)}`);
1680
1836
  containersKilled++;
1681
1837
  } else {
1682
- error(`Failed to kill container ${target.id}`);
1838
+ error2(`Failed to kill container ${target.id}`);
1683
1839
  killFailures++;
1684
1840
  }
1685
1841
  }
@@ -1692,7 +1848,7 @@ Found ${targets.length} yolobox resource${targets.length === 1 ? "" : "s"}:
1692
1848
  const removed = removeWorktree(repoRoot, target.id);
1693
1849
  if (removed) {
1694
1850
  success(
1695
- `Removed worktree ${pc.bold(`.yolobox/${target.id}`)}`
1851
+ `Removed worktree ${pc2.bold(`.yolobox/${target.id}`)}`
1696
1852
  );
1697
1853
  worktreesRemoved++;
1698
1854
  } else {
@@ -1703,7 +1859,7 @@ Found ${targets.length} yolobox resource${targets.length === 1 ? "" : "s"}:
1703
1859
  const branchName = `yolo/${target.id}`;
1704
1860
  const deleted = deleteBranch(branchName);
1705
1861
  if (deleted) {
1706
- success(`Deleted branch ${pc.bold(branchName)}`);
1862
+ success(`Deleted branch ${pc2.bold(branchName)}`);
1707
1863
  branchesDeleted++;
1708
1864
  } else {
1709
1865
  warn(`Could not delete branch ${branchName}`);
@@ -1733,7 +1889,7 @@ Found ${targets.length} yolobox resource${targets.length === 1 ? "" : "s"}:
1733
1889
  summaryParts.push(
1734
1890
  `${killFailures} container${s(killFailures)} failed to stop`
1735
1891
  );
1736
- error(`${summaryParts.join(", ")}.`);
1892
+ error2(`${summaryParts.join(", ")}.`);
1737
1893
  process.exit(1);
1738
1894
  } else {
1739
1895
  const msg = summaryParts.join(", ");
@@ -1759,7 +1915,7 @@ var rm_default = defineCommand8({
1759
1915
  },
1760
1916
  run: async ({ args }) => {
1761
1917
  if (!isDockerRunning()) {
1762
- error("Docker is not running.");
1918
+ error2("Docker is not running.");
1763
1919
  return process.exit(1);
1764
1920
  }
1765
1921
  const repoRoot = getRepoRoot();
@@ -1767,7 +1923,7 @@ var rm_default = defineCommand8({
1767
1923
  if (!id) {
1768
1924
  const containers = listContainers();
1769
1925
  if (containers.length === 0) {
1770
- error("No yolobox containers found.");
1926
+ error2("No yolobox containers found.");
1771
1927
  return process.exit(1);
1772
1928
  }
1773
1929
  const selected = await p.select({
@@ -1794,7 +1950,7 @@ var rm_default = defineCommand8({
1794
1950
  const hasWorktree = getExistingWorktreeIds(repoRoot).includes(id);
1795
1951
  const hasBranch = getBranches().includes(`yolo/${id}`);
1796
1952
  if (!hasContainer && !hasWorktree && !hasBranch) {
1797
- error(`No yolobox found with ID "${id}".`);
1953
+ error2(`No yolobox found with ID "${id}".`);
1798
1954
  return process.exit(1);
1799
1955
  }
1800
1956
  }
@@ -1831,7 +1987,7 @@ var start_default = defineCommand9({
1831
1987
  run: async ({ args }) => {
1832
1988
  if (args.name) {
1833
1989
  if (!isDockerRunning()) {
1834
- error("Docker is not running.");
1990
+ error2("Docker is not running.");
1835
1991
  return process.exit(1);
1836
1992
  }
1837
1993
  const containers = listContainers();
@@ -1844,7 +2000,9 @@ var start_default = defineCommand9({
1844
2000
  }
1845
2001
  info(`Restarting stopped container "${args.name}"...`);
1846
2002
  if (!restartContainer(args.name)) {
1847
- error(`Failed to restart container "${args.name}".`);
2003
+ error2(
2004
+ `Failed to restart container "${args.name}". Run with --debug for details.`
2005
+ );
1848
2006
  return process.exit(1);
1849
2007
  }
1850
2008
  outro2(`Launching shell in ${args.name}...`);
@@ -1861,10 +2019,21 @@ var start_default = defineCommand9({
1861
2019
  });
1862
2020
 
1863
2021
  // src/index.ts
2022
+ var debugIndex = process.argv.indexOf("--debug");
2023
+ if (debugIndex !== -1) {
2024
+ process.argv.splice(debugIndex, 1);
2025
+ const logPath = enable();
2026
+ log(`yolobox v${"0.1.5"}`);
2027
+ log(`args: ${process.argv.slice(2).join(" ")}`);
2028
+ log(`cwd: ${process.cwd()}`);
2029
+ log(`node: ${process.version}`);
2030
+ log(`platform: ${process.platform} ${process.arch}`);
2031
+ log(`log file: ${logPath}`);
2032
+ }
1864
2033
  var main = defineCommand10({
1865
2034
  meta: {
1866
2035
  name: "yolobox",
1867
- version: "0.1.3",
2036
+ version: "0.1.5",
1868
2037
  description: "Run Claude Code in Docker containers. YOLO safely."
1869
2038
  },
1870
2039
  subCommands: {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yolobox",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "Run Claude Code in a Docker container with --dangerously-skip-permissions. YOLO safely.",
5
5
  "type": "module",
6
6
  "bin": {