sync-worktrees 4.0.0 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  import React from "react";
2
2
  import type { AppEventEmitter } from "../utils/app-events";
3
- import type { HookContext, WorktreeStatusEntry, DivergedDirectoryInfo } from "../types";
3
+ import type { HookContext, WorktreeStatusEntry, DivergedDirectoryInfo, RepositoryListEntry, RepositoryDiskUsage } from "../types";
4
4
  export type { HookContext, WorktreeStatusEntry };
5
5
  export interface AppProps {
6
6
  events: AppEventEmitter;
@@ -9,11 +9,9 @@ export interface AppProps {
9
9
  onManualSync: () => void;
10
10
  onReload: () => void;
11
11
  onQuit: () => Promise<void>;
12
- getRepositoryList: () => Array<{
13
- index: number;
14
- name: string;
15
- repoUrl: string;
16
- }>;
12
+ maxProgressLines?: number;
13
+ getRepositoryList: () => RepositoryListEntry[];
14
+ getRepositoryDiskUsage?: (index: number) => Promise<RepositoryDiskUsage>;
17
15
  getBranchesForRepo: (index: number) => Promise<string[]>;
18
16
  getDefaultBranchForRepo: (index: number) => string;
19
17
  fetchForRepo?: (index: number) => Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/components/App.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/components/App.tsx"],"names":[],"mappings":"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"}
@@ -1,6 +1,9 @@
1
1
  import React from "react";
2
+ import type { AppSyncProgress } from "../utils/app-events";
2
3
  export interface StatusBarProps {
3
4
  status: "idle" | "syncing";
5
+ syncProgressEntries?: AppSyncProgress[];
6
+ maxProgressLines?: number;
4
7
  repositoryCount: number;
5
8
  lastSyncTime: Date | null;
6
9
  cronSchedule?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"StatusBar.d.ts","sourceRoot":"","sources":["../../src/components/StatusBar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAInD,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,IAAI,GAAG,IAAI,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,QAAA,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,cAAc,CA6EvC,CAAC;AAEF,eAAe,SAAS,CAAC"}
1
+ {"version":3,"file":"StatusBar.d.ts","sourceRoot":"","sources":["../../src/components/StatusBar.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AAInD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3D,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B,mBAAmB,CAAC,EAAE,eAAe,EAAE,CAAC;IACxC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,IAAI,GAAG,IAAI,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,QAAA,MAAM,SAAS,EAAE,KAAK,CAAC,EAAE,CAAC,cAAc,CA6GvC,CAAC;AAEF,eAAe,SAAS,CAAC"}
@@ -1,13 +1,10 @@
1
1
  import React from "react";
2
- import type { WorktreeStatusEntry, DivergedDirectoryInfo } from "../types";
2
+ import type { WorktreeStatusEntry, DivergedDirectoryInfo, RepositoryListEntry, RepositoryDiskUsage } from "../types";
3
3
  export type { WorktreeStatusEntry };
4
4
  export interface WorktreeStatusViewProps {
5
- repositories: Array<{
6
- index: number;
7
- name: string;
8
- repoUrl: string;
9
- }>;
5
+ repositories: RepositoryListEntry[];
10
6
  getWorktreeStatusForRepo: (index: number) => Promise<WorktreeStatusEntry[]>;
7
+ getRepositoryDiskUsage?: (index: number) => Promise<RepositoryDiskUsage>;
11
8
  getDivergedDirectoriesForRepo?: (index: number) => Promise<DivergedDirectoryInfo[]>;
12
9
  deleteDivergedDirectory?: (repoIndex: number, name: string) => Promise<void>;
13
10
  onClose: () => void;
@@ -1 +1 @@
1
- {"version":3,"file":"WorktreeStatusView.d.ts","sourceRoot":"","sources":["../../src/components/WorktreeStatusView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4D,MAAM,OAAO,CAAC;AAIjF,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAG3E,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAIpC,MAAM,WAAW,uBAAuB;IACtC,YAAY,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACtE,wBAAwB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAAC;IAC5E,6BAA6B,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAAC;IACpF,uBAAuB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AAsGD,QAAA,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,uBAAuB,CA2dzD,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
1
+ {"version":3,"file":"WorktreeStatusView.d.ts","sourceRoot":"","sources":["../../src/components/WorktreeStatusView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA4D,MAAM,OAAO,CAAC;AAIjF,OAAO,KAAK,EACV,mBAAmB,EACnB,qBAAqB,EACrB,mBAAmB,EACnB,mBAAmB,EACpB,MAAM,UAAU,CAAC;AAGlB,YAAY,EAAE,mBAAmB,EAAE,CAAC;AAIpC,MAAM,WAAW,uBAAuB;IACtC,YAAY,EAAE,mBAAmB,EAAE,CAAC;IACpC,wBAAwB,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAAC;IAC5E,sBAAsB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACzE,6BAA6B,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAAC;IACpF,uBAAuB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E,OAAO,EAAE,MAAM,IAAI,CAAC;CACrB;AA2GD,QAAA,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,uBAAuB,CAyhBzD,CAAC;AAEF,eAAe,kBAAkB,CAAC"}
package/dist/index.js CHANGED
@@ -826,7 +826,15 @@ import { Box as Box7, useInput as useInput6, useStdout } from "ink";
826
826
  import React, { useState, useEffect } from "react";
827
827
  import { Box, Text } from "ink";
828
828
  import { CronExpressionParser } from "cron-parser";
829
- var StatusBar = ({ status, repositoryCount, lastSyncTime, cronSchedule, diskSpaceUsed }) => {
829
+ var StatusBar = ({
830
+ status,
831
+ syncProgressEntries = [],
832
+ maxProgressLines = 2,
833
+ repositoryCount,
834
+ lastSyncTime,
835
+ cronSchedule,
836
+ diskSpaceUsed
837
+ }) => {
830
838
  const [nextSyncTime, setNextSyncTime] = useState(null);
831
839
  useEffect(() => {
832
840
  if (!cronSchedule) {
@@ -856,7 +864,17 @@ var StatusBar = ({ status, repositoryCount, lastSyncTime, cronSchedule, diskSpac
856
864
  const getStatusIcon = () => {
857
865
  return status === "syncing" ? "\u27F3" : "\u2713";
858
866
  };
859
- return /* @__PURE__ */ React.createElement(Box, { borderStyle: "single", paddingX: 1 }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", width: "100%" }, /* @__PURE__ */ React.createElement(Box, { justifyContent: "space-between" }, /* @__PURE__ */ React.createElement(Text, { bold: true }, getStatusIcon(), " Status:", " ", /* @__PURE__ */ React.createElement(Text, { color: getStatusColor() }, status === "syncing" ? "Syncing..." : "Running")), /* @__PURE__ */ React.createElement(Text, null, "Repositories: ", /* @__PURE__ */ React.createElement(Text, { bold: true, color: "cyan" }, repositoryCount))), /* @__PURE__ */ React.createElement(Box, { justifyContent: "space-between" }, /* @__PURE__ */ React.createElement(Text, null, "Last Sync: ", /* @__PURE__ */ React.createElement(Text, { color: "gray" }, formatTime(lastSyncTime))), cronSchedule && /* @__PURE__ */ React.createElement(Text, null, "Next Sync: ", /* @__PURE__ */ React.createElement(Text, { color: "gray" }, formatTime(nextSyncTime)))), /* @__PURE__ */ React.createElement(Box, { justifyContent: "space-between" }, /* @__PURE__ */ React.createElement(Text, null, "Disk Space: ", /* @__PURE__ */ React.createElement(Text, { color: "magenta" }, diskSpaceUsed || "Calculating...")), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, /* @__PURE__ */ React.createElement(Text, { color: "yellow" }, "s"), "ync", " ", /* @__PURE__ */ React.createElement(Text, { color: "yellow" }, "c"), "reate", " ", /* @__PURE__ */ React.createElement(Text, { color: "yellow" }, "o"), "pen", " ", /* @__PURE__ */ React.createElement(Text, { color: "yellow" }, "w"), "tree", " ", /* @__PURE__ */ React.createElement(Text, { color: "yellow" }, "r"), "eload", " ", /* @__PURE__ */ React.createElement(Text, { color: "yellow" }, "?"), "help", " ", /* @__PURE__ */ React.createElement(Text, { color: "yellow" }, "q"), "uit"))));
867
+ const formatProgress = (syncProgress) => {
868
+ const percent = syncProgress.progress === void 0 || syncProgress.message.includes(`${syncProgress.progress}%`) ? "" : ` ${syncProgress.progress}%`;
869
+ return `[${syncProgress.repo}] ${syncProgress.message}${percent}`;
870
+ };
871
+ const progressLineCount = Math.max(1, maxProgressLines);
872
+ const visibleProgress = syncProgressEntries.slice(-progressLineCount);
873
+ return /* @__PURE__ */ React.createElement(Box, { borderStyle: "single", paddingX: 1 }, /* @__PURE__ */ React.createElement(Box, { flexDirection: "column", width: "100%" }, /* @__PURE__ */ React.createElement(Box, { justifyContent: "space-between" }, /* @__PURE__ */ React.createElement(Text, { bold: true }, getStatusIcon(), " Status:", " ", /* @__PURE__ */ React.createElement(Text, { color: getStatusColor() }, status === "syncing" ? "Syncing..." : "Running")), /* @__PURE__ */ React.createElement(Text, null, "Repositories: ", /* @__PURE__ */ React.createElement(Text, { bold: true, color: "cyan" }, repositoryCount))), /* @__PURE__ */ React.createElement(Box, { justifyContent: "space-between" }, /* @__PURE__ */ React.createElement(Text, null, "Last Sync: ", /* @__PURE__ */ React.createElement(Text, { color: "gray" }, formatTime(lastSyncTime))), cronSchedule && /* @__PURE__ */ React.createElement(Text, null, "Next Sync: ", /* @__PURE__ */ React.createElement(Text, { color: "gray" }, formatTime(nextSyncTime)))), status === "syncing" && Array.from({ length: progressLineCount }).map((_, index) => {
874
+ const entry = visibleProgress[index];
875
+ const message = entry ? formatProgress(entry) : index === 0 ? "waiting for progress events" : "";
876
+ return /* @__PURE__ */ React.createElement(Box, { key: index }, /* @__PURE__ */ React.createElement(Text, { wrap: "truncate" }, message ? "Progress: " : " ", message && /* @__PURE__ */ React.createElement(Text, { color: "cyan" }, message)));
877
+ }), /* @__PURE__ */ React.createElement(Box, { justifyContent: "space-between" }, /* @__PURE__ */ React.createElement(Text, null, "Disk Space: ", /* @__PURE__ */ React.createElement(Text, { color: "magenta" }, diskSpaceUsed || "Calculating...")), /* @__PURE__ */ React.createElement(Text, { dimColor: true }, /* @__PURE__ */ React.createElement(Text, { color: "yellow" }, "s"), "ync", " ", /* @__PURE__ */ React.createElement(Text, { color: "yellow" }, "c"), "reate", " ", /* @__PURE__ */ React.createElement(Text, { color: "yellow" }, "o"), "pen", " ", /* @__PURE__ */ React.createElement(Text, { color: "yellow" }, "w"), "tree", " ", /* @__PURE__ */ React.createElement(Text, { color: "yellow" }, "r"), "eload", " ", /* @__PURE__ */ React.createElement(Text, { color: "yellow" }, "?"), "help", " ", /* @__PURE__ */ React.createElement(Text, { color: "yellow" }, "q"), "uit"))));
860
878
  };
861
879
  var StatusBar_default = StatusBar;
862
880
 
@@ -1526,6 +1544,7 @@ var formatDivergedDate = (dateStr) => {
1526
1544
  var WorktreeStatusView = ({
1527
1545
  repositories,
1528
1546
  getWorktreeStatusForRepo,
1547
+ getRepositoryDiskUsage,
1529
1548
  getDivergedDirectoriesForRepo,
1530
1549
  deleteDivergedDirectory,
1531
1550
  onClose
@@ -1540,6 +1559,8 @@ var WorktreeStatusView = ({
1540
1559
  const [entryFilter, setEntryFilter] = useState4("");
1541
1560
  const [expandedEntry, setExpandedEntry] = useState4(null);
1542
1561
  const [loading, setLoading] = useState4(false);
1562
+ const [repoDiskUsage, setRepoDiskUsage] = useState4({});
1563
+ const requestedDiskUsageRef = useRef3(/* @__PURE__ */ new Set());
1543
1564
  const [confirmDelete, setConfirmDelete] = useState4(null);
1544
1565
  const [deleting, setDeleting] = useState4(false);
1545
1566
  const [error, setError] = useState4(null);
@@ -1595,6 +1616,29 @@ var WorktreeStatusView = ({
1595
1616
  },
1596
1617
  [getWorktreeStatusForRepo, getDivergedDirectoriesForRepo]
1597
1618
  );
1619
+ useEffect4(() => {
1620
+ if (!getRepositoryDiskUsage) return void 0;
1621
+ let cancelled = false;
1622
+ const indexesToLoad = repositories.map((repo) => repo.index).filter((repoIndex) => !requestedDiskUsageRef.current.has(repoIndex));
1623
+ if (indexesToLoad.length === 0) return void 0;
1624
+ for (const repoIndex of indexesToLoad) {
1625
+ requestedDiskUsageRef.current.add(repoIndex);
1626
+ setRepoDiskUsage((prev) => ({ ...prev, [repoIndex]: { status: "loading" } }));
1627
+ void getRepositoryDiskUsage(repoIndex).then((usage) => {
1628
+ if (cancelled) return;
1629
+ setRepoDiskUsage((prev) => ({ ...prev, [repoIndex]: { status: "ready", usage } }));
1630
+ }).catch(() => {
1631
+ if (cancelled) return;
1632
+ setRepoDiskUsage((prev) => ({
1633
+ ...prev,
1634
+ [repoIndex]: { status: "error" }
1635
+ }));
1636
+ });
1637
+ }
1638
+ return () => {
1639
+ cancelled = true;
1640
+ };
1641
+ }, [repositories, getRepositoryDiskUsage]);
1598
1642
  useEffect4(() => {
1599
1643
  if (step === "VIEW_STATUS" && entries.length === 0 && !loading && selectedRepoIndexRef.current >= 0) {
1600
1644
  loadStatus(selectedRepoIndexRef.current);
@@ -1668,11 +1712,11 @@ var WorktreeStatusView = ({
1668
1712
  } else if (key.downArrow) {
1669
1713
  setSelectedProjectIndex((prev) => Math.min(filteredProjects.length - 1, prev + 1));
1670
1714
  } else if (key.return && filteredProjects.length > 0) {
1671
- const selectedRepo = filteredProjects[selectedProjectIndex];
1672
- if (selectedRepo) {
1673
- selectedRepoIndexRef.current = selectedRepo.index;
1715
+ const selectedRepo2 = filteredProjects[selectedProjectIndex];
1716
+ if (selectedRepo2) {
1717
+ selectedRepoIndexRef.current = selectedRepo2.index;
1674
1718
  setStep("VIEW_STATUS");
1675
- loadStatus(selectedRepo.index);
1719
+ loadStatus(selectedRepo2.index);
1676
1720
  }
1677
1721
  } else if (key.backspace || key.delete) {
1678
1722
  setProjectFilter((prev) => prev.slice(0, -1));
@@ -1720,7 +1764,7 @@ var WorktreeStatusView = ({
1720
1764
  return /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column", gap: 1 }, /* @__PURE__ */ React5.createElement(Text5, null, "Select repository:"), /* @__PURE__ */ React5.createElement(Box5, null, /* @__PURE__ */ React5.createElement(Text5, null, "Filter: "), /* @__PURE__ */ React5.createElement(Text5, { color: "cyan" }, projectFilter || "_"), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " ", "(", filteredProjects.length, "/", repositories.length, " matches)")), /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column" }, filteredProjects.length === 0 ? /* @__PURE__ */ React5.createElement(Text5, { color: "yellow" }, "No matches") : /* @__PURE__ */ React5.createElement(React5.Fragment, null, startIdx > 0 && /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " ..."), visibleProjects.map((repo, idx) => {
1721
1765
  const actualIdx = startIdx + idx;
1722
1766
  const isSelected = actualIdx === selectedProjectIndex;
1723
- return /* @__PURE__ */ React5.createElement(Box5, { key: repo.index }, /* @__PURE__ */ React5.createElement(Text5, { color: isSelected ? "cyan" : void 0 }, isSelected ? "> " : " ", repo.name));
1767
+ return /* @__PURE__ */ React5.createElement(Box5, { key: repo.index }, /* @__PURE__ */ React5.createElement(Text5, { color: isSelected ? "cyan" : void 0 }, isSelected ? "> " : " "), /* @__PURE__ */ React5.createElement(Box5, { width: 38 }, /* @__PURE__ */ React5.createElement(Text5, { color: isSelected ? "cyan" : void 0 }, repo.name)), getRepositoryDiskUsage && /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " "), renderRepositoryDiskUsage(repo.index));
1724
1768
  }), endIdx < filteredProjects.length && /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " ..."))));
1725
1769
  };
1726
1770
  const renderDetailPanel = (entry) => {
@@ -1731,6 +1775,17 @@ var WorktreeStatusView = ({
1731
1775
  const renderDivergedDetailPanel = (entry) => {
1732
1776
  return /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column", marginLeft: 4, marginTop: 0, marginBottom: 1 }, /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "Path: ", entry.path), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " Original branch: ", entry.originalBranch), entry.divergedAt && /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " Diverged: ", entry.divergedAt), /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " Size: ", entry.sizeFormatted));
1733
1777
  };
1778
+ const renderRepositoryDiskUsage = (repoIndex) => {
1779
+ if (!getRepositoryDiskUsage) return null;
1780
+ const state = repoDiskUsage[repoIndex];
1781
+ if (!state || state.status === "loading") {
1782
+ return /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "Size: calculating...");
1783
+ }
1784
+ if (state.status === "error") {
1785
+ return /* @__PURE__ */ React5.createElement(Text5, { color: "red" }, "Size: N/A");
1786
+ }
1787
+ return /* @__PURE__ */ React5.createElement(Text5, null, "Size: ", /* @__PURE__ */ React5.createElement(Text5, { color: "magenta" }, state.usage.sizeFormatted));
1788
+ };
1734
1789
  const renderStatusList = () => {
1735
1790
  if (loading) {
1736
1791
  return /* @__PURE__ */ React5.createElement(Text5, { color: "yellow" }, "Loading worktree status...");
@@ -1784,7 +1839,8 @@ var WorktreeStatusView = ({
1784
1839
  }
1785
1840
  return /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, step === "VIEW_STATUS" ? isDivergedSelected ? "\u2191/\u2193 navigate \u2022 Type to filter \u2022 Enter to expand \u2022 d to delete \u2022 ESC to close" : "\u2191/\u2193 navigate \u2022 Type to filter \u2022 Enter to expand \u2022 ESC to close" : "\u2191/\u2193 navigate \u2022 Type to filter \u2022 Enter to select \u2022 ESC to cancel");
1786
1841
  };
1787
- return /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column", marginTop: 1, marginBottom: 1 }, /* @__PURE__ */ React5.createElement(Box5, { borderStyle: "round", borderColor: "green", paddingX: 2, paddingY: 1, flexDirection: "column", width: 70 }, /* @__PURE__ */ React5.createElement(Box5, { marginBottom: 1 }, /* @__PURE__ */ React5.createElement(Text5, { bold: true, color: "green" }, "\u{1F4CA} Worktree Status", " ", step !== "ERROR" && /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "(Step ", getStepNumber(), "/", getTotalSteps(), ")"))), repositories.length > 1 && step === "VIEW_STATUS" && !loading && selectedRepoIndexRef.current >= 0 && /* @__PURE__ */ React5.createElement(Box5, { marginBottom: 1 }, /* @__PURE__ */ React5.createElement(Text5, null, "Repository: ", /* @__PURE__ */ React5.createElement(Text5, { color: "cyan" }, repositories.find((r) => r.index === selectedRepoIndexRef.current)?.name))), renderContent(), /* @__PURE__ */ React5.createElement(Box5, { marginTop: 1 }, renderFooter())));
1842
+ const selectedRepo = selectedRepoIndexRef.current >= 0 ? repositories.find((repo) => repo.index === selectedRepoIndexRef.current) : void 0;
1843
+ return /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column", marginTop: 1, marginBottom: 1 }, /* @__PURE__ */ React5.createElement(Box5, { borderStyle: "round", borderColor: "green", paddingX: 2, paddingY: 1, flexDirection: "column", width: 70 }, /* @__PURE__ */ React5.createElement(Box5, { marginBottom: 1 }, /* @__PURE__ */ React5.createElement(Text5, { bold: true, color: "green" }, "\u{1F4CA} Worktree Status", " ", step !== "ERROR" && /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, "(Step ", getStepNumber(), "/", getTotalSteps(), ")"))), step === "VIEW_STATUS" && selectedRepo && /* @__PURE__ */ React5.createElement(Box5, { marginBottom: 1 }, /* @__PURE__ */ React5.createElement(Text5, null, "Repository: ", /* @__PURE__ */ React5.createElement(Text5, { color: "cyan" }, selectedRepo.name)), getRepositoryDiskUsage && /* @__PURE__ */ React5.createElement(Text5, { dimColor: true }, " "), renderRepositoryDiskUsage(selectedRepo.index)), renderContent(), /* @__PURE__ */ React5.createElement(Box5, { marginTop: 1 }, renderFooter())));
1788
1844
  };
1789
1845
  var WorktreeStatusView_default = WorktreeStatusView;
1790
1846
 
@@ -1893,7 +1949,9 @@ var App = ({
1893
1949
  onManualSync,
1894
1950
  onReload,
1895
1951
  onQuit,
1952
+ maxProgressLines = 2,
1896
1953
  getRepositoryList,
1954
+ getRepositoryDiskUsage,
1897
1955
  getBranchesForRepo,
1898
1956
  getDefaultBranchForRepo,
1899
1957
  fetchForRepo,
@@ -1913,6 +1971,7 @@ var App = ({
1913
1971
  const [showOpenEditorWizard, setShowOpenEditorWizard] = useState6(false);
1914
1972
  const [showWorktreeStatus, setShowWorktreeStatus] = useState6(false);
1915
1973
  const [status, setStatus] = useState6("idle");
1974
+ const [syncProgressEntries, setSyncProgressEntries] = useState6([]);
1916
1975
  const [lastSyncTime, setLastSyncTime] = useState6(null);
1917
1976
  const [diskSpaceUsed, setDiskSpaceUsed] = useState6(null);
1918
1977
  const [logs, setLogs] = useState6([]);
@@ -1983,15 +2042,36 @@ var App = ({
1983
2042
  const updateLastSyncTime = useCallback4(() => {
1984
2043
  setLastSyncTime(/* @__PURE__ */ new Date());
1985
2044
  setStatus("idle");
2045
+ setSyncProgressEntries([]);
1986
2046
  }, []);
1987
2047
  useEffect6(() => {
1988
2048
  const unsubscribers = [
1989
2049
  events.on("updateLastSyncTime", () => {
1990
2050
  setLastSyncTime(/* @__PURE__ */ new Date());
1991
2051
  setStatus("idle");
2052
+ setSyncProgressEntries([]);
1992
2053
  }),
1993
2054
  events.on("setStatus", (newStatus) => {
1994
2055
  setStatus(newStatus);
2056
+ if (newStatus === "idle") {
2057
+ setSyncProgressEntries([]);
2058
+ }
2059
+ }),
2060
+ events.on("setSyncProgress", (progress) => {
2061
+ if (progress === null) {
2062
+ setSyncProgressEntries([]);
2063
+ return;
2064
+ }
2065
+ setSyncProgressEntries((prev) => {
2066
+ if (progress.completed) {
2067
+ return prev.filter((entry) => entry.repo !== progress.repo);
2068
+ }
2069
+ const existingIndex = prev.findIndex((entry) => entry.repo === progress.repo);
2070
+ if (existingIndex === -1) {
2071
+ return [...prev, progress];
2072
+ }
2073
+ return prev.map((entry, index) => index === existingIndex ? progress : entry);
2074
+ });
1995
2075
  }),
1996
2076
  events.on("setDiskSpace", (diskSpace) => {
1997
2077
  setDiskSpaceUsed(diskSpace);
@@ -2011,7 +2091,8 @@ var App = ({
2011
2091
  unsubscribers.forEach((unsub) => unsub());
2012
2092
  };
2013
2093
  }, []);
2014
- const statusBarHeight = 5;
2094
+ const progressLineCount = status === "syncing" ? Math.max(1, maxProgressLines) : 0;
2095
+ const statusBarHeight = 5 + progressLineCount;
2015
2096
  const terminalRows = stdout.rows ?? 24;
2016
2097
  const logPanelHeight = Math.max(5, terminalRows - statusBarHeight);
2017
2098
  const showModal = showHelp || showBranchWizard || showOpenEditorWizard || showWorktreeStatus;
@@ -2076,6 +2157,7 @@ var App = ({
2076
2157
  {
2077
2158
  repositories: getRepositoryList(),
2078
2159
  getWorktreeStatusForRepo,
2160
+ getRepositoryDiskUsage,
2079
2161
  getDivergedDirectoriesForRepo,
2080
2162
  deleteDivergedDirectory,
2081
2163
  onClose: () => setShowWorktreeStatus(false)
@@ -2084,6 +2166,8 @@ var App = ({
2084
2166
  StatusBar_default,
2085
2167
  {
2086
2168
  status,
2169
+ syncProgressEntries,
2170
+ maxProgressLines,
2087
2171
  repositoryCount: repoCount,
2088
2172
  lastSyncTime,
2089
2173
  cronSchedule: schedule2,
@@ -2287,7 +2371,7 @@ function makeGitProgressHandler(logger, emitProgress) {
2287
2371
  lastBucket.set(key, bucket);
2288
2372
  const total = event.total > 0 ? `${event.processed}/${event.total}` : `${event.processed}`;
2289
2373
  const message = `${event.method} ${event.stage}: ${event.progress}% (${total})`;
2290
- logger.info(` \u21B3 ${message}`);
2374
+ logger.debug(` \u21B3 ${message}`);
2291
2375
  emitProgress?.({
2292
2376
  phase: event.method,
2293
2377
  message,
@@ -4022,8 +4106,9 @@ function sanitizeGitEnv(env) {
4022
4106
  return sanitized;
4023
4107
  }
4024
4108
  var GitService = class {
4025
- constructor(config, logger) {
4109
+ constructor(config, logger, progressEmitter) {
4026
4110
  this.config = config;
4111
+ this.progressEmitter = progressEmitter;
4027
4112
  this.logger = logger ?? Logger.createDefault(void 0, config.debug);
4028
4113
  this.bareRepoPath = this.config.bareRepoDir || getDefaultBareRepoDir(this.config.repoUrl);
4029
4114
  this.mainWorktreePath = path8.join(this.config.worktreeDir, GIT_CONSTANTS.DEFAULT_BRANCH);
@@ -4064,7 +4149,9 @@ var GitService = class {
4064
4149
  return git;
4065
4150
  }
4066
4151
  buildSimpleGitOptions(blockMs) {
4067
- const options = { progress: makeGitProgressHandler(this.logger) };
4152
+ const options = {
4153
+ progress: makeGitProgressHandler(this.logger, (event) => this.progressEmitter?.(event))
4154
+ };
4068
4155
  if (blockMs > 0) options.timeout = { block: blockMs };
4069
4156
  return options;
4070
4157
  }
@@ -5884,7 +5971,7 @@ var WorktreeSyncService = class {
5884
5971
  constructor(config) {
5885
5972
  this.config = config;
5886
5973
  this.logger = config.logger ?? Logger.createDefault(void 0, config.debug);
5887
- this.gitService = new GitService(config, this.logger);
5974
+ this.gitService = new GitService(config, this.logger, (event) => this.emitProgress(event));
5888
5975
  this.repoOperationLock = new RepoOperationLock(config, this.gitService, this.logger);
5889
5976
  this.retryPolicy = new SyncRetryPolicy(config, this.gitService, this.logger);
5890
5977
  this.worktreeModeSyncRunner = new WorktreeModeSyncRunner(
@@ -6288,11 +6375,13 @@ var InteractiveUIService = class {
6288
6375
  branchCreatedActions = new BranchCreatedActionsService();
6289
6376
  pathResolution = new PathResolutionService();
6290
6377
  limit;
6378
+ maxProgressLines;
6291
6379
  reloadInProgress = false;
6292
6380
  isDestroyed = false;
6293
6381
  events;
6294
6382
  ownsEvents;
6295
6383
  unsubscribeCallbacks = [];
6384
+ progressUnsubscribers = [];
6296
6385
  constructor(syncServices, configPath, cronSchedule, maxParallel, events) {
6297
6386
  this.ownsEvents = events === void 0;
6298
6387
  this.events = events ?? new AppEventEmitter();
@@ -6303,9 +6392,11 @@ var InteractiveUIService = class {
6303
6392
  this.configPath = configPath;
6304
6393
  this.cronSchedule = cronSchedule;
6305
6394
  this.repositoryCount = syncServices.length;
6306
- this.limit = pLimit2(maxParallel ?? DEFAULT_CONFIG.PARALLELISM.MAX_REPOSITORIES);
6395
+ this.maxProgressLines = Math.max(1, maxParallel ?? DEFAULT_CONFIG.PARALLELISM.MAX_REPOSITORIES);
6396
+ this.limit = pLimit2(this.maxProgressLines);
6307
6397
  this.startBufferFlushCheck();
6308
6398
  this.renderUI();
6399
+ this.subscribeToServiceProgress();
6309
6400
  this.injectLoggersIntoServices();
6310
6401
  setTimeout(() => {
6311
6402
  this.addLog("\u{1F680} sync-worktrees UI initialized", "info");
@@ -6343,6 +6434,26 @@ var InteractiveUIService = class {
6343
6434
  );
6344
6435
  }
6345
6436
  }
6437
+ subscribeToServiceProgress() {
6438
+ for (const unsubscribe of this.progressUnsubscribers) {
6439
+ unsubscribe();
6440
+ }
6441
+ this.progressUnsubscribers = this.syncServices.map((service, index) => {
6442
+ const repoName = this.getRepoName(index);
6443
+ if (!service.onProgress) return () => void 0;
6444
+ return service.onProgress((event) => {
6445
+ if (this.isDestroyed) return;
6446
+ this.events.emit("setSyncProgress", {
6447
+ repo: repoName,
6448
+ phase: event.phase,
6449
+ message: event.message,
6450
+ progress: event.progress,
6451
+ processed: event.processed,
6452
+ total: event.total
6453
+ });
6454
+ });
6455
+ });
6456
+ }
6346
6457
  addLog(message, level = "info") {
6347
6458
  if (this.isDestroyed) return;
6348
6459
  if (this.uiReady) {
@@ -6395,6 +6506,7 @@ var InteractiveUIService = class {
6395
6506
  events: this.events,
6396
6507
  repositoryCount: this.repositoryCount,
6397
6508
  cronSchedule: this.cronSchedule,
6509
+ maxProgressLines: this.maxProgressLines,
6398
6510
  onManualSync: () => this.handleManualSync(),
6399
6511
  onReload: () => this.handleReload(),
6400
6512
  onQuit: () => this.handleQuit(),
@@ -6405,6 +6517,7 @@ var InteractiveUIService = class {
6405
6517
  createAndPushBranch: (repoIndex, baseBranch, branchName) => this.createAndPushBranch(repoIndex, baseBranch, branchName),
6406
6518
  getWorktreesForRepo: (index) => this.getWorktreesForRepo(index),
6407
6519
  getWorktreeStatusForRepo: (index) => this.getWorktreeStatusForRepo(index),
6520
+ getRepositoryDiskUsage: (index) => this.getRepositoryDiskUsage(index),
6408
6521
  getDivergedDirectoriesForRepo: (index) => this.getDivergedDirectoriesForRepo(index),
6409
6522
  deleteDivergedDirectory: (repoIndex, name) => this.deleteDivergedDirectory(repoIndex, name),
6410
6523
  openEditorInWorktree: (path18) => this.openEditorInWorktree(path18),
@@ -6470,6 +6583,7 @@ var InteractiveUIService = class {
6470
6583
  cronJobsCancelled = true;
6471
6584
  this.syncServices = newServices;
6472
6585
  this.repositoryCount = this.syncServices.length;
6586
+ this.subscribeToServiceProgress();
6473
6587
  this.injectLoggersIntoServices();
6474
6588
  const uniqueSchedules = [...new Set(this.syncServices.map((s) => s.config.cronSchedule))];
6475
6589
  this.cronSchedule = uniqueSchedules.length === 1 ? uniqueSchedules[0] : void 0;
@@ -6546,6 +6660,9 @@ var InteractiveUIService = class {
6546
6660
  setStatus(status) {
6547
6661
  if (this.isDestroyed) return;
6548
6662
  this.events.emit("setStatus", status);
6663
+ if (status === "idle") {
6664
+ this.events.emit("setSyncProgress", null);
6665
+ }
6549
6666
  }
6550
6667
  setDiskSpace(diskSpace) {
6551
6668
  if (this.isDestroyed) return;
@@ -6575,6 +6692,45 @@ var InteractiveUIService = class {
6575
6692
  const service = this.syncServices[index];
6576
6693
  return service.config.name || `repo-${index}`;
6577
6694
  }
6695
+ async getRepositoryDiskUsage(repoIndex) {
6696
+ if (repoIndex < 0 || repoIndex >= this.syncServices.length) {
6697
+ throw new Error(`Invalid repository index: ${repoIndex}`);
6698
+ }
6699
+ const service = this.syncServices[repoIndex];
6700
+ const config = service.config;
6701
+ const repoName = this.getRepoName(repoIndex);
6702
+ const mode = resolveMode(config);
6703
+ const sizeTargets = [
6704
+ ...mode === "worktree" ? [{ kind: "bare", path: config.bareRepoDir || getDefaultBareRepoDir(config.repoUrl) }] : [],
6705
+ { kind: "worktree", path: config.worktreeDir }
6706
+ ];
6707
+ let bareSizeBytes = 0;
6708
+ let worktreeSizeBytes = 0;
6709
+ const errors = [];
6710
+ for (const target of sizeTargets) {
6711
+ try {
6712
+ const size = await calculateDirectorySize(target.path);
6713
+ if (target.kind === "bare") {
6714
+ bareSizeBytes = size;
6715
+ } else {
6716
+ worktreeSizeBytes = size;
6717
+ }
6718
+ } catch (error) {
6719
+ errors.push(`${target.path}: ${error instanceof Error ? error.message : String(error)}`);
6720
+ }
6721
+ }
6722
+ const sizeBytes = bareSizeBytes + worktreeSizeBytes;
6723
+ const failedAllPaths = errors.length === sizeTargets.length;
6724
+ return {
6725
+ repoIndex,
6726
+ repoName,
6727
+ sizeBytes: failedAllPaths ? null : sizeBytes,
6728
+ sizeFormatted: failedAllPaths ? "N/A" : formatBytes(sizeBytes),
6729
+ bareSizeBytes,
6730
+ worktreeSizeBytes,
6731
+ error: errors.length > 0 ? errors.join("; ") : void 0
6732
+ };
6733
+ }
6578
6734
  async getBranchesForRepo(repoIndex) {
6579
6735
  if (repoIndex < 0 || repoIndex >= this.syncServices.length) {
6580
6736
  throw new Error(`Invalid repository index: ${repoIndex}`);
@@ -6870,19 +7026,28 @@ var InteractiveUIService = class {
6870
7026
  }
6871
7027
  async runSyncServices(services) {
6872
7028
  const syncResults = await Promise.allSettled(
6873
- services.map(
6874
- (service) => this.limit(async () => {
7029
+ services.map((service) => {
7030
+ const repoName = service.config.name || service.config.repoUrl;
7031
+ return this.limit(async () => {
6875
7032
  service.clearRecordedSkips();
6876
- if (!service.isInitialized()) {
6877
- await service.initialize();
7033
+ try {
7034
+ if (!service.isInitialized()) {
7035
+ await service.initialize();
7036
+ }
7037
+ const result = await service.sync();
7038
+ return { service, result };
7039
+ } finally {
7040
+ this.events.emit("setSyncProgress", {
7041
+ repo: repoName,
7042
+ phase: "complete",
7043
+ message: "Finished",
7044
+ completed: true
7045
+ });
6878
7046
  }
6879
- const result = await service.sync();
6880
- return { service, result };
6881
7047
  }).catch((error) => {
6882
- const repoName = service.config.name || service.config.repoUrl;
6883
7048
  throw Object.assign(error instanceof Error ? error : new Error(String(error)), { repoName });
6884
- })
6885
- )
7049
+ });
7050
+ })
6886
7051
  );
6887
7052
  const failures = [];
6888
7053
  const skipped = [];
@@ -6977,6 +7142,10 @@ var InteractiveUIService = class {
6977
7142
  unsubscribe();
6978
7143
  }
6979
7144
  this.unsubscribeCallbacks = [];
7145
+ for (const unsubscribe of this.progressUnsubscribers) {
7146
+ unsubscribe();
7147
+ }
7148
+ this.progressUnsubscribers = [];
6980
7149
  if (this.ownsEvents) {
6981
7150
  this.events.removeAllListeners();
6982
7151
  }
@@ -7106,11 +7275,14 @@ async function generateConfigFile(input2, configPath, options = {}) {
7106
7275
  const useRelativeBare = !bareRepoDirRelative.startsWith("../../../");
7107
7276
  repository.bareRepoDir = useRelativeBare ? `./${bareRepoDirRelative}` : input2.bareRepoDir;
7108
7277
  }
7278
+ const defaults = {
7279
+ cronSchedule: input2.cronSchedule
7280
+ };
7281
+ if (input2.runOnce) {
7282
+ defaults.runOnce = input2.runOnce;
7283
+ }
7109
7284
  const configObject = {
7110
- defaults: {
7111
- cronSchedule: input2.cronSchedule,
7112
- runOnce: input2.runOnce
7113
- },
7285
+ defaults,
7114
7286
  repositories: [repository]
7115
7287
  };
7116
7288
  const configContent = `// @ts-check