@lumy-pack/syncpoint 0.0.10 → 0.0.11

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/dist/cli.mjs CHANGED
@@ -87,9 +87,6 @@ var init_wizard_template = __esm({
87
87
  }
88
88
  });
89
89
 
90
- // src/cli.ts
91
- import { Command } from "commander";
92
-
93
90
  // ../shared/src/respond.ts
94
91
  function respond(command, data, startTime, version) {
95
92
  const response = {
@@ -119,6 +116,9 @@ function respondError(command, code, message, startTime, version, details) {
119
116
  process.exitCode = 1;
120
117
  }
121
118
 
119
+ // src/cli.ts
120
+ import { Command } from "commander";
121
+
122
122
  // src/commands/Backup.tsx
123
123
  import { Box, Static, Text as Text2, useApp } from "ink";
124
124
  import { render } from "ink";
@@ -602,7 +602,7 @@ function validateMetadata(data) {
602
602
  }
603
603
 
604
604
  // src/version.ts
605
- var VERSION = "0.0.10";
605
+ var VERSION = "0.0.11";
606
606
 
607
607
  // src/core/metadata.ts
608
608
  var METADATA_VERSION = "1.0.0";
@@ -1102,6 +1102,8 @@ var SyncpointErrorCode = {
1102
1102
  PROVISION_FAILED: "PROVISION_FAILED",
1103
1103
  MISSING_ARGUMENT: "MISSING_ARGUMENT",
1104
1104
  INVALID_ARGUMENT: "INVALID_ARGUMENT",
1105
+ LINK_FAILED: "LINK_FAILED",
1106
+ UNLINK_FAILED: "UNLINK_FAILED",
1105
1107
  UNKNOWN: "UNKNOWN"
1106
1108
  };
1107
1109
  function classifyError(err) {
@@ -1118,6 +1120,12 @@ function classifyError(err) {
1118
1120
  if (msg.includes("Template file not found")) {
1119
1121
  return SyncpointErrorCode.TEMPLATE_NOT_FOUND;
1120
1122
  }
1123
+ if (msg.includes("not a symlink") || msg.includes('"syncpoint link"')) {
1124
+ return SyncpointErrorCode.UNLINK_FAILED;
1125
+ }
1126
+ if (msg.includes("destination is not set") || msg.includes("cross-device") || msg.includes("EXDEV")) {
1127
+ return SyncpointErrorCode.LINK_FAILED;
1128
+ }
1121
1129
  return SyncpointErrorCode.UNKNOWN;
1122
1130
  }
1123
1131
 
@@ -1267,7 +1275,13 @@ var COMMANDS = {
1267
1275
  required: false
1268
1276
  }
1269
1277
  ],
1270
- options: [{ flag: "--delete <filename>", description: "Delete item by filename", type: "string" }],
1278
+ options: [
1279
+ {
1280
+ flag: "--delete <filename>",
1281
+ description: "Delete item by filename",
1282
+ type: "string"
1283
+ }
1284
+ ],
1271
1285
  examples: [
1272
1286
  "npx @lumy-pack/syncpoint list",
1273
1287
  "npx @lumy-pack/syncpoint list backups",
@@ -1279,7 +1293,11 @@ var COMMANDS = {
1279
1293
  description: "Show ~/.syncpoint/ status summary and manage cleanup",
1280
1294
  usage: "npx @lumy-pack/syncpoint status [options]",
1281
1295
  options: [
1282
- { flag: "--cleanup", description: "Enter interactive cleanup mode", type: "boolean" }
1296
+ {
1297
+ flag: "--cleanup",
1298
+ description: "Enter interactive cleanup mode",
1299
+ type: "boolean"
1300
+ }
1283
1301
  ],
1284
1302
  examples: [
1285
1303
  "npx @lumy-pack/syncpoint status",
@@ -1302,6 +1320,38 @@ var COMMANDS = {
1302
1320
  "npx @lumy-pack/syncpoint migrate --dry-run"
1303
1321
  ]
1304
1322
  },
1323
+ link: {
1324
+ name: "link",
1325
+ description: "Move ~/.syncpoint to backup destination and create a symlink",
1326
+ usage: "npx @lumy-pack/syncpoint link [options]",
1327
+ options: [
1328
+ {
1329
+ flag: "-r, --ref <path>",
1330
+ description: "Adopt <path>/.syncpoint as ~/.syncpoint via symlink",
1331
+ type: "string"
1332
+ }
1333
+ ],
1334
+ examples: [
1335
+ "npx @lumy-pack/syncpoint link",
1336
+ "npx @lumy-pack/syncpoint link --ref ~/Dropbox"
1337
+ ]
1338
+ },
1339
+ unlink: {
1340
+ name: "unlink",
1341
+ description: "Remove the symlink and restore ~/.syncpoint from the destination copy",
1342
+ usage: "npx @lumy-pack/syncpoint unlink [options]",
1343
+ options: [
1344
+ {
1345
+ flag: "--clean",
1346
+ description: "Also remove the destination copy after restoring",
1347
+ type: "boolean"
1348
+ }
1349
+ ],
1350
+ examples: [
1351
+ "npx @lumy-pack/syncpoint unlink",
1352
+ "npx @lumy-pack/syncpoint unlink --clean"
1353
+ ]
1354
+ },
1305
1355
  help: {
1306
1356
  name: "help",
1307
1357
  description: "Display help information",
@@ -1604,19 +1654,19 @@ init_assets();
1604
1654
  // src/utils/claude-code-runner.ts
1605
1655
  import { spawn } from "child_process";
1606
1656
  async function isClaudeCodeAvailable() {
1607
- return new Promise((resolve2) => {
1657
+ return new Promise((resolve3) => {
1608
1658
  const child = spawn("which", ["claude"], { shell: true });
1609
1659
  child.on("close", (code) => {
1610
- resolve2(code === 0);
1660
+ resolve3(code === 0);
1611
1661
  });
1612
1662
  child.on("error", () => {
1613
- resolve2(false);
1663
+ resolve3(false);
1614
1664
  });
1615
1665
  });
1616
1666
  }
1617
1667
  async function invokeClaudeCode(prompt, options) {
1618
1668
  const timeout = options?.timeout ?? 12e4;
1619
- return await new Promise((resolve2, reject) => {
1669
+ return await new Promise((resolve3, reject) => {
1620
1670
  const args = ["--permission-mode", "acceptEdits", "--model", "sonnet"];
1621
1671
  if (options?.sessionId) {
1622
1672
  args.push("--session", options.sessionId);
@@ -1641,13 +1691,13 @@ async function invokeClaudeCode(prompt, options) {
1641
1691
  child.on("close", (code) => {
1642
1692
  clearTimeout(timer);
1643
1693
  if (code === 0) {
1644
- resolve2({
1694
+ resolve3({
1645
1695
  success: true,
1646
1696
  output: stdout,
1647
1697
  sessionId: options?.sessionId
1648
1698
  });
1649
1699
  } else {
1650
- resolve2({
1700
+ resolve3({
1651
1701
  success: false,
1652
1702
  output: stdout,
1653
1703
  error: stderr || `Process exited with code ${code}`
@@ -1667,7 +1717,7 @@ async function resumeClaudeCodeSession(sessionId, prompt, options) {
1667
1717
  });
1668
1718
  }
1669
1719
  async function invokeClaudeCodeInteractive(prompt) {
1670
- return await new Promise((resolve2, reject) => {
1720
+ return await new Promise((resolve3, reject) => {
1671
1721
  const initialMessage = `${prompt}
1672
1722
 
1673
1723
  IMPORTANT INSTRUCTIONS:
@@ -1691,7 +1741,7 @@ Start by asking the user about their backup priorities for the home directory st
1691
1741
  // Share stdin/stdout/stderr with parent process
1692
1742
  });
1693
1743
  child.on("close", (code) => {
1694
- resolve2({
1744
+ resolve3({
1695
1745
  success: code === 0,
1696
1746
  output: ""
1697
1747
  // No captured output in interactive mode
@@ -2247,11 +2297,10 @@ function registerInitCommand(program2) {
2247
2297
  });
2248
2298
  }
2249
2299
 
2250
- // src/commands/List.tsx
2251
- import { unlinkSync } from "fs";
2252
- import { Box as Box6, Text as Text8, useApp as useApp4, useInput as useInput2 } from "ink";
2300
+ // src/commands/Link.tsx
2301
+ import { lstat as lstat3 } from "fs/promises";
2302
+ import { Box as Box5, Text as Text7, useApp as useApp4 } from "ink";
2253
2303
  import { render as render5 } from "ink";
2254
- import SelectInput from "ink-select-input";
2255
2304
  import { useEffect as useEffect4, useState as useState5 } from "react";
2256
2305
 
2257
2306
  // src/components/Confirm.tsx
@@ -2291,9 +2340,298 @@ var Confirm = ({
2291
2340
  ] });
2292
2341
  };
2293
2342
 
2343
+ // src/core/link.ts
2344
+ import {
2345
+ cp,
2346
+ lstat as lstat2,
2347
+ readlink as readlink2,
2348
+ rename,
2349
+ rm as rm2,
2350
+ stat as stat2,
2351
+ symlink,
2352
+ unlink
2353
+ } from "fs/promises";
2354
+ import { basename as basename2, join as join10, resolve as resolve2 } from "path";
2355
+ async function linkSyncpoint(destination, _options = {}) {
2356
+ const appDir = getAppDir();
2357
+ const expandedDest = expandTilde(destination);
2358
+ const targetDir = join10(expandedDest, ".syncpoint");
2359
+ let wasAlreadyLinked = false;
2360
+ let lstats;
2361
+ try {
2362
+ lstats = await lstat2(appDir);
2363
+ } catch {
2364
+ throw new Error(`${appDir} does not exist. Run "syncpoint init" first.`);
2365
+ }
2366
+ if (lstats.isSymbolicLink()) {
2367
+ wasAlreadyLinked = true;
2368
+ const existingTarget = await readlink2(appDir);
2369
+ await unlink(appDir);
2370
+ if (existingTarget !== targetDir) {
2371
+ await rename(existingTarget, appDir);
2372
+ } else {
2373
+ await symlink(targetDir, appDir);
2374
+ return { appDir, targetDir, wasAlreadyLinked };
2375
+ }
2376
+ }
2377
+ try {
2378
+ await rename(appDir, targetDir);
2379
+ } catch (err) {
2380
+ const isExdev = err instanceof Error && (err.message.includes("EXDEV") || err.message.includes("cross-device"));
2381
+ if (isExdev) {
2382
+ throw new Error(
2383
+ `Cannot move ${appDir} to ${targetDir}: source and destination are on different filesystems. Use a destination on the same filesystem, or manually copy the directory.`
2384
+ );
2385
+ }
2386
+ throw err;
2387
+ }
2388
+ await symlink(targetDir, appDir);
2389
+ return { appDir, targetDir, wasAlreadyLinked };
2390
+ }
2391
+ async function linkSyncpointByRef(refPath) {
2392
+ const appDir = getAppDir();
2393
+ const absoluteRef = resolve2(expandTilde(refPath));
2394
+ const targetDir = basename2(absoluteRef) === ".syncpoint" ? absoluteRef : join10(absoluteRef, ".syncpoint");
2395
+ let refStats;
2396
+ try {
2397
+ refStats = await stat2(targetDir);
2398
+ } catch {
2399
+ throw new Error(`Reference syncpoint path does not exist: ${targetDir}`);
2400
+ }
2401
+ if (!refStats.isDirectory()) {
2402
+ throw new Error(
2403
+ `Reference syncpoint path is not a directory: ${targetDir}`
2404
+ );
2405
+ }
2406
+ try {
2407
+ const existing = await lstat2(appDir);
2408
+ if (existing.isSymbolicLink()) {
2409
+ await unlink(appDir);
2410
+ } else {
2411
+ await rm2(appDir, { recursive: true, force: true });
2412
+ }
2413
+ } catch {
2414
+ }
2415
+ await symlink(targetDir, appDir);
2416
+ return { appDir, targetDir, wasAlreadyLinked: false };
2417
+ }
2418
+ async function unlinkSyncpoint(options = {}) {
2419
+ const appDir = getAppDir();
2420
+ let lstats;
2421
+ try {
2422
+ lstats = await lstat2(appDir);
2423
+ } catch {
2424
+ throw new Error(`${appDir} does not exist.`);
2425
+ }
2426
+ if (!lstats.isSymbolicLink()) {
2427
+ throw new Error(`${appDir} is not a symlink. Run "syncpoint link" first.`);
2428
+ }
2429
+ const targetDir = await readlink2(appDir);
2430
+ await unlink(appDir);
2431
+ await cp(targetDir, appDir, { recursive: true });
2432
+ if (options.clean) {
2433
+ await rm2(targetDir, { recursive: true, force: true });
2434
+ }
2435
+ return { appDir, targetDir, cleaned: options.clean ?? false };
2436
+ }
2437
+
2438
+ // src/commands/Link.tsx
2439
+ import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
2440
+ var LinkView = ({ refPath, yes }) => {
2441
+ const { exit } = useApp4();
2442
+ const [phase, setPhase] = useState5("checking");
2443
+ const [result, setResult] = useState5(null);
2444
+ const [error, setError] = useState5(null);
2445
+ const [existingType, setExistingType] = useState5("directory");
2446
+ useEffect4(() => {
2447
+ (async () => {
2448
+ try {
2449
+ if (!refPath) {
2450
+ setPhase("linking");
2451
+ return;
2452
+ }
2453
+ const appDir = getAppDir();
2454
+ try {
2455
+ const stats = await lstat3(appDir);
2456
+ setExistingType(stats.isSymbolicLink() ? "symlink" : "directory");
2457
+ if (yes) {
2458
+ setPhase("linking");
2459
+ } else {
2460
+ setPhase("confirming");
2461
+ }
2462
+ } catch {
2463
+ setPhase("linking");
2464
+ }
2465
+ } catch (err) {
2466
+ setError(err instanceof Error ? err.message : String(err));
2467
+ setPhase("error");
2468
+ setTimeout(() => exit(), 100);
2469
+ }
2470
+ })();
2471
+ }, []);
2472
+ useEffect4(() => {
2473
+ if (phase !== "linking") return;
2474
+ (async () => {
2475
+ try {
2476
+ let linkResult;
2477
+ if (refPath) {
2478
+ linkResult = await linkSyncpointByRef(refPath);
2479
+ } else {
2480
+ const config = await loadConfig();
2481
+ const destination = config.backup.destination;
2482
+ if (!destination) {
2483
+ throw new Error(
2484
+ 'backup.destination is not set in config.yml. Set it before running "syncpoint link".'
2485
+ );
2486
+ }
2487
+ linkResult = await linkSyncpoint(destination);
2488
+ }
2489
+ setResult(linkResult);
2490
+ setPhase("done");
2491
+ setTimeout(() => exit(), 100);
2492
+ } catch (err) {
2493
+ setError(err instanceof Error ? err.message : String(err));
2494
+ setPhase("error");
2495
+ setTimeout(() => exit(), 100);
2496
+ }
2497
+ })();
2498
+ }, [phase]);
2499
+ if (phase === "error" || error) {
2500
+ return /* @__PURE__ */ jsx7(Box5, { flexDirection: "column", children: /* @__PURE__ */ jsxs7(Text7, { color: "red", children: [
2501
+ "\u2717 Link failed: ",
2502
+ error
2503
+ ] }) });
2504
+ }
2505
+ if (phase === "confirming") {
2506
+ return /* @__PURE__ */ jsxs7(Box5, { flexDirection: "column", children: [
2507
+ /* @__PURE__ */ jsxs7(Text7, { color: "yellow", children: [
2508
+ "\u26A0 ~/.syncpoint already exists as a ",
2509
+ existingType,
2510
+ "."
2511
+ ] }),
2512
+ /* @__PURE__ */ jsx7(
2513
+ Confirm,
2514
+ {
2515
+ message: "Overwrite with symlink?",
2516
+ defaultYes: false,
2517
+ onConfirm: (confirmed) => {
2518
+ if (confirmed) {
2519
+ setPhase("linking");
2520
+ } else {
2521
+ setError("Aborted.");
2522
+ setPhase("error");
2523
+ setTimeout(() => exit(), 100);
2524
+ }
2525
+ }
2526
+ }
2527
+ )
2528
+ ] });
2529
+ }
2530
+ if (phase === "checking" || phase === "linking") {
2531
+ return /* @__PURE__ */ jsx7(Box5, { children: /* @__PURE__ */ jsx7(Text7, { children: "\u25B8 Linking ~/.syncpoint to destination..." }) });
2532
+ }
2533
+ return /* @__PURE__ */ jsxs7(Box5, { flexDirection: "column", children: [
2534
+ /* @__PURE__ */ jsx7(Text7, { color: "green", bold: true, children: "\u2713 Link complete" }),
2535
+ result && /* @__PURE__ */ jsxs7(Fragment2, { children: [
2536
+ /* @__PURE__ */ jsxs7(Text7, { children: [
2537
+ " ",
2538
+ "From: ",
2539
+ contractTilde(result.appDir)
2540
+ ] }),
2541
+ /* @__PURE__ */ jsxs7(Text7, { children: [
2542
+ " ",
2543
+ "To: ",
2544
+ contractTilde(result.targetDir)
2545
+ ] }),
2546
+ result.wasAlreadyLinked && /* @__PURE__ */ jsxs7(Text7, { color: "yellow", children: [
2547
+ " ",
2548
+ "(previous link was removed and re-linked)"
2549
+ ] })
2550
+ ] })
2551
+ ] });
2552
+ };
2553
+ function registerLinkCommand(program2) {
2554
+ const cmdInfo = COMMANDS.link;
2555
+ const cmd = program2.command("link").description(cmdInfo.description);
2556
+ cmd.option(
2557
+ "-r, --ref <path>",
2558
+ "Adopt <path>/.syncpoint as ~/.syncpoint via symlink"
2559
+ );
2560
+ cmd.action(async (opts) => {
2561
+ const globalOpts = program2.opts();
2562
+ const startTime = Date.now();
2563
+ if (globalOpts.json) {
2564
+ try {
2565
+ if (opts.ref) {
2566
+ const appDir = getAppDir();
2567
+ let appDirExists = false;
2568
+ try {
2569
+ await lstat3(appDir);
2570
+ appDirExists = true;
2571
+ } catch {
2572
+ }
2573
+ if (appDirExists && !globalOpts.yes) {
2574
+ respondError(
2575
+ "link",
2576
+ SyncpointErrorCode.LINK_FAILED,
2577
+ "~/.syncpoint already exists. Use --yes to overwrite.",
2578
+ startTime,
2579
+ VERSION
2580
+ );
2581
+ return;
2582
+ }
2583
+ const linkResult = await linkSyncpointByRef(opts.ref);
2584
+ respond(
2585
+ "link",
2586
+ {
2587
+ appDir: linkResult.appDir,
2588
+ targetDir: linkResult.targetDir,
2589
+ wasAlreadyLinked: linkResult.wasAlreadyLinked
2590
+ },
2591
+ startTime,
2592
+ VERSION
2593
+ );
2594
+ } else {
2595
+ const config = await loadConfig();
2596
+ const destination = config.backup.destination;
2597
+ if (!destination) {
2598
+ throw new Error("backup.destination is not set in config.yml.");
2599
+ }
2600
+ const linkResult = await linkSyncpoint(destination);
2601
+ respond(
2602
+ "link",
2603
+ {
2604
+ appDir: linkResult.appDir,
2605
+ targetDir: linkResult.targetDir,
2606
+ wasAlreadyLinked: linkResult.wasAlreadyLinked
2607
+ },
2608
+ startTime,
2609
+ VERSION
2610
+ );
2611
+ }
2612
+ } catch (error) {
2613
+ const code = classifyError(error);
2614
+ respondError("link", code, error.message, startTime, VERSION);
2615
+ }
2616
+ return;
2617
+ }
2618
+ const { waitUntilExit } = render5(
2619
+ /* @__PURE__ */ jsx7(LinkView, { refPath: opts.ref, yes: globalOpts.yes ?? false })
2620
+ );
2621
+ await waitUntilExit();
2622
+ });
2623
+ }
2624
+
2625
+ // src/commands/List.tsx
2626
+ import { unlinkSync } from "fs";
2627
+ import { Box as Box7, Text as Text9, useApp as useApp5, useInput as useInput2 } from "ink";
2628
+ import { render as render6 } from "ink";
2629
+ import SelectInput from "ink-select-input";
2630
+ import { useEffect as useEffect5, useState as useState6 } from "react";
2631
+
2294
2632
  // src/components/Table.tsx
2295
- import { Box as Box5, Text as Text7 } from "ink";
2296
- import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
2633
+ import { Box as Box6, Text as Text8 } from "ink";
2634
+ import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
2297
2635
  var Table = ({
2298
2636
  headers,
2299
2637
  rows,
@@ -2310,13 +2648,13 @@ var Table = ({
2310
2648
  return text.padEnd(width);
2311
2649
  };
2312
2650
  const separator = widths.map((w) => "\u2500".repeat(w)).join(" ");
2313
- return /* @__PURE__ */ jsxs7(Box5, { flexDirection: "column", children: [
2314
- /* @__PURE__ */ jsx7(Text7, { children: headers.map((h, i) => /* @__PURE__ */ jsxs7(Text7, { bold: true, children: [
2651
+ return /* @__PURE__ */ jsxs8(Box6, { flexDirection: "column", children: [
2652
+ /* @__PURE__ */ jsx8(Text8, { children: headers.map((h, i) => /* @__PURE__ */ jsxs8(Text8, { bold: true, children: [
2315
2653
  padCell(h, widths[i]),
2316
2654
  i < headers.length - 1 ? " " : ""
2317
2655
  ] }, i)) }),
2318
- /* @__PURE__ */ jsx7(Text7, { color: "gray", children: separator }),
2319
- rows.map((row, rowIdx) => /* @__PURE__ */ jsx7(Text7, { children: row.map((cell, colIdx) => /* @__PURE__ */ jsxs7(Text7, { children: [
2656
+ /* @__PURE__ */ jsx8(Text8, { color: "gray", children: separator }),
2657
+ rows.map((row, rowIdx) => /* @__PURE__ */ jsx8(Text8, { children: row.map((cell, colIdx) => /* @__PURE__ */ jsxs8(Text8, { children: [
2320
2658
  padCell(cell, widths[colIdx]),
2321
2659
  colIdx < row.length - 1 ? " " : ""
2322
2660
  ] }, colIdx)) }, rowIdx))
@@ -2326,7 +2664,7 @@ var Table = ({
2326
2664
  // src/core/provision.ts
2327
2665
  import { exec } from "child_process";
2328
2666
  import { readFile as readFile4, readdir as readdir2 } from "fs/promises";
2329
- import { join as join10 } from "path";
2667
+ import { join as join11 } from "path";
2330
2668
  import YAML3 from "yaml";
2331
2669
  var REMOTE_SCRIPT_PATTERNS = [
2332
2670
  /curl\s.*\|\s*(ba)?sh/,
@@ -2366,7 +2704,7 @@ async function listTemplates() {
2366
2704
  if (!entry.isFile() || !entry.name.endsWith(".yml") && !entry.name.endsWith(".yaml")) {
2367
2705
  continue;
2368
2706
  }
2369
- const fullPath = join10(templatesDir, entry.name);
2707
+ const fullPath = join11(templatesDir, entry.name);
2370
2708
  try {
2371
2709
  const config = await loadTemplate(fullPath);
2372
2710
  templates.push({
@@ -2381,7 +2719,7 @@ async function listTemplates() {
2381
2719
  return templates;
2382
2720
  }
2383
2721
  function execAsync(command) {
2384
- return new Promise((resolve2, reject) => {
2722
+ return new Promise((resolve3, reject) => {
2385
2723
  exec(
2386
2724
  command,
2387
2725
  { shell: "/bin/bash", timeout: 3e5 },
@@ -2394,7 +2732,7 @@ function execAsync(command) {
2394
2732
  })
2395
2733
  );
2396
2734
  } else {
2397
- resolve2({
2735
+ resolve3({
2398
2736
  stdout: stdout?.toString() ?? "",
2399
2737
  stderr: stderr?.toString() ?? ""
2400
2738
  });
@@ -2484,8 +2822,8 @@ async function* runProvision(templatePath, options = {}) {
2484
2822
  }
2485
2823
 
2486
2824
  // src/core/restore.ts
2487
- import { copyFile, lstat as lstat2, readdir as readdir3, stat as stat2 } from "fs/promises";
2488
- import { dirname as dirname2, join as join11 } from "path";
2825
+ import { copyFile, lstat as lstat4, readdir as readdir3, stat as stat3 } from "fs/promises";
2826
+ import { dirname as dirname2, join as join12 } from "path";
2489
2827
  async function getBackupList(config) {
2490
2828
  const backupDir = config?.backup.destination ? resolveTargetPath(config.backup.destination) : getSubDir(BACKUPS_DIR);
2491
2829
  const exists = await fileExists(backupDir);
@@ -2494,8 +2832,8 @@ async function getBackupList(config) {
2494
2832
  const backups = [];
2495
2833
  for (const entry of entries) {
2496
2834
  if (!entry.isFile() || !entry.name.endsWith(".tar.gz")) continue;
2497
- const fullPath = join11(backupDir, entry.name);
2498
- const fileStat = await stat2(fullPath);
2835
+ const fullPath = join12(backupDir, entry.name);
2836
+ const fileStat = await stat3(fullPath);
2499
2837
  let hostname;
2500
2838
  let fileCount;
2501
2839
  try {
@@ -2543,7 +2881,7 @@ This may not be a valid syncpoint backup.`
2543
2881
  continue;
2544
2882
  }
2545
2883
  const currentHash = await computeFileHash(absPath);
2546
- const currentStat = await stat2(absPath);
2884
+ const currentStat = await stat3(absPath);
2547
2885
  if (currentHash === file.hash) {
2548
2886
  actions.push({
2549
2887
  path: file.path,
@@ -2569,7 +2907,7 @@ async function createSafetyBackup(filePaths) {
2569
2907
  const filename = `_pre-restore_${formatDatetime(now)}.tar.gz`;
2570
2908
  const backupDir = getSubDir(BACKUPS_DIR);
2571
2909
  await ensureDir(backupDir);
2572
- const archivePath = join11(backupDir, filename);
2910
+ const archivePath = join12(backupDir, filename);
2573
2911
  const files = [];
2574
2912
  for (const fp of filePaths) {
2575
2913
  const absPath = resolveTargetPath(fp);
@@ -2602,9 +2940,9 @@ async function restoreBackup(archivePath, options = {}) {
2602
2940
  safetyBackupPath
2603
2941
  };
2604
2942
  }
2605
- const { mkdtemp: mkdtemp2, rm: rm2 } = await import("fs/promises");
2943
+ const { mkdtemp: mkdtemp2, rm: rm3 } = await import("fs/promises");
2606
2944
  const { tmpdir: tmpdir2 } = await import("os");
2607
- const tmpDir = await mkdtemp2(join11(tmpdir2(), "syncpoint-restore-"));
2945
+ const tmpDir = await mkdtemp2(join12(tmpdir2(), "syncpoint-restore-"));
2608
2946
  try {
2609
2947
  await extractArchive(archivePath, tmpDir);
2610
2948
  for (const action of plan.actions) {
@@ -2613,7 +2951,7 @@ async function restoreBackup(archivePath, options = {}) {
2613
2951
  continue;
2614
2952
  }
2615
2953
  const archiveName = action.path.startsWith("~/") ? action.path.slice(2) : action.path;
2616
- const extractedPath = join11(tmpDir, archiveName);
2954
+ const extractedPath = join12(tmpDir, archiveName);
2617
2955
  const destPath = resolveTargetPath(action.path);
2618
2956
  const extractedExists = await fileExists(extractedPath);
2619
2957
  if (!extractedExists) {
@@ -2623,7 +2961,7 @@ async function restoreBackup(archivePath, options = {}) {
2623
2961
  }
2624
2962
  await ensureDir(dirname2(destPath));
2625
2963
  try {
2626
- const destStat = await lstat2(destPath);
2964
+ const destStat = await lstat4(destPath);
2627
2965
  if (destStat.isSymbolicLink()) {
2628
2966
  logger.warn(`Skipping symlink target: ${action.path}`);
2629
2967
  skippedFiles.push(action.path);
@@ -2636,45 +2974,45 @@ async function restoreBackup(archivePath, options = {}) {
2636
2974
  restoredFiles.push(action.path);
2637
2975
  }
2638
2976
  } finally {
2639
- await rm2(tmpDir, { recursive: true, force: true });
2977
+ await rm3(tmpDir, { recursive: true, force: true });
2640
2978
  }
2641
2979
  return { restoredFiles, skippedFiles, safetyBackupPath };
2642
2980
  }
2643
2981
 
2644
2982
  // src/commands/List.tsx
2645
- import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
2983
+ import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
2646
2984
  var MenuItem = ({ isSelected = false, label }) => {
2647
2985
  if (label === "Delete") {
2648
- return /* @__PURE__ */ jsx8(Text8, { bold: isSelected, color: "red", children: label });
2986
+ return /* @__PURE__ */ jsx9(Text9, { bold: isSelected, color: "red", children: label });
2649
2987
  }
2650
2988
  const match = label.match(/^(.+?)\s+\((\d+)\)$/);
2651
2989
  if (match) {
2652
2990
  const [, name, count] = match;
2653
- return /* @__PURE__ */ jsxs8(Text8, { children: [
2654
- /* @__PURE__ */ jsx8(Text8, { bold: isSelected, children: name }),
2991
+ return /* @__PURE__ */ jsxs9(Text9, { children: [
2992
+ /* @__PURE__ */ jsx9(Text9, { bold: isSelected, children: name }),
2655
2993
  " ",
2656
- /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
2994
+ /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
2657
2995
  "(",
2658
2996
  count,
2659
2997
  ")"
2660
2998
  ] })
2661
2999
  ] });
2662
3000
  }
2663
- return /* @__PURE__ */ jsx8(Text8, { bold: isSelected, children: label });
3001
+ return /* @__PURE__ */ jsx9(Text9, { bold: isSelected, children: label });
2664
3002
  };
2665
3003
  var ListView = ({ type, deleteIndex }) => {
2666
- const { exit } = useApp4();
2667
- const [phase, setPhase] = useState5("loading");
2668
- const [backups, setBackups] = useState5([]);
2669
- const [templates, setTemplates] = useState5([]);
2670
- const [selectedTemplate, setSelectedTemplate] = useState5(
3004
+ const { exit } = useApp5();
3005
+ const [phase, setPhase] = useState6("loading");
3006
+ const [backups, setBackups] = useState6([]);
3007
+ const [templates, setTemplates] = useState6([]);
3008
+ const [selectedTemplate, setSelectedTemplate] = useState6(
2671
3009
  null
2672
3010
  );
2673
- const [selectedBackup, setSelectedBackup] = useState5(null);
2674
- const [deleteTarget, setDeleteTarget] = useState5(null);
2675
- const [error, setError] = useState5(null);
2676
- const [backupDir, setBackupDir] = useState5(getSubDir("backups"));
2677
- const [successMessage, setSuccessMessage] = useState5(null);
3011
+ const [selectedBackup, setSelectedBackup] = useState6(null);
3012
+ const [deleteTarget, setDeleteTarget] = useState6(null);
3013
+ const [error, setError] = useState6(null);
3014
+ const [backupDir, setBackupDir] = useState6(getSubDir("backups"));
3015
+ const [successMessage, setSuccessMessage] = useState6(null);
2678
3016
  useInput2((_input, key) => {
2679
3017
  if (!key.escape) return;
2680
3018
  switch (phase) {
@@ -2698,7 +3036,7 @@ var ListView = ({ type, deleteIndex }) => {
2698
3036
  break;
2699
3037
  }
2700
3038
  });
2701
- useEffect4(() => {
3039
+ useEffect5(() => {
2702
3040
  (async () => {
2703
3041
  try {
2704
3042
  const config = await loadConfig();
@@ -2790,16 +3128,16 @@ var ListView = ({ type, deleteIndex }) => {
2790
3128
  }
2791
3129
  };
2792
3130
  if (phase === "error" || error) {
2793
- return /* @__PURE__ */ jsx8(Box6, { flexDirection: "column", children: /* @__PURE__ */ jsxs8(Text8, { color: "red", children: [
3131
+ return /* @__PURE__ */ jsx9(Box7, { flexDirection: "column", children: /* @__PURE__ */ jsxs9(Text9, { color: "red", children: [
2794
3132
  "\u2717 ",
2795
3133
  error
2796
3134
  ] }) });
2797
3135
  }
2798
3136
  if (phase === "loading") {
2799
- return /* @__PURE__ */ jsx8(Text8, { color: "cyan", children: "Loading..." });
3137
+ return /* @__PURE__ */ jsx9(Text9, { color: "cyan", children: "Loading..." });
2800
3138
  }
2801
3139
  if (phase === "deleting" && deleteTarget) {
2802
- return /* @__PURE__ */ jsx8(Box6, { flexDirection: "column", children: /* @__PURE__ */ jsx8(
3140
+ return /* @__PURE__ */ jsx9(Box7, { flexDirection: "column", children: /* @__PURE__ */ jsx9(
2803
3141
  Confirm,
2804
3142
  {
2805
3143
  message: `Delete ${deleteTarget.name}?`,
@@ -2809,7 +3147,7 @@ var ListView = ({ type, deleteIndex }) => {
2809
3147
  ) });
2810
3148
  }
2811
3149
  if (phase === "done" && deleteTarget) {
2812
- return /* @__PURE__ */ jsxs8(Text8, { color: "green", children: [
3150
+ return /* @__PURE__ */ jsxs9(Text9, { color: "green", children: [
2813
3151
  "\u2713 ",
2814
3152
  deleteTarget.name,
2815
3153
  " deleted"
@@ -2841,8 +3179,8 @@ var ListView = ({ type, deleteIndex }) => {
2841
3179
  setPhase("template-list");
2842
3180
  }
2843
3181
  };
2844
- return /* @__PURE__ */ jsxs8(Box6, { flexDirection: "column", children: [
2845
- /* @__PURE__ */ jsx8(
3182
+ return /* @__PURE__ */ jsxs9(Box7, { flexDirection: "column", children: [
3183
+ /* @__PURE__ */ jsx9(
2846
3184
  SelectInput,
2847
3185
  {
2848
3186
  items: menuItems,
@@ -2850,9 +3188,9 @@ var ListView = ({ type, deleteIndex }) => {
2850
3188
  itemComponent: MenuItem
2851
3189
  }
2852
3190
  ),
2853
- /* @__PURE__ */ jsx8(Box6, { marginTop: 1, children: /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
3191
+ /* @__PURE__ */ jsx9(Box7, { marginTop: 1, children: /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
2854
3192
  "Press ",
2855
- /* @__PURE__ */ jsx8(Text8, { bold: true, children: "ESC" }),
3193
+ /* @__PURE__ */ jsx9(Text9, { bold: true, children: "ESC" }),
2856
3194
  " to exit"
2857
3195
  ] }) })
2858
3196
  ] });
@@ -2869,10 +3207,10 @@ var ListView = ({ type, deleteIndex }) => {
2869
3207
  setPhase("backup-detail");
2870
3208
  }
2871
3209
  };
2872
- return /* @__PURE__ */ jsxs8(Box6, { flexDirection: "column", children: [
2873
- successMessage && /* @__PURE__ */ jsx8(Box6, { marginBottom: 1, children: /* @__PURE__ */ jsx8(Text8, { color: "green", children: successMessage }) }),
2874
- /* @__PURE__ */ jsx8(Text8, { bold: true, children: "\u25B8 Backups" }),
2875
- /* @__PURE__ */ jsx8(Box6, { marginTop: 1, children: backups.length === 0 ? /* @__PURE__ */ jsx8(Text8, { color: "gray", children: " No backups found." }) : /* @__PURE__ */ jsx8(
3210
+ return /* @__PURE__ */ jsxs9(Box7, { flexDirection: "column", children: [
3211
+ successMessage && /* @__PURE__ */ jsx9(Box7, { marginBottom: 1, children: /* @__PURE__ */ jsx9(Text9, { color: "green", children: successMessage }) }),
3212
+ /* @__PURE__ */ jsx9(Text9, { bold: true, children: "\u25B8 Backups" }),
3213
+ /* @__PURE__ */ jsx9(Box7, { marginTop: 1, children: backups.length === 0 ? /* @__PURE__ */ jsx9(Text9, { color: "gray", children: " No backups found." }) : /* @__PURE__ */ jsx9(
2876
3214
  SelectInput,
2877
3215
  {
2878
3216
  items,
@@ -2880,9 +3218,9 @@ var ListView = ({ type, deleteIndex }) => {
2880
3218
  itemComponent: MenuItem
2881
3219
  }
2882
3220
  ) }),
2883
- /* @__PURE__ */ jsx8(Box6, { marginTop: 1, children: /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
3221
+ /* @__PURE__ */ jsx9(Box7, { marginTop: 1, children: /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
2884
3222
  "Press ",
2885
- /* @__PURE__ */ jsx8(Text8, { bold: true, children: "ESC" }),
3223
+ /* @__PURE__ */ jsx9(Text9, { bold: true, children: "ESC" }),
2886
3224
  " to go back"
2887
3225
  ] }) })
2888
3226
  ] });
@@ -2899,9 +3237,9 @@ var ListView = ({ type, deleteIndex }) => {
2899
3237
  setPhase("template-detail");
2900
3238
  }
2901
3239
  };
2902
- return /* @__PURE__ */ jsxs8(Box6, { flexDirection: "column", children: [
2903
- /* @__PURE__ */ jsx8(Text8, { bold: true, children: "\u25B8 Templates" }),
2904
- /* @__PURE__ */ jsx8(Box6, { marginTop: 1, children: templates.length === 0 ? /* @__PURE__ */ jsx8(Text8, { color: "gray", children: " No templates found." }) : /* @__PURE__ */ jsx8(
3240
+ return /* @__PURE__ */ jsxs9(Box7, { flexDirection: "column", children: [
3241
+ /* @__PURE__ */ jsx9(Text9, { bold: true, children: "\u25B8 Templates" }),
3242
+ /* @__PURE__ */ jsx9(Box7, { marginTop: 1, children: templates.length === 0 ? /* @__PURE__ */ jsx9(Text9, { color: "gray", children: " No templates found." }) : /* @__PURE__ */ jsx9(
2905
3243
  SelectInput,
2906
3244
  {
2907
3245
  items,
@@ -2909,9 +3247,9 @@ var ListView = ({ type, deleteIndex }) => {
2909
3247
  itemComponent: MenuItem
2910
3248
  }
2911
3249
  ) }),
2912
- /* @__PURE__ */ jsx8(Box6, { marginTop: 1, children: /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
3250
+ /* @__PURE__ */ jsx9(Box7, { marginTop: 1, children: /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
2913
3251
  "Press ",
2914
- /* @__PURE__ */ jsx8(Text8, { bold: true, children: "ESC" }),
3252
+ /* @__PURE__ */ jsx9(Text9, { bold: true, children: "ESC" }),
2915
3253
  " to go back"
2916
3254
  ] }) })
2917
3255
  ] });
@@ -2939,20 +3277,20 @@ var ListView = ({ type, deleteIndex }) => {
2939
3277
  goBackToBackupList();
2940
3278
  }
2941
3279
  };
2942
- return /* @__PURE__ */ jsxs8(Box6, { flexDirection: "column", children: [
2943
- /* @__PURE__ */ jsxs8(Text8, { bold: true, children: [
3280
+ return /* @__PURE__ */ jsxs9(Box7, { flexDirection: "column", children: [
3281
+ /* @__PURE__ */ jsxs9(Text9, { bold: true, children: [
2944
3282
  "\u25B8 ",
2945
3283
  selectedBackup.filename.replace(".tar.gz", "")
2946
3284
  ] }),
2947
- /* @__PURE__ */ jsx8(Box6, { flexDirection: "column", marginLeft: 2, marginTop: 1, children: sections.map((section, idx) => {
3285
+ /* @__PURE__ */ jsx9(Box7, { flexDirection: "column", marginLeft: 2, marginTop: 1, children: sections.map((section, idx) => {
2948
3286
  const labelWidth = Math.max(...sections.map((s) => s.label.length)) + 1;
2949
- return /* @__PURE__ */ jsxs8(Box6, { children: [
2950
- /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: section.label.padEnd(labelWidth) }),
2951
- /* @__PURE__ */ jsx8(Text8, { children: " " }),
2952
- /* @__PURE__ */ jsx8(Text8, { children: section.value })
3287
+ return /* @__PURE__ */ jsxs9(Box7, { children: [
3288
+ /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: section.label.padEnd(labelWidth) }),
3289
+ /* @__PURE__ */ jsx9(Text9, { children: " " }),
3290
+ /* @__PURE__ */ jsx9(Text9, { children: section.value })
2953
3291
  ] }, idx);
2954
3292
  }) }),
2955
- /* @__PURE__ */ jsx8(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx8(
3293
+ /* @__PURE__ */ jsx9(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx9(
2956
3294
  SelectInput,
2957
3295
  {
2958
3296
  items: actionItems,
@@ -2960,9 +3298,9 @@ var ListView = ({ type, deleteIndex }) => {
2960
3298
  itemComponent: MenuItem
2961
3299
  }
2962
3300
  ) }),
2963
- /* @__PURE__ */ jsx8(Box6, { marginTop: 1, children: /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
3301
+ /* @__PURE__ */ jsx9(Box7, { marginTop: 1, children: /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
2964
3302
  "Press ",
2965
- /* @__PURE__ */ jsx8(Text8, { bold: true, children: "ESC" }),
3303
+ /* @__PURE__ */ jsx9(Text9, { bold: true, children: "ESC" }),
2966
3304
  " to go back"
2967
3305
  ] }) })
2968
3306
  ] });
@@ -2982,19 +3320,19 @@ var ListView = ({ type, deleteIndex }) => {
2982
3320
  }
2983
3321
  };
2984
3322
  const labelWidth = Math.max(...sections.map((s) => s.label.length)) + 1;
2985
- return /* @__PURE__ */ jsxs8(Box6, { flexDirection: "column", children: [
2986
- /* @__PURE__ */ jsxs8(Text8, { bold: true, children: [
3323
+ return /* @__PURE__ */ jsxs9(Box7, { flexDirection: "column", children: [
3324
+ /* @__PURE__ */ jsxs9(Text9, { bold: true, children: [
2987
3325
  "\u25B8 ",
2988
3326
  selectedTemplate.name
2989
3327
  ] }),
2990
- /* @__PURE__ */ jsx8(Box6, { flexDirection: "column", marginLeft: 2, marginTop: 1, children: sections.map((section, idx) => /* @__PURE__ */ jsxs8(Box6, { children: [
2991
- /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: section.label.padEnd(labelWidth) }),
2992
- /* @__PURE__ */ jsx8(Text8, { children: " " }),
2993
- /* @__PURE__ */ jsx8(Text8, { children: section.value })
3328
+ /* @__PURE__ */ jsx9(Box7, { flexDirection: "column", marginLeft: 2, marginTop: 1, children: sections.map((section, idx) => /* @__PURE__ */ jsxs9(Box7, { children: [
3329
+ /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: section.label.padEnd(labelWidth) }),
3330
+ /* @__PURE__ */ jsx9(Text9, { children: " " }),
3331
+ /* @__PURE__ */ jsx9(Text9, { children: section.value })
2994
3332
  ] }, idx)) }),
2995
- t.steps.length > 0 && /* @__PURE__ */ jsxs8(Box6, { flexDirection: "column", marginTop: 1, children: [
2996
- /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: " Provisioning Steps" }),
2997
- /* @__PURE__ */ jsx8(Box6, { marginLeft: 2, children: /* @__PURE__ */ jsx8(
3333
+ t.steps.length > 0 && /* @__PURE__ */ jsxs9(Box7, { flexDirection: "column", marginTop: 1, children: [
3334
+ /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: " Provisioning Steps" }),
3335
+ /* @__PURE__ */ jsx9(Box7, { marginLeft: 2, children: /* @__PURE__ */ jsx9(
2998
3336
  Table,
2999
3337
  {
3000
3338
  headers: ["#", "Step", "Description"],
@@ -3006,7 +3344,7 @@ var ListView = ({ type, deleteIndex }) => {
3006
3344
  }
3007
3345
  ) })
3008
3346
  ] }),
3009
- /* @__PURE__ */ jsx8(Box6, { marginTop: 1, children: /* @__PURE__ */ jsx8(
3347
+ /* @__PURE__ */ jsx9(Box7, { marginTop: 1, children: /* @__PURE__ */ jsx9(
3010
3348
  SelectInput,
3011
3349
  {
3012
3350
  items: actionItems,
@@ -3014,9 +3352,9 @@ var ListView = ({ type, deleteIndex }) => {
3014
3352
  itemComponent: MenuItem
3015
3353
  }
3016
3354
  ) }),
3017
- /* @__PURE__ */ jsx8(Box6, { marginTop: 1, children: /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
3355
+ /* @__PURE__ */ jsx9(Box7, { marginTop: 1, children: /* @__PURE__ */ jsxs9(Text9, { dimColor: true, children: [
3018
3356
  "Press ",
3019
- /* @__PURE__ */ jsx8(Text8, { bold: true, children: "ESC" }),
3357
+ /* @__PURE__ */ jsx9(Text9, { bold: true, children: "ESC" }),
3020
3358
  " to go back"
3021
3359
  ] }) })
3022
3360
  ] });
@@ -3085,23 +3423,23 @@ function registerListCommand(program2) {
3085
3423
  }
3086
3424
  const deleteIndex = opts.delete ? parseInt(opts.delete, 10) : void 0;
3087
3425
  if (deleteIndex !== void 0 && isNaN(deleteIndex)) {
3088
- const { waitUntilExit: waitUntilExit2 } = render5(
3089
- /* @__PURE__ */ jsx8(ListView, { type, deleteIndex: void 0 })
3426
+ const { waitUntilExit: waitUntilExit2 } = render6(
3427
+ /* @__PURE__ */ jsx9(ListView, { type, deleteIndex: void 0 })
3090
3428
  );
3091
3429
  await waitUntilExit2();
3092
3430
  return;
3093
3431
  }
3094
- const { waitUntilExit } = render5(
3095
- /* @__PURE__ */ jsx8(ListView, { type, deleteIndex })
3432
+ const { waitUntilExit } = render6(
3433
+ /* @__PURE__ */ jsx9(ListView, { type, deleteIndex })
3096
3434
  );
3097
3435
  await waitUntilExit();
3098
3436
  });
3099
3437
  }
3100
3438
 
3101
3439
  // src/commands/Migrate.tsx
3102
- import { Box as Box7, Text as Text9, useApp as useApp5 } from "ink";
3103
- import { render as render6 } from "ink";
3104
- import { useEffect as useEffect5, useState as useState6 } from "react";
3440
+ import { Box as Box8, Text as Text10, useApp as useApp6 } from "ink";
3441
+ import { render as render7 } from "ink";
3442
+ import { useEffect as useEffect6, useState as useState7 } from "react";
3105
3443
 
3106
3444
  // src/core/migrate.ts
3107
3445
  import { copyFile as copyFile2, readFile as readFile5, writeFile as writeFile4 } from "fs/promises";
@@ -3249,13 +3587,13 @@ ${(validation.errors ?? []).join("\n")}`
3249
3587
  }
3250
3588
 
3251
3589
  // src/commands/Migrate.tsx
3252
- import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
3590
+ import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
3253
3591
  var MigrateView = ({ dryRun }) => {
3254
- const { exit } = useApp5();
3255
- const [result, setResult] = useState6(null);
3256
- const [error, setError] = useState6(null);
3257
- const [loading, setLoading] = useState6(true);
3258
- useEffect5(() => {
3592
+ const { exit } = useApp6();
3593
+ const [result, setResult] = useState7(null);
3594
+ const [error, setError] = useState7(null);
3595
+ const [loading, setLoading] = useState7(true);
3596
+ useEffect6(() => {
3259
3597
  (async () => {
3260
3598
  try {
3261
3599
  const res = await migrateConfig({ dryRun });
@@ -3270,54 +3608,54 @@ var MigrateView = ({ dryRun }) => {
3270
3608
  })();
3271
3609
  }, []);
3272
3610
  if (error) {
3273
- return /* @__PURE__ */ jsx9(Box7, { flexDirection: "column", children: /* @__PURE__ */ jsxs9(Text9, { color: "red", children: [
3611
+ return /* @__PURE__ */ jsx10(Box8, { flexDirection: "column", children: /* @__PURE__ */ jsxs10(Text10, { color: "red", children: [
3274
3612
  "\u2717 ",
3275
3613
  error
3276
3614
  ] }) });
3277
3615
  }
3278
3616
  if (loading) {
3279
- return /* @__PURE__ */ jsx9(Text9, { children: "Analyzing config..." });
3617
+ return /* @__PURE__ */ jsx10(Text10, { children: "Analyzing config..." });
3280
3618
  }
3281
3619
  if (!result) return null;
3282
3620
  if (!result.migrated && result.added.length === 0 && result.deprecated.length === 0) {
3283
- return /* @__PURE__ */ jsx9(Box7, { flexDirection: "column", children: /* @__PURE__ */ jsx9(Text9, { color: "green", children: "\u2713 Config is already up to date." }) });
3621
+ return /* @__PURE__ */ jsx10(Box8, { flexDirection: "column", children: /* @__PURE__ */ jsx10(Text10, { color: "green", children: "\u2713 Config is already up to date." }) });
3284
3622
  }
3285
- return /* @__PURE__ */ jsxs9(Box7, { flexDirection: "column", children: [
3286
- dryRun && /* @__PURE__ */ jsx9(Box7, { marginBottom: 1, children: /* @__PURE__ */ jsx9(Text9, { color: "yellow", bold: true, children: "[dry-run] Preview only \u2014 no changes written." }) }),
3287
- result.added.length > 0 && /* @__PURE__ */ jsxs9(Box7, { flexDirection: "column", children: [
3288
- /* @__PURE__ */ jsx9(Text9, { bold: true, children: "New fields (added with defaults):" }),
3289
- result.added.map((field, i) => /* @__PURE__ */ jsxs9(Text9, { children: [
3623
+ return /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", children: [
3624
+ dryRun && /* @__PURE__ */ jsx10(Box8, { marginBottom: 1, children: /* @__PURE__ */ jsx10(Text10, { color: "yellow", bold: true, children: "[dry-run] Preview only \u2014 no changes written." }) }),
3625
+ result.added.length > 0 && /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", children: [
3626
+ /* @__PURE__ */ jsx10(Text10, { bold: true, children: "New fields (added with defaults):" }),
3627
+ result.added.map((field, i) => /* @__PURE__ */ jsxs10(Text10, { children: [
3290
3628
  " ",
3291
- /* @__PURE__ */ jsx9(Text9, { color: "green", children: "+" }),
3629
+ /* @__PURE__ */ jsx10(Text10, { color: "green", children: "+" }),
3292
3630
  " ",
3293
3631
  field
3294
3632
  ] }, i))
3295
3633
  ] }),
3296
- result.deprecated.length > 0 && /* @__PURE__ */ jsxs9(Box7, { flexDirection: "column", marginTop: result.added.length > 0 ? 1 : 0, children: [
3297
- /* @__PURE__ */ jsx9(Text9, { bold: true, children: "Deprecated fields (commented out):" }),
3298
- result.deprecated.map((field, i) => /* @__PURE__ */ jsxs9(Text9, { children: [
3634
+ result.deprecated.length > 0 && /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", marginTop: result.added.length > 0 ? 1 : 0, children: [
3635
+ /* @__PURE__ */ jsx10(Text10, { bold: true, children: "Deprecated fields (commented out):" }),
3636
+ result.deprecated.map((field, i) => /* @__PURE__ */ jsxs10(Text10, { children: [
3299
3637
  " ",
3300
- /* @__PURE__ */ jsx9(Text9, { color: "yellow", children: "~" }),
3638
+ /* @__PURE__ */ jsx10(Text10, { color: "yellow", children: "~" }),
3301
3639
  " ",
3302
3640
  field
3303
3641
  ] }, i))
3304
3642
  ] }),
3305
- result.preserved.length > 0 && /* @__PURE__ */ jsxs9(Box7, { flexDirection: "column", marginTop: 1, children: [
3306
- /* @__PURE__ */ jsxs9(Text9, { bold: true, children: [
3643
+ result.preserved.length > 0 && /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", marginTop: 1, children: [
3644
+ /* @__PURE__ */ jsxs10(Text10, { bold: true, children: [
3307
3645
  "Preserved fields (",
3308
3646
  result.preserved.length,
3309
3647
  "):"
3310
3648
  ] }),
3311
- result.preserved.map((field, i) => /* @__PURE__ */ jsxs9(Text9, { children: [
3649
+ result.preserved.map((field, i) => /* @__PURE__ */ jsxs10(Text10, { children: [
3312
3650
  " ",
3313
- /* @__PURE__ */ jsx9(Text9, { color: "blue", children: "\u2022" }),
3651
+ /* @__PURE__ */ jsx10(Text10, { color: "blue", children: "\u2022" }),
3314
3652
  " ",
3315
3653
  field
3316
3654
  ] }, i))
3317
3655
  ] }),
3318
- result.migrated && /* @__PURE__ */ jsxs9(Box7, { flexDirection: "column", marginTop: 1, children: [
3319
- /* @__PURE__ */ jsx9(Text9, { color: "green", children: "\u2713 Migration complete." }),
3320
- result.backupPath && /* @__PURE__ */ jsxs9(Text9, { children: [
3656
+ result.migrated && /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", marginTop: 1, children: [
3657
+ /* @__PURE__ */ jsx10(Text10, { color: "green", children: "\u2713 Migration complete." }),
3658
+ result.backupPath && /* @__PURE__ */ jsxs10(Text10, { children: [
3321
3659
  " ",
3322
3660
  "Backup saved to: ",
3323
3661
  result.backupPath
@@ -3339,50 +3677,50 @@ function registerMigrateCommand(program2) {
3339
3677
  }
3340
3678
  return;
3341
3679
  }
3342
- const { waitUntilExit } = render6(
3343
- /* @__PURE__ */ jsx9(MigrateView, { dryRun: opts.dryRun ?? false })
3680
+ const { waitUntilExit } = render7(
3681
+ /* @__PURE__ */ jsx10(MigrateView, { dryRun: opts.dryRun ?? false })
3344
3682
  );
3345
3683
  await waitUntilExit();
3346
3684
  });
3347
3685
  }
3348
3686
 
3349
3687
  // src/commands/Provision.tsx
3350
- import { Box as Box9, Text as Text11, useApp as useApp6 } from "ink";
3351
- import { render as render7 } from "ink";
3352
- import { useEffect as useEffect6, useState as useState7 } from "react";
3688
+ import { Box as Box10, Text as Text12, useApp as useApp7 } from "ink";
3689
+ import { render as render8 } from "ink";
3690
+ import { useEffect as useEffect7, useState as useState8 } from "react";
3353
3691
 
3354
3692
  // src/components/StepRunner.tsx
3355
- import { Box as Box8, Static as Static2, Text as Text10 } from "ink";
3693
+ import { Box as Box9, Static as Static2, Text as Text11 } from "ink";
3356
3694
  import Spinner2 from "ink-spinner";
3357
- import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
3695
+ import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
3358
3696
  var StepIcon = ({ status }) => {
3359
3697
  switch (status) {
3360
3698
  case "success":
3361
- return /* @__PURE__ */ jsx10(Text10, { color: "green", children: "\u2713" });
3699
+ return /* @__PURE__ */ jsx11(Text11, { color: "green", children: "\u2713" });
3362
3700
  case "running":
3363
- return /* @__PURE__ */ jsx10(Text10, { color: "yellow", children: /* @__PURE__ */ jsx10(Spinner2, { type: "dots" }) });
3701
+ return /* @__PURE__ */ jsx11(Text11, { color: "yellow", children: /* @__PURE__ */ jsx11(Spinner2, { type: "dots" }) });
3364
3702
  case "skipped":
3365
- return /* @__PURE__ */ jsx10(Text10, { color: "blue", children: "\u23ED" });
3703
+ return /* @__PURE__ */ jsx11(Text11, { color: "blue", children: "\u23ED" });
3366
3704
  case "failed":
3367
- return /* @__PURE__ */ jsx10(Text10, { color: "red", children: "\u2717" });
3705
+ return /* @__PURE__ */ jsx11(Text11, { color: "red", children: "\u2717" });
3368
3706
  case "pending":
3369
3707
  default:
3370
- return /* @__PURE__ */ jsx10(Text10, { color: "gray", children: "\u25CB" });
3708
+ return /* @__PURE__ */ jsx11(Text11, { color: "gray", children: "\u25CB" });
3371
3709
  }
3372
3710
  };
3373
3711
  var StepStatusText = ({ step }) => {
3374
3712
  switch (step.status) {
3375
3713
  case "success":
3376
- return /* @__PURE__ */ jsxs10(Text10, { color: "green", children: [
3714
+ return /* @__PURE__ */ jsxs11(Text11, { color: "green", children: [
3377
3715
  "Done",
3378
3716
  step.duration != null ? ` (${Math.round(step.duration / 1e3)}s)` : ""
3379
3717
  ] });
3380
3718
  case "running":
3381
- return /* @__PURE__ */ jsx10(Text10, { color: "yellow", children: "Running..." });
3719
+ return /* @__PURE__ */ jsx11(Text11, { color: "yellow", children: "Running..." });
3382
3720
  case "skipped":
3383
- return /* @__PURE__ */ jsx10(Text10, { color: "blue", children: "Skipped (already installed)" });
3721
+ return /* @__PURE__ */ jsx11(Text11, { color: "blue", children: "Skipped (already installed)" });
3384
3722
  case "failed":
3385
- return /* @__PURE__ */ jsxs10(Text10, { color: "red", children: [
3723
+ return /* @__PURE__ */ jsxs11(Text11, { color: "red", children: [
3386
3724
  "Failed",
3387
3725
  step.error ? `: ${step.error}` : ""
3388
3726
  ] });
@@ -3396,13 +3734,13 @@ var StepItemView = ({
3396
3734
  index,
3397
3735
  total,
3398
3736
  isLast
3399
- }) => /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", marginBottom: isLast ? 0 : 1, children: [
3400
- /* @__PURE__ */ jsxs10(Text10, { children: [
3737
+ }) => /* @__PURE__ */ jsxs11(Box9, { flexDirection: "column", marginBottom: isLast ? 0 : 1, children: [
3738
+ /* @__PURE__ */ jsxs11(Text11, { children: [
3401
3739
  " ",
3402
- /* @__PURE__ */ jsx10(StepIcon, { status: step.status }),
3403
- /* @__PURE__ */ jsxs10(Text10, { children: [
3740
+ /* @__PURE__ */ jsx11(StepIcon, { status: step.status }),
3741
+ /* @__PURE__ */ jsxs11(Text11, { children: [
3404
3742
  " ",
3405
- /* @__PURE__ */ jsxs10(Text10, { bold: true, children: [
3743
+ /* @__PURE__ */ jsxs11(Text11, { bold: true, children: [
3406
3744
  "Step ",
3407
3745
  index + 1,
3408
3746
  "/",
@@ -3412,13 +3750,13 @@ var StepItemView = ({
3412
3750
  step.name
3413
3751
  ] })
3414
3752
  ] }),
3415
- step.output && step.status !== "pending" && /* @__PURE__ */ jsxs10(Text10, { color: "gray", children: [
3753
+ step.output && step.status !== "pending" && /* @__PURE__ */ jsxs11(Text11, { color: "gray", children: [
3416
3754
  " ",
3417
3755
  step.output
3418
3756
  ] }),
3419
- /* @__PURE__ */ jsxs10(Text10, { children: [
3757
+ /* @__PURE__ */ jsxs11(Text11, { children: [
3420
3758
  " ",
3421
- /* @__PURE__ */ jsx10(StepStatusText, { step })
3759
+ /* @__PURE__ */ jsx11(StepStatusText, { step })
3422
3760
  ] })
3423
3761
  ] });
3424
3762
  var StepRunner = ({ steps, total }) => {
@@ -3433,8 +3771,8 @@ var StepRunner = ({ steps, total }) => {
3433
3771
  }
3434
3772
  });
3435
3773
  const lastIdx = steps.length - 1;
3436
- return /* @__PURE__ */ jsxs10(Box8, { flexDirection: "column", children: [
3437
- /* @__PURE__ */ jsx10(Static2, { items: completedSteps, children: (item) => /* @__PURE__ */ jsx10(
3774
+ return /* @__PURE__ */ jsxs11(Box9, { flexDirection: "column", children: [
3775
+ /* @__PURE__ */ jsx11(Static2, { items: completedSteps, children: (item) => /* @__PURE__ */ jsx11(
3438
3776
  StepItemView,
3439
3777
  {
3440
3778
  step: item,
@@ -3444,7 +3782,7 @@ var StepRunner = ({ steps, total }) => {
3444
3782
  },
3445
3783
  item.idx
3446
3784
  ) }),
3447
- activeSteps.map((item) => /* @__PURE__ */ jsx10(
3785
+ activeSteps.map((item) => /* @__PURE__ */ jsx11(
3448
3786
  StepItemView,
3449
3787
  {
3450
3788
  step: item,
@@ -3490,26 +3828,26 @@ ${pc2.red("\u2717")} Sudo authentication failed or was cancelled. Aborting.`
3490
3828
  }
3491
3829
 
3492
3830
  // src/commands/Provision.tsx
3493
- import { jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
3831
+ import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
3494
3832
  var ProvisionView = ({
3495
3833
  template,
3496
3834
  templatePath,
3497
3835
  options
3498
3836
  }) => {
3499
- const { exit } = useApp6();
3500
- const [phase, setPhase] = useState7(
3837
+ const { exit } = useApp7();
3838
+ const [phase, setPhase] = useState8(
3501
3839
  options.dryRun ? "done" : "running"
3502
3840
  );
3503
- const [steps, setSteps] = useState7(
3841
+ const [steps, setSteps] = useState8(
3504
3842
  template.steps.map((s) => ({
3505
3843
  name: s.name,
3506
3844
  status: "pending",
3507
3845
  output: s.description
3508
3846
  }))
3509
3847
  );
3510
- const [currentStep, setCurrentStep] = useState7(0);
3511
- const [error, setError] = useState7(null);
3512
- useEffect6(() => {
3848
+ const [currentStep, setCurrentStep] = useState8(0);
3849
+ const [error, setError] = useState8(null);
3850
+ useEffect7(() => {
3513
3851
  if (options.dryRun) {
3514
3852
  setTimeout(() => exit(), 100);
3515
3853
  return;
@@ -3551,7 +3889,7 @@ var ProvisionView = ({
3551
3889
  })();
3552
3890
  }, []);
3553
3891
  if (phase === "error" || error) {
3554
- return /* @__PURE__ */ jsx11(Box9, { flexDirection: "column", children: /* @__PURE__ */ jsxs11(Text11, { color: "red", children: [
3892
+ return /* @__PURE__ */ jsx12(Box10, { flexDirection: "column", children: /* @__PURE__ */ jsxs12(Text12, { color: "red", children: [
3555
3893
  "\u2717 ",
3556
3894
  error
3557
3895
  ] }) });
@@ -3559,27 +3897,27 @@ var ProvisionView = ({
3559
3897
  const successCount = steps.filter((s) => s.status === "success").length;
3560
3898
  const skippedCount = steps.filter((s) => s.status === "skipped").length;
3561
3899
  const failedCount = steps.filter((s) => s.status === "failed").length;
3562
- return /* @__PURE__ */ jsxs11(Box9, { flexDirection: "column", children: [
3563
- /* @__PURE__ */ jsxs11(Box9, { flexDirection: "column", marginBottom: 1, children: [
3564
- /* @__PURE__ */ jsxs11(Text11, { bold: true, children: [
3900
+ return /* @__PURE__ */ jsxs12(Box10, { flexDirection: "column", children: [
3901
+ /* @__PURE__ */ jsxs12(Box10, { flexDirection: "column", marginBottom: 1, children: [
3902
+ /* @__PURE__ */ jsxs12(Text12, { bold: true, children: [
3565
3903
  "\u25B8 ",
3566
3904
  template.name
3567
3905
  ] }),
3568
- template.description && /* @__PURE__ */ jsxs11(Text11, { color: "gray", children: [
3906
+ template.description && /* @__PURE__ */ jsxs12(Text12, { color: "gray", children: [
3569
3907
  " ",
3570
3908
  template.description
3571
3909
  ] })
3572
3910
  ] }),
3573
- options.dryRun && phase === "done" && /* @__PURE__ */ jsxs11(Box9, { flexDirection: "column", children: [
3574
- /* @__PURE__ */ jsx11(Text11, { color: "yellow", children: "(dry-run) Showing execution plan only" }),
3575
- template.sudo && /* @__PURE__ */ jsxs11(Text11, { color: "yellow", children: [
3911
+ options.dryRun && phase === "done" && /* @__PURE__ */ jsxs12(Box10, { flexDirection: "column", children: [
3912
+ /* @__PURE__ */ jsx12(Text12, { color: "yellow", children: "(dry-run) Showing execution plan only" }),
3913
+ template.sudo && /* @__PURE__ */ jsxs12(Text12, { color: "yellow", children: [
3576
3914
  " ",
3577
3915
  "\u26A0 This template requires sudo privileges (will prompt on actual run)"
3578
3916
  ] }),
3579
- /* @__PURE__ */ jsx11(Box9, { flexDirection: "column", marginTop: 1, children: template.steps.map((step, idx) => /* @__PURE__ */ jsxs11(Box9, { flexDirection: "column", marginBottom: 1, children: [
3580
- /* @__PURE__ */ jsxs11(Text11, { children: [
3917
+ /* @__PURE__ */ jsx12(Box10, { flexDirection: "column", marginTop: 1, children: template.steps.map((step, idx) => /* @__PURE__ */ jsxs12(Box10, { flexDirection: "column", marginBottom: 1, children: [
3918
+ /* @__PURE__ */ jsxs12(Text12, { children: [
3581
3919
  " ",
3582
- /* @__PURE__ */ jsxs11(Text11, { bold: true, children: [
3920
+ /* @__PURE__ */ jsxs12(Text12, { bold: true, children: [
3583
3921
  "Step ",
3584
3922
  idx + 1,
3585
3923
  "/",
@@ -3588,18 +3926,18 @@ var ProvisionView = ({
3588
3926
  " ",
3589
3927
  step.name
3590
3928
  ] }),
3591
- step.description && /* @__PURE__ */ jsxs11(Text11, { color: "gray", children: [
3929
+ step.description && /* @__PURE__ */ jsxs12(Text12, { color: "gray", children: [
3592
3930
  " ",
3593
3931
  step.description
3594
3932
  ] }),
3595
- step.skip_if && /* @__PURE__ */ jsxs11(Text11, { color: "blue", children: [
3933
+ step.skip_if && /* @__PURE__ */ jsxs12(Text12, { color: "blue", children: [
3596
3934
  " ",
3597
3935
  "Skip condition: ",
3598
3936
  step.skip_if
3599
3937
  ] })
3600
3938
  ] }, idx)) })
3601
3939
  ] }),
3602
- (phase === "running" || phase === "done" && !options.dryRun) && /* @__PURE__ */ jsx11(
3940
+ (phase === "running" || phase === "done" && !options.dryRun) && /* @__PURE__ */ jsx12(
3603
3941
  StepRunner,
3604
3942
  {
3605
3943
  steps,
@@ -3607,34 +3945,34 @@ var ProvisionView = ({
3607
3945
  total: template.steps.length
3608
3946
  }
3609
3947
  ),
3610
- phase === "done" && !options.dryRun && /* @__PURE__ */ jsxs11(Box9, { flexDirection: "column", marginTop: 1, children: [
3611
- /* @__PURE__ */ jsxs11(Text11, { color: "gray", children: [
3948
+ phase === "done" && !options.dryRun && /* @__PURE__ */ jsxs12(Box10, { flexDirection: "column", marginTop: 1, children: [
3949
+ /* @__PURE__ */ jsxs12(Text12, { color: "gray", children: [
3612
3950
  " ",
3613
3951
  "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
3614
3952
  ] }),
3615
- /* @__PURE__ */ jsxs11(Text11, { children: [
3953
+ /* @__PURE__ */ jsxs12(Text12, { children: [
3616
3954
  " ",
3617
3955
  "Result: ",
3618
- /* @__PURE__ */ jsxs11(Text11, { color: "green", children: [
3956
+ /* @__PURE__ */ jsxs12(Text12, { color: "green", children: [
3619
3957
  successCount,
3620
3958
  " succeeded"
3621
3959
  ] }),
3622
3960
  " \xB7",
3623
3961
  " ",
3624
- /* @__PURE__ */ jsxs11(Text11, { color: "blue", children: [
3962
+ /* @__PURE__ */ jsxs12(Text12, { color: "blue", children: [
3625
3963
  skippedCount,
3626
3964
  " skipped"
3627
3965
  ] }),
3628
3966
  " \xB7",
3629
3967
  " ",
3630
- /* @__PURE__ */ jsxs11(Text11, { color: "red", children: [
3968
+ /* @__PURE__ */ jsxs12(Text12, { color: "red", children: [
3631
3969
  failedCount,
3632
3970
  " failed"
3633
3971
  ] })
3634
3972
  ] }),
3635
- template.backup && !options.skipRestore && /* @__PURE__ */ jsxs11(Box9, { flexDirection: "column", marginTop: 1, children: [
3636
- /* @__PURE__ */ jsx11(Text11, { bold: true, children: "\u25B8 Proceeding with config file restore..." }),
3637
- /* @__PURE__ */ jsxs11(Text11, { color: "gray", children: [
3973
+ template.backup && !options.skipRestore && /* @__PURE__ */ jsxs12(Box10, { flexDirection: "column", marginTop: 1, children: [
3974
+ /* @__PURE__ */ jsx12(Text12, { bold: true, children: "\u25B8 Proceeding with config file restore..." }),
3975
+ /* @__PURE__ */ jsxs12(Text12, { color: "gray", children: [
3638
3976
  " ",
3639
3977
  "Backup link: ",
3640
3978
  template.backup
@@ -3727,8 +4065,8 @@ function registerProvisionCommand(program2) {
3727
4065
  }
3728
4066
  return;
3729
4067
  }
3730
- const { waitUntilExit } = render7(
3731
- /* @__PURE__ */ jsx11(
4068
+ const { waitUntilExit } = render8(
4069
+ /* @__PURE__ */ jsx12(
3732
4070
  ProvisionView,
3733
4071
  {
3734
4072
  template: tmpl,
@@ -3746,21 +4084,21 @@ function registerProvisionCommand(program2) {
3746
4084
  }
3747
4085
 
3748
4086
  // src/commands/Restore.tsx
3749
- import { Box as Box10, Text as Text12, useApp as useApp7 } from "ink";
3750
- import { render as render8 } from "ink";
4087
+ import { Box as Box11, Text as Text13, useApp as useApp8 } from "ink";
4088
+ import { render as render9 } from "ink";
3751
4089
  import SelectInput2 from "ink-select-input";
3752
- import { useEffect as useEffect7, useState as useState8 } from "react";
3753
- import { jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
4090
+ import { useEffect as useEffect8, useState as useState9 } from "react";
4091
+ import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
3754
4092
  var RestoreView = ({ filename, options }) => {
3755
- const { exit } = useApp7();
3756
- const [phase, setPhase] = useState8("loading");
3757
- const [backups, setBackups] = useState8([]);
3758
- const [selectedPath, setSelectedPath] = useState8(null);
3759
- const [plan, setPlan] = useState8(null);
3760
- const [result, setResult] = useState8(null);
3761
- const [safetyDone, setSafetyDone] = useState8(false);
3762
- const [error, setError] = useState8(null);
3763
- useEffect7(() => {
4093
+ const { exit } = useApp8();
4094
+ const [phase, setPhase] = useState9("loading");
4095
+ const [backups, setBackups] = useState9([]);
4096
+ const [selectedPath, setSelectedPath] = useState9(null);
4097
+ const [plan, setPlan] = useState9(null);
4098
+ const [result, setResult] = useState9(null);
4099
+ const [safetyDone, setSafetyDone] = useState9(false);
4100
+ const [error, setError] = useState9(null);
4101
+ useEffect8(() => {
3764
4102
  (async () => {
3765
4103
  try {
3766
4104
  const config = await loadConfig();
@@ -3794,7 +4132,7 @@ var RestoreView = ({ filename, options }) => {
3794
4132
  }
3795
4133
  })();
3796
4134
  }, []);
3797
- useEffect7(() => {
4135
+ useEffect8(() => {
3798
4136
  if (phase !== "planning" || !selectedPath) return;
3799
4137
  (async () => {
3800
4138
  try {
@@ -3842,7 +4180,7 @@ var RestoreView = ({ filename, options }) => {
3842
4180
  }
3843
4181
  };
3844
4182
  if (phase === "error" || error) {
3845
- return /* @__PURE__ */ jsx12(Box10, { flexDirection: "column", children: /* @__PURE__ */ jsxs12(Text12, { color: "red", children: [
4183
+ return /* @__PURE__ */ jsx13(Box11, { flexDirection: "column", children: /* @__PURE__ */ jsxs13(Text13, { color: "red", children: [
3846
4184
  "\u2717 ",
3847
4185
  error
3848
4186
  ] }) });
@@ -3853,30 +4191,30 @@ var RestoreView = ({ filename, options }) => {
3853
4191
  }));
3854
4192
  const currentHostname = getHostname();
3855
4193
  const isRemoteBackup = plan?.metadata.hostname && plan.metadata.hostname !== currentHostname;
3856
- return /* @__PURE__ */ jsxs12(Box10, { flexDirection: "column", children: [
3857
- phase === "loading" && /* @__PURE__ */ jsx12(Text12, { children: "\u25B8 Loading backup list..." }),
3858
- phase === "selecting" && /* @__PURE__ */ jsxs12(Box10, { flexDirection: "column", children: [
3859
- /* @__PURE__ */ jsx12(Text12, { bold: true, children: "\u25B8 Select backup" }),
3860
- /* @__PURE__ */ jsx12(SelectInput2, { items: selectItems, onSelect: handleSelect })
4194
+ return /* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", children: [
4195
+ phase === "loading" && /* @__PURE__ */ jsx13(Text13, { children: "\u25B8 Loading backup list..." }),
4196
+ phase === "selecting" && /* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", children: [
4197
+ /* @__PURE__ */ jsx13(Text13, { bold: true, children: "\u25B8 Select backup" }),
4198
+ /* @__PURE__ */ jsx13(SelectInput2, { items: selectItems, onSelect: handleSelect })
3861
4199
  ] }),
3862
- (phase === "planning" || phase === "confirming" || phase === "restoring" || phase === "done" && plan) && plan && /* @__PURE__ */ jsxs12(Box10, { flexDirection: "column", children: [
3863
- /* @__PURE__ */ jsxs12(Box10, { flexDirection: "column", marginBottom: 1, children: [
3864
- /* @__PURE__ */ jsxs12(Text12, { bold: true, children: [
4200
+ (phase === "planning" || phase === "confirming" || phase === "restoring" || phase === "done" && plan) && plan && /* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", children: [
4201
+ /* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", marginBottom: 1, children: [
4202
+ /* @__PURE__ */ jsxs13(Text13, { bold: true, children: [
3865
4203
  "\u25B8 Metadata (",
3866
4204
  plan.metadata.config.filename ?? "",
3867
4205
  ")"
3868
4206
  ] }),
3869
- /* @__PURE__ */ jsxs12(Text12, { children: [
4207
+ /* @__PURE__ */ jsxs13(Text13, { children: [
3870
4208
  " ",
3871
4209
  "Host: ",
3872
4210
  plan.metadata.hostname
3873
4211
  ] }),
3874
- /* @__PURE__ */ jsxs12(Text12, { children: [
4212
+ /* @__PURE__ */ jsxs13(Text13, { children: [
3875
4213
  " ",
3876
4214
  "Created: ",
3877
4215
  plan.metadata.createdAt
3878
4216
  ] }),
3879
- /* @__PURE__ */ jsxs12(Text12, { children: [
4217
+ /* @__PURE__ */ jsxs13(Text13, { children: [
3880
4218
  " ",
3881
4219
  "Files: ",
3882
4220
  plan.metadata.summary.fileCount,
@@ -3884,15 +4222,15 @@ var RestoreView = ({ filename, options }) => {
3884
4222
  formatBytes(plan.metadata.summary.totalSize),
3885
4223
  ")"
3886
4224
  ] }),
3887
- isRemoteBackup && /* @__PURE__ */ jsxs12(Text12, { color: "yellow", children: [
4225
+ isRemoteBackup && /* @__PURE__ */ jsxs13(Text13, { color: "yellow", children: [
3888
4226
  " ",
3889
4227
  "\u26A0 This backup was created on a different machine (",
3890
4228
  plan.metadata.hostname,
3891
4229
  ")"
3892
4230
  ] })
3893
4231
  ] }),
3894
- /* @__PURE__ */ jsxs12(Box10, { flexDirection: "column", marginBottom: 1, children: [
3895
- /* @__PURE__ */ jsx12(Text12, { bold: true, children: "\u25B8 Restore plan:" }),
4232
+ /* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", marginBottom: 1, children: [
4233
+ /* @__PURE__ */ jsx13(Text13, { bold: true, children: "\u25B8 Restore plan:" }),
3896
4234
  plan.actions.map((action, idx) => {
3897
4235
  let icon;
3898
4236
  let color;
@@ -3914,45 +4252,45 @@ var RestoreView = ({ filename, options }) => {
3914
4252
  label = "(not present)";
3915
4253
  break;
3916
4254
  }
3917
- return /* @__PURE__ */ jsxs12(Text12, { children: [
4255
+ return /* @__PURE__ */ jsxs13(Text13, { children: [
3918
4256
  " ",
3919
- /* @__PURE__ */ jsx12(Text12, { color, children: icon.padEnd(8) }),
4257
+ /* @__PURE__ */ jsx13(Text13, { color, children: icon.padEnd(8) }),
3920
4258
  " ",
3921
4259
  contractTilde(action.path),
3922
4260
  " ",
3923
- /* @__PURE__ */ jsx12(Text12, { color: "gray", children: label })
4261
+ /* @__PURE__ */ jsx13(Text13, { color: "gray", children: label })
3924
4262
  ] }, idx);
3925
4263
  })
3926
4264
  ] }),
3927
- options.dryRun && phase === "done" && /* @__PURE__ */ jsx12(Text12, { color: "yellow", children: "(dry-run) No actual restore was performed" })
4265
+ options.dryRun && phase === "done" && /* @__PURE__ */ jsx13(Text13, { color: "yellow", children: "(dry-run) No actual restore was performed" })
3928
4266
  ] }),
3929
- phase === "confirming" && /* @__PURE__ */ jsx12(Box10, { flexDirection: "column", children: /* @__PURE__ */ jsx12(Confirm, { message: "Proceed with restore?", onConfirm: handleConfirm }) }),
3930
- phase === "restoring" && /* @__PURE__ */ jsxs12(Box10, { flexDirection: "column", children: [
3931
- safetyDone && /* @__PURE__ */ jsxs12(Text12, { children: [
3932
- /* @__PURE__ */ jsx12(Text12, { color: "green", children: "\u2713" }),
4267
+ phase === "confirming" && /* @__PURE__ */ jsx13(Box11, { flexDirection: "column", children: /* @__PURE__ */ jsx13(Confirm, { message: "Proceed with restore?", onConfirm: handleConfirm }) }),
4268
+ phase === "restoring" && /* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", children: [
4269
+ safetyDone && /* @__PURE__ */ jsxs13(Text13, { children: [
4270
+ /* @__PURE__ */ jsx13(Text13, { color: "green", children: "\u2713" }),
3933
4271
  " Safety backup of current files complete"
3934
4272
  ] }),
3935
- /* @__PURE__ */ jsx12(Text12, { children: "\u25B8 Restoring..." })
4273
+ /* @__PURE__ */ jsx13(Text13, { children: "\u25B8 Restoring..." })
3936
4274
  ] }),
3937
- phase === "done" && result && !options.dryRun && /* @__PURE__ */ jsxs12(Box10, { flexDirection: "column", marginTop: 1, children: [
3938
- safetyDone && /* @__PURE__ */ jsxs12(Text12, { children: [
3939
- /* @__PURE__ */ jsx12(Text12, { color: "green", children: "\u2713" }),
4275
+ phase === "done" && result && !options.dryRun && /* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", marginTop: 1, children: [
4276
+ safetyDone && /* @__PURE__ */ jsxs13(Text13, { children: [
4277
+ /* @__PURE__ */ jsx13(Text13, { color: "green", children: "\u2713" }),
3940
4278
  " Safety backup of current files complete"
3941
4279
  ] }),
3942
- /* @__PURE__ */ jsx12(Text12, { color: "green", bold: true, children: "\u2713 Restore complete" }),
3943
- /* @__PURE__ */ jsxs12(Text12, { children: [
4280
+ /* @__PURE__ */ jsx13(Text13, { color: "green", bold: true, children: "\u2713 Restore complete" }),
4281
+ /* @__PURE__ */ jsxs13(Text13, { children: [
3944
4282
  " ",
3945
4283
  "Restored: ",
3946
4284
  result.restoredFiles.length,
3947
4285
  " files"
3948
4286
  ] }),
3949
- /* @__PURE__ */ jsxs12(Text12, { children: [
4287
+ /* @__PURE__ */ jsxs13(Text13, { children: [
3950
4288
  " ",
3951
4289
  "Skipped: ",
3952
4290
  result.skippedFiles.length,
3953
4291
  " files"
3954
4292
  ] }),
3955
- result.safetyBackupPath && /* @__PURE__ */ jsxs12(Text12, { children: [
4293
+ result.safetyBackupPath && /* @__PURE__ */ jsxs13(Text13, { children: [
3956
4294
  " ",
3957
4295
  "Safety backup: ",
3958
4296
  contractTilde(result.safetyBackupPath)
@@ -4008,8 +4346,8 @@ function registerRestoreCommand(program2) {
4008
4346
  }
4009
4347
  return;
4010
4348
  }
4011
- const { waitUntilExit } = render8(
4012
- /* @__PURE__ */ jsx12(RestoreView, { filename, options: { dryRun: opts.dryRun } })
4349
+ const { waitUntilExit } = render9(
4350
+ /* @__PURE__ */ jsx13(RestoreView, { filename, options: { dryRun: opts.dryRun } })
4013
4351
  );
4014
4352
  await waitUntilExit();
4015
4353
  });
@@ -4017,12 +4355,12 @@ function registerRestoreCommand(program2) {
4017
4355
 
4018
4356
  // src/commands/Status.tsx
4019
4357
  import { readdirSync, statSync, unlinkSync as unlinkSync2 } from "fs";
4020
- import { join as join12 } from "path";
4021
- import { Box as Box11, Text as Text13, useApp as useApp8, useInput as useInput3 } from "ink";
4022
- import { render as render9 } from "ink";
4358
+ import { join as join13 } from "path";
4359
+ import { Box as Box12, Text as Text14, useApp as useApp9, useInput as useInput3 } from "ink";
4360
+ import { render as render10 } from "ink";
4023
4361
  import SelectInput3 from "ink-select-input";
4024
- import { useEffect as useEffect8, useState as useState9 } from "react";
4025
- import { jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
4362
+ import { useEffect as useEffect9, useState as useState10 } from "react";
4363
+ import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
4026
4364
  function getDirStats(dirPath) {
4027
4365
  try {
4028
4366
  const entries = readdirSync(dirPath);
@@ -4030,9 +4368,9 @@ function getDirStats(dirPath) {
4030
4368
  let count = 0;
4031
4369
  for (const entry of entries) {
4032
4370
  try {
4033
- const stat4 = statSync(join12(dirPath, entry));
4034
- if (stat4.isFile()) {
4035
- totalSize += stat4.size;
4371
+ const stat5 = statSync(join13(dirPath, entry));
4372
+ if (stat5.isFile()) {
4373
+ totalSize += stat5.size;
4036
4374
  count++;
4037
4375
  }
4038
4376
  } catch {
@@ -4044,34 +4382,34 @@ function getDirStats(dirPath) {
4044
4382
  }
4045
4383
  }
4046
4384
  var DisplayActionItem = ({ isSelected = false, label }) => {
4047
- return /* @__PURE__ */ jsx13(Text13, { bold: isSelected, children: label });
4385
+ return /* @__PURE__ */ jsx14(Text14, { bold: isSelected, children: label });
4048
4386
  };
4049
4387
  var CleanupActionItem = ({ isSelected = false, label }) => {
4050
4388
  if (label === "Cancel" || label === "Select specific backups to delete") {
4051
- return /* @__PURE__ */ jsx13(Text13, { bold: isSelected, children: label });
4389
+ return /* @__PURE__ */ jsx14(Text14, { bold: isSelected, children: label });
4052
4390
  }
4053
4391
  const parts = label.split(/\s{2,}/);
4054
4392
  if (parts.length === 2) {
4055
- return /* @__PURE__ */ jsxs13(Text13, { bold: isSelected, children: [
4393
+ return /* @__PURE__ */ jsxs14(Text14, { bold: isSelected, children: [
4056
4394
  parts[0],
4057
4395
  " ",
4058
- /* @__PURE__ */ jsx13(Text13, { dimColor: true, children: parts[1] })
4396
+ /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: parts[1] })
4059
4397
  ] });
4060
4398
  }
4061
- return /* @__PURE__ */ jsx13(Text13, { bold: isSelected, children: label });
4399
+ return /* @__PURE__ */ jsx14(Text14, { bold: isSelected, children: label });
4062
4400
  };
4063
4401
  var StatusView = ({ cleanup }) => {
4064
- const { exit } = useApp8();
4065
- const [phase, setPhase] = useState9("loading");
4066
- const [status, setStatus] = useState9(null);
4067
- const [backups, setBackups] = useState9([]);
4068
- const [cleanupAction, setCleanupAction] = useState9(null);
4069
- const [cleanupMessage, setCleanupMessage] = useState9("");
4070
- const [error, setError] = useState9(null);
4071
- const [selectedForDeletion, setSelectedForDeletion] = useState9(
4402
+ const { exit } = useApp9();
4403
+ const [phase, setPhase] = useState10("loading");
4404
+ const [status, setStatus] = useState10(null);
4405
+ const [backups, setBackups] = useState10([]);
4406
+ const [cleanupAction, setCleanupAction] = useState10(null);
4407
+ const [cleanupMessage, setCleanupMessage] = useState10("");
4408
+ const [error, setError] = useState10(null);
4409
+ const [selectedForDeletion, setSelectedForDeletion] = useState10(
4072
4410
  []
4073
4411
  );
4074
- const [backupDir, setBackupDir] = useState9(getSubDir("backups"));
4412
+ const [backupDir, setBackupDir] = useState10(getSubDir("backups"));
4075
4413
  useInput3((_input, key) => {
4076
4414
  if (!key.escape) return;
4077
4415
  if (phase === "display") {
@@ -4083,7 +4421,7 @@ var StatusView = ({ cleanup }) => {
4083
4421
  setPhase("cleanup");
4084
4422
  }
4085
4423
  });
4086
- useEffect8(() => {
4424
+ useEffect9(() => {
4087
4425
  (async () => {
4088
4426
  try {
4089
4427
  const config = await loadConfig();
@@ -4182,7 +4520,7 @@ var StatusView = ({ cleanup }) => {
4182
4520
  try {
4183
4521
  const entries = readdirSync(logsDir);
4184
4522
  for (const entry of entries) {
4185
- const logPath = join12(logsDir, entry);
4523
+ const logPath = join13(logsDir, entry);
4186
4524
  if (!isInsideDir(logPath, logsDir))
4187
4525
  throw new Error(
4188
4526
  `Refusing to delete file outside logs directory: ${logPath}`
@@ -4232,26 +4570,26 @@ var StatusView = ({ cleanup }) => {
4232
4570
  }
4233
4571
  };
4234
4572
  if (phase === "error" || error) {
4235
- return /* @__PURE__ */ jsx13(Box11, { flexDirection: "column", children: /* @__PURE__ */ jsxs13(Text13, { color: "red", children: [
4573
+ return /* @__PURE__ */ jsx14(Box12, { flexDirection: "column", children: /* @__PURE__ */ jsxs14(Text14, { color: "red", children: [
4236
4574
  "\u2717 ",
4237
4575
  error
4238
4576
  ] }) });
4239
4577
  }
4240
4578
  if (phase === "loading") {
4241
- return /* @__PURE__ */ jsx13(Text13, { color: "cyan", children: "Loading..." });
4579
+ return /* @__PURE__ */ jsx14(Text14, { color: "cyan", children: "Loading..." });
4242
4580
  }
4243
4581
  if (!status) return null;
4244
4582
  const totalCount = status.backups.count + status.templates.count + status.scripts.count + status.logs.count;
4245
4583
  const totalSize = status.backups.totalSize + status.templates.totalSize + status.scripts.totalSize + status.logs.totalSize;
4246
- const statusDisplay = /* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", children: [
4247
- /* @__PURE__ */ jsxs13(Text13, { bold: true, children: [
4584
+ const statusDisplay = /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
4585
+ /* @__PURE__ */ jsxs14(Text14, { bold: true, children: [
4248
4586
  "\u25B8 ",
4249
4587
  APP_NAME,
4250
4588
  " status \u2014 ~/.",
4251
4589
  APP_NAME,
4252
4590
  "/"
4253
4591
  ] }),
4254
- /* @__PURE__ */ jsx13(Box11, { marginLeft: 2, marginTop: 1, children: /* @__PURE__ */ jsx13(
4592
+ /* @__PURE__ */ jsx14(Box12, { marginLeft: 2, marginTop: 1, children: /* @__PURE__ */ jsx14(
4255
4593
  Table,
4256
4594
  {
4257
4595
  headers: ["Directory", "Count", "Size"],
@@ -4280,15 +4618,15 @@ var StatusView = ({ cleanup }) => {
4280
4618
  ]
4281
4619
  }
4282
4620
  ) }),
4283
- status.lastBackup && /* @__PURE__ */ jsxs13(Box11, { marginTop: 1, marginLeft: 2, flexDirection: "column", children: [
4284
- /* @__PURE__ */ jsxs13(Text13, { children: [
4621
+ status.lastBackup && /* @__PURE__ */ jsxs14(Box12, { marginTop: 1, marginLeft: 2, flexDirection: "column", children: [
4622
+ /* @__PURE__ */ jsxs14(Text14, { children: [
4285
4623
  "Latest backup: ",
4286
4624
  formatDate(status.lastBackup),
4287
4625
  " (",
4288
4626
  formatRelativeTime(status.lastBackup),
4289
4627
  ")"
4290
4628
  ] }),
4291
- status.oldestBackup && /* @__PURE__ */ jsxs13(Text13, { children: [
4629
+ status.oldestBackup && /* @__PURE__ */ jsxs14(Text14, { children: [
4292
4630
  "Oldest backup: ",
4293
4631
  formatDate(status.oldestBackup),
4294
4632
  " (",
@@ -4297,18 +4635,18 @@ var StatusView = ({ cleanup }) => {
4297
4635
  ] })
4298
4636
  ] })
4299
4637
  ] });
4300
- const escHint = (action) => /* @__PURE__ */ jsx13(Box11, { marginTop: 1, children: /* @__PURE__ */ jsxs13(Text13, { dimColor: true, children: [
4638
+ const escHint = (action) => /* @__PURE__ */ jsx14(Box12, { marginTop: 1, children: /* @__PURE__ */ jsxs14(Text14, { dimColor: true, children: [
4301
4639
  "Press ",
4302
- /* @__PURE__ */ jsx13(Text13, { bold: true, children: "ESC" }),
4640
+ /* @__PURE__ */ jsx14(Text14, { bold: true, children: "ESC" }),
4303
4641
  " to ",
4304
4642
  action
4305
4643
  ] }) });
4306
4644
  if (phase === "display") {
4307
- return /* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", children: [
4645
+ return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
4308
4646
  statusDisplay,
4309
- /* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", marginTop: 1, children: [
4310
- /* @__PURE__ */ jsx13(Text13, { bold: true, children: "\u25B8 Actions" }),
4311
- /* @__PURE__ */ jsx13(
4647
+ /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", marginTop: 1, children: [
4648
+ /* @__PURE__ */ jsx14(Text14, { bold: true, children: "\u25B8 Actions" }),
4649
+ /* @__PURE__ */ jsx14(
4312
4650
  SelectInput3,
4313
4651
  {
4314
4652
  items: [
@@ -4356,11 +4694,11 @@ var StatusView = ({ cleanup }) => {
4356
4694
  value: "cancel"
4357
4695
  }
4358
4696
  ];
4359
- return /* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", children: [
4697
+ return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
4360
4698
  statusDisplay,
4361
- /* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", marginTop: 1, children: [
4362
- /* @__PURE__ */ jsx13(Text13, { bold: true, children: "\u25B8 Cleanup options" }),
4363
- /* @__PURE__ */ jsx13(
4699
+ /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", marginTop: 1, children: [
4700
+ /* @__PURE__ */ jsx14(Text14, { bold: true, children: "\u25B8 Cleanup options" }),
4701
+ /* @__PURE__ */ jsx14(
4364
4702
  SelectInput3,
4365
4703
  {
4366
4704
  items: cleanupItems,
@@ -4386,26 +4724,26 @@ var StatusView = ({ cleanup }) => {
4386
4724
  value: "done"
4387
4725
  }
4388
4726
  ];
4389
- return /* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", children: [
4727
+ return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
4390
4728
  statusDisplay,
4391
- /* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", marginTop: 1, children: [
4392
- /* @__PURE__ */ jsx13(Text13, { bold: true, children: "\u25B8 Select backups to delete" }),
4393
- selectedForDeletion.length > 0 && /* @__PURE__ */ jsxs13(Text13, { dimColor: true, children: [
4729
+ /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", marginTop: 1, children: [
4730
+ /* @__PURE__ */ jsx14(Text14, { bold: true, children: "\u25B8 Select backups to delete" }),
4731
+ selectedForDeletion.length > 0 && /* @__PURE__ */ jsxs14(Text14, { dimColor: true, children: [
4394
4732
  " ",
4395
4733
  selectedForDeletion.length,
4396
4734
  " backup(s) selected (",
4397
4735
  formatBytes(selectedForDeletion.reduce((s, b) => s + b.size, 0)),
4398
4736
  ")"
4399
4737
  ] }),
4400
- /* @__PURE__ */ jsx13(SelectInput3, { items: selectItems, onSelect: handleSelectBackup })
4738
+ /* @__PURE__ */ jsx14(SelectInput3, { items: selectItems, onSelect: handleSelectBackup })
4401
4739
  ] }),
4402
4740
  escHint("go back")
4403
4741
  ] });
4404
4742
  }
4405
4743
  if (phase === "confirming") {
4406
- return /* @__PURE__ */ jsxs13(Box11, { flexDirection: "column", children: [
4407
- /* @__PURE__ */ jsx13(Text13, { children: cleanupMessage }),
4408
- /* @__PURE__ */ jsx13(
4744
+ return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
4745
+ /* @__PURE__ */ jsx14(Text14, { children: cleanupMessage }),
4746
+ /* @__PURE__ */ jsx14(
4409
4747
  Confirm,
4410
4748
  {
4411
4749
  message: "Proceed?",
@@ -4416,7 +4754,7 @@ var StatusView = ({ cleanup }) => {
4416
4754
  ] });
4417
4755
  }
4418
4756
  if (phase === "done") {
4419
- return /* @__PURE__ */ jsx13(Box11, { flexDirection: "column", children: /* @__PURE__ */ jsx13(Text13, { color: "green", children: "\u2713 Cleanup complete" }) });
4757
+ return /* @__PURE__ */ jsx14(Box12, { flexDirection: "column", children: /* @__PURE__ */ jsx14(Text14, { color: "green", children: "\u2713 Cleanup complete" }) });
4420
4758
  }
4421
4759
  return null;
4422
4760
  };
@@ -4450,7 +4788,93 @@ function registerStatusCommand(program2) {
4450
4788
  }
4451
4789
  return;
4452
4790
  }
4453
- const { waitUntilExit } = render9(/* @__PURE__ */ jsx13(StatusView, { cleanup: opts.cleanup }));
4791
+ const { waitUntilExit } = render10(/* @__PURE__ */ jsx14(StatusView, { cleanup: opts.cleanup }));
4792
+ await waitUntilExit();
4793
+ });
4794
+ }
4795
+
4796
+ // src/commands/Unlink.tsx
4797
+ import { Box as Box13, Text as Text15, useApp as useApp10 } from "ink";
4798
+ import { render as render11 } from "ink";
4799
+ import { useEffect as useEffect10, useState as useState11 } from "react";
4800
+ import { Fragment as Fragment3, jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
4801
+ var UnlinkView = ({ clean }) => {
4802
+ const { exit } = useApp10();
4803
+ const [phase, setPhase] = useState11("unlinking");
4804
+ const [result, setResult] = useState11(null);
4805
+ const [error, setError] = useState11(null);
4806
+ useEffect10(() => {
4807
+ (async () => {
4808
+ try {
4809
+ const unlinkResult = await unlinkSyncpoint({ clean });
4810
+ setResult(unlinkResult);
4811
+ setPhase("done");
4812
+ setTimeout(() => exit(), 100);
4813
+ } catch (err) {
4814
+ setError(err instanceof Error ? err.message : String(err));
4815
+ setPhase("error");
4816
+ setTimeout(() => exit(), 100);
4817
+ }
4818
+ })();
4819
+ }, []);
4820
+ if (phase === "error" || error) {
4821
+ return /* @__PURE__ */ jsx15(Box13, { flexDirection: "column", children: /* @__PURE__ */ jsxs15(Text15, { color: "red", children: [
4822
+ "\u2717 Unlink failed: ",
4823
+ error
4824
+ ] }) });
4825
+ }
4826
+ if (phase === "unlinking") {
4827
+ return /* @__PURE__ */ jsx15(Box13, { children: /* @__PURE__ */ jsx15(Text15, { children: "\u25B8 Restoring ~/.syncpoint from destination..." }) });
4828
+ }
4829
+ return /* @__PURE__ */ jsxs15(Box13, { flexDirection: "column", children: [
4830
+ /* @__PURE__ */ jsx15(Text15, { color: "green", bold: true, children: "\u2713 Unlink complete" }),
4831
+ result && /* @__PURE__ */ jsxs15(Fragment3, { children: [
4832
+ /* @__PURE__ */ jsxs15(Text15, { children: [
4833
+ " ",
4834
+ "Restored: ",
4835
+ contractTilde(result.appDir)
4836
+ ] }),
4837
+ /* @__PURE__ */ jsxs15(Text15, { children: [
4838
+ " ",
4839
+ "Source: ",
4840
+ contractTilde(result.targetDir)
4841
+ ] }),
4842
+ result.cleaned && /* @__PURE__ */ jsxs15(Text15, { color: "yellow", children: [
4843
+ " ",
4844
+ "Destination copy removed (--clean)"
4845
+ ] })
4846
+ ] })
4847
+ ] });
4848
+ };
4849
+ function registerUnlinkCommand(program2) {
4850
+ const cmdInfo = COMMANDS.unlink;
4851
+ const cmd = program2.command("unlink").description(cmdInfo.description);
4852
+ cmdInfo.options?.forEach((opt) => {
4853
+ cmd.option(opt.flag, opt.description);
4854
+ });
4855
+ cmd.action(async (opts) => {
4856
+ const globalOpts = program2.opts();
4857
+ const startTime = Date.now();
4858
+ if (globalOpts.json) {
4859
+ try {
4860
+ const unlinkResult = await unlinkSyncpoint({ clean: opts.clean });
4861
+ respond(
4862
+ "unlink",
4863
+ {
4864
+ appDir: unlinkResult.appDir,
4865
+ targetDir: unlinkResult.targetDir,
4866
+ cleaned: unlinkResult.cleaned
4867
+ },
4868
+ startTime,
4869
+ VERSION
4870
+ );
4871
+ } catch (error) {
4872
+ const code = classifyError(error);
4873
+ respondError("unlink", code, error.message, startTime, VERSION);
4874
+ }
4875
+ return;
4876
+ }
4877
+ const { waitUntilExit } = render11(/* @__PURE__ */ jsx15(UnlinkView, { clean: opts.clean ?? false }));
4454
4878
  await waitUntilExit();
4455
4879
  });
4456
4880
  }
@@ -4459,15 +4883,15 @@ function registerStatusCommand(program2) {
4459
4883
  import {
4460
4884
  copyFile as copyFile3,
4461
4885
  readFile as readFile6,
4462
- rename,
4463
- unlink,
4886
+ rename as rename2,
4887
+ unlink as unlink2,
4464
4888
  writeFile as writeFile5
4465
4889
  } from "fs/promises";
4466
- import { join as join14 } from "path";
4467
- import { Box as Box12, Text as Text14, useApp as useApp9 } from "ink";
4468
- import { render as render10 } from "ink";
4890
+ import { join as join15 } from "path";
4891
+ import { Box as Box14, Text as Text16, useApp as useApp11 } from "ink";
4892
+ import { render as render12 } from "ink";
4469
4893
  import Spinner3 from "ink-spinner";
4470
- import { useEffect as useEffect9, useState as useState10 } from "react";
4894
+ import { useEffect as useEffect11, useState as useState12 } from "react";
4471
4895
 
4472
4896
  // src/prompts/wizard-config.ts
4473
4897
  function generateConfigWizardPrompt(variables) {
@@ -4511,8 +4935,8 @@ ${variables.defaultConfig}
4511
4935
  init_assets();
4512
4936
 
4513
4937
  // src/utils/file-scanner.ts
4514
- import { stat as stat3 } from "fs/promises";
4515
- import { join as join13 } from "path";
4938
+ import { stat as stat4 } from "fs/promises";
4939
+ import { join as join14 } from "path";
4516
4940
  import glob from "fast-glob";
4517
4941
  var FILE_CATEGORIES = {
4518
4942
  shell: {
@@ -4591,8 +5015,8 @@ async function scanHomeDirectory(options) {
4591
5015
  const validFiles = [];
4592
5016
  for (const file of files) {
4593
5017
  try {
4594
- const fullPath = join13(homeDir, file);
4595
- await stat3(fullPath);
5018
+ const fullPath = join14(homeDir, file);
5019
+ await stat4(fullPath);
4596
5020
  validFiles.push(file);
4597
5021
  categorizedFiles.add(file);
4598
5022
  } catch {
@@ -4626,8 +5050,8 @@ async function scanHomeDirectory(options) {
4626
5050
  if (categorizedFiles.has(file)) continue;
4627
5051
  if (totalFiles >= maxFiles) break;
4628
5052
  try {
4629
- const fullPath = join13(homeDir, file);
4630
- await stat3(fullPath);
5053
+ const fullPath = join14(homeDir, file);
5054
+ await stat4(fullPath);
4631
5055
  uncategorizedFiles.push(file);
4632
5056
  totalFiles++;
4633
5057
  } catch {
@@ -4651,7 +5075,7 @@ async function scanHomeDirectory(options) {
4651
5075
  }
4652
5076
 
4653
5077
  // src/commands/Wizard.tsx
4654
- import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
5078
+ import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
4655
5079
  var MAX_RETRIES2 = 3;
4656
5080
  async function restoreBackup2(configPath) {
4657
5081
  const bakPath = `${configPath}.bak`;
@@ -4701,21 +5125,21 @@ ${formatValidationErrors(validation.errors || [])}`
4701
5125
  }
4702
5126
  }
4703
5127
  var WizardView = ({ printMode }) => {
4704
- const { exit } = useApp9();
4705
- const [phase, setPhase] = useState10("init");
4706
- const [message, setMessage] = useState10("");
4707
- const [error, setError] = useState10(null);
4708
- const [prompt, setPrompt] = useState10("");
4709
- const [sessionId, setSessionId] = useState10(void 0);
4710
- const [attemptNumber, setAttemptNumber] = useState10(1);
4711
- useEffect9(() => {
5128
+ const { exit } = useApp11();
5129
+ const [phase, setPhase] = useState12("init");
5130
+ const [message, setMessage] = useState12("");
5131
+ const [error, setError] = useState12(null);
5132
+ const [prompt, setPrompt] = useState12("");
5133
+ const [sessionId, setSessionId] = useState12(void 0);
5134
+ const [attemptNumber, setAttemptNumber] = useState12(1);
5135
+ useEffect11(() => {
4712
5136
  (async () => {
4713
5137
  try {
4714
- const configPath = join14(getAppDir(), CONFIG_FILENAME);
5138
+ const configPath = join15(getAppDir(), CONFIG_FILENAME);
4715
5139
  if (await fileExists(configPath)) {
4716
5140
  const timestamp2 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
4717
5141
  const bakPath = `${configPath}.${timestamp2}.bak`;
4718
- await rename(configPath, bakPath);
5142
+ await rename2(configPath, bakPath);
4719
5143
  setMessage(`Backed up existing config to ${bakPath}`);
4720
5144
  }
4721
5145
  setPhase("scanning");
@@ -4781,9 +5205,9 @@ var WizardView = ({ printMode }) => {
4781
5205
  await writeFile5(tmpPath, yamlContent, "utf-8");
4782
5206
  const verification = validateConfig(parseYAML(yamlContent));
4783
5207
  if (verification.valid) {
4784
- await rename(tmpPath, configPath);
5208
+ await rename2(tmpPath, configPath);
4785
5209
  } else {
4786
- await unlink(tmpPath);
5210
+ await unlink2(tmpPath);
4787
5211
  throw new Error("Final validation failed");
4788
5212
  }
4789
5213
  setPhase("done");
@@ -4822,37 +5246,37 @@ ${formatValidationErrors(validation.errors || [])}`
4822
5246
  }
4823
5247
  }
4824
5248
  if (error) {
4825
- return /* @__PURE__ */ jsx14(Box12, { flexDirection: "column", children: /* @__PURE__ */ jsxs14(Text14, { color: "red", children: [
5249
+ return /* @__PURE__ */ jsx16(Box14, { flexDirection: "column", children: /* @__PURE__ */ jsxs16(Text16, { color: "red", children: [
4826
5250
  "\u2717 ",
4827
5251
  error
4828
5252
  ] }) });
4829
5253
  }
4830
5254
  if (printMode && phase === "done") {
4831
- return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
4832
- /* @__PURE__ */ jsx14(Text14, { bold: true, children: "Config Wizard Prompt (Copy and paste to your LLM):" }),
4833
- /* @__PURE__ */ jsx14(Box12, { marginTop: 1, marginBottom: 1, children: /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: "\u2500".repeat(60) }) }),
4834
- /* @__PURE__ */ jsx14(Text14, { children: prompt }),
4835
- /* @__PURE__ */ jsx14(Box12, { marginTop: 1, marginBottom: 1, children: /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: "\u2500".repeat(60) }) }),
4836
- /* @__PURE__ */ jsx14(Text14, { dimColor: true, children: "After getting the YAML response, save it to ~/.syncpoint/config.yml" })
5255
+ return /* @__PURE__ */ jsxs16(Box14, { flexDirection: "column", children: [
5256
+ /* @__PURE__ */ jsx16(Text16, { bold: true, children: "Config Wizard Prompt (Copy and paste to your LLM):" }),
5257
+ /* @__PURE__ */ jsx16(Box14, { marginTop: 1, marginBottom: 1, children: /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: "\u2500".repeat(60) }) }),
5258
+ /* @__PURE__ */ jsx16(Text16, { children: prompt }),
5259
+ /* @__PURE__ */ jsx16(Box14, { marginTop: 1, marginBottom: 1, children: /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: "\u2500".repeat(60) }) }),
5260
+ /* @__PURE__ */ jsx16(Text16, { dimColor: true, children: "After getting the YAML response, save it to ~/.syncpoint/config.yml" })
4837
5261
  ] });
4838
5262
  }
4839
5263
  if (phase === "done") {
4840
- return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
4841
- /* @__PURE__ */ jsx14(Text14, { color: "green", children: message }),
4842
- /* @__PURE__ */ jsxs14(Box12, { marginTop: 1, children: [
4843
- /* @__PURE__ */ jsx14(Text14, { children: "Next steps:" }),
4844
- /* @__PURE__ */ jsx14(Text14, { children: " 1. Review your config: ~/.syncpoint/config.yml" }),
4845
- /* @__PURE__ */ jsx14(Text14, { children: " 2. Run: syncpoint backup" })
5264
+ return /* @__PURE__ */ jsxs16(Box14, { flexDirection: "column", children: [
5265
+ /* @__PURE__ */ jsx16(Text16, { color: "green", children: message }),
5266
+ /* @__PURE__ */ jsxs16(Box14, { marginTop: 1, children: [
5267
+ /* @__PURE__ */ jsx16(Text16, { children: "Next steps:" }),
5268
+ /* @__PURE__ */ jsx16(Text16, { children: " 1. Review your config: ~/.syncpoint/config.yml" }),
5269
+ /* @__PURE__ */ jsx16(Text16, { children: " 2. Run: syncpoint backup" })
4846
5270
  ] })
4847
5271
  ] });
4848
5272
  }
4849
- return /* @__PURE__ */ jsxs14(Box12, { flexDirection: "column", children: [
4850
- /* @__PURE__ */ jsxs14(Text14, { children: [
4851
- /* @__PURE__ */ jsx14(Text14, { color: "cyan", children: /* @__PURE__ */ jsx14(Spinner3, { type: "dots" }) }),
5273
+ return /* @__PURE__ */ jsxs16(Box14, { flexDirection: "column", children: [
5274
+ /* @__PURE__ */ jsxs16(Text16, { children: [
5275
+ /* @__PURE__ */ jsx16(Text16, { color: "cyan", children: /* @__PURE__ */ jsx16(Spinner3, { type: "dots" }) }),
4852
5276
  " ",
4853
5277
  message
4854
5278
  ] }),
4855
- attemptNumber > 1 && /* @__PURE__ */ jsxs14(Text14, { dimColor: true, children: [
5279
+ attemptNumber > 1 && /* @__PURE__ */ jsxs16(Text16, { dimColor: true, children: [
4856
5280
  "Attempt ",
4857
5281
  attemptNumber,
4858
5282
  "/",
@@ -4889,17 +5313,17 @@ function registerWizardCommand(program2) {
4889
5313
  return;
4890
5314
  }
4891
5315
  if (opts.print) {
4892
- const { waitUntilExit } = render10(/* @__PURE__ */ jsx14(WizardView, { printMode: true }));
5316
+ const { waitUntilExit } = render12(/* @__PURE__ */ jsx16(WizardView, { printMode: true }));
4893
5317
  await waitUntilExit();
4894
5318
  return;
4895
5319
  }
4896
- const configPath = join14(getAppDir(), CONFIG_FILENAME);
5320
+ const configPath = join15(getAppDir(), CONFIG_FILENAME);
4897
5321
  try {
4898
5322
  if (await fileExists(configPath)) {
4899
5323
  const timestamp2 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
4900
5324
  const bakPath = `${configPath}.${timestamp2}.bak`;
4901
5325
  console.log(`\u{1F4CB} Backing up existing config to ${bakPath}`);
4902
- await rename(configPath, bakPath);
5326
+ await rename2(configPath, bakPath);
4903
5327
  }
4904
5328
  if (!await isClaudeCodeAvailable()) {
4905
5329
  throw new Error(
@@ -4934,15 +5358,37 @@ registerCreateTemplateCommand(program);
4934
5358
  registerListCommand(program);
4935
5359
  registerMigrateCommand(program);
4936
5360
  registerStatusCommand(program);
5361
+ registerLinkCommand(program);
5362
+ registerUnlinkCommand(program);
4937
5363
  registerHelpCommand(program);
4938
5364
  if (process.argv.includes("--describe")) {
4939
5365
  const startTime = Date.now();
4940
5366
  const globalOptions = [
4941
- { flag: "--json", description: "Output structured JSON to stdout", type: "boolean" },
4942
- { flag: "--yes", description: "Skip confirmation prompts (non-interactive mode)", type: "boolean" },
4943
- { flag: "--describe", description: "Print CLI schema as JSON and exit", type: "boolean" },
4944
- { flag: "-V, --version", description: "Output the version number", type: "boolean" },
4945
- { flag: "-h, --help", description: "Display help for command", type: "boolean" }
5367
+ {
5368
+ flag: "--json",
5369
+ description: "Output structured JSON to stdout",
5370
+ type: "boolean"
5371
+ },
5372
+ {
5373
+ flag: "--yes",
5374
+ description: "Skip confirmation prompts (non-interactive mode)",
5375
+ type: "boolean"
5376
+ },
5377
+ {
5378
+ flag: "--describe",
5379
+ description: "Print CLI schema as JSON and exit",
5380
+ type: "boolean"
5381
+ },
5382
+ {
5383
+ flag: "-V, --version",
5384
+ description: "Output the version number",
5385
+ type: "boolean"
5386
+ },
5387
+ {
5388
+ flag: "-h, --help",
5389
+ description: "Display help for command",
5390
+ type: "boolean"
5391
+ }
4946
5392
  ];
4947
5393
  respond(
4948
5394
  "describe",
@@ -4960,7 +5406,13 @@ if (process.argv.includes("--describe")) {
4960
5406
  }
4961
5407
  program.parseAsync(process.argv).catch((error) => {
4962
5408
  if (process.argv.includes("--json")) {
4963
- respondError("unknown", SyncpointErrorCode.UNKNOWN, error.message, Date.now(), VERSION);
5409
+ respondError(
5410
+ "unknown",
5411
+ SyncpointErrorCode.UNKNOWN,
5412
+ error.message,
5413
+ Date.now(),
5414
+ VERSION
5415
+ );
4964
5416
  process.exit(1);
4965
5417
  }
4966
5418
  console.error("Fatal error:", error.message);