@ogpoyraz/wtx 0.9.1 → 0.9.3
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 +652 -370
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -25068,7 +25068,7 @@ var init_owner = __esm(() => {
|
|
|
25068
25068
|
});
|
|
25069
25069
|
|
|
25070
25070
|
// src/lib/workspace.ts
|
|
25071
|
-
import
|
|
25071
|
+
import fs24 from "fs";
|
|
25072
25072
|
import path29 from "path";
|
|
25073
25073
|
function getWorkspaceRoot(config2) {
|
|
25074
25074
|
return path29.resolve(expandTilde(config2.workspace_root ?? path29.join(config2.root, "wtx-workspaces")));
|
|
@@ -25119,12 +25119,12 @@ function assertNoPathNesting(workspacePath, members) {
|
|
|
25119
25119
|
async function writeManifest(workspacePath, manifest) {
|
|
25120
25120
|
const manifestPath = path29.join(workspacePath, MANIFEST_FILE);
|
|
25121
25121
|
const tmpPath = path29.join(workspacePath, `${MANIFEST_FILE}.${process.pid}.${Date.now()}.tmp`);
|
|
25122
|
-
await
|
|
25122
|
+
await fs24.promises.writeFile(tmpPath, `${JSON.stringify(manifest, null, 2)}
|
|
25123
25123
|
`, "utf8");
|
|
25124
|
-
await
|
|
25124
|
+
await fs24.promises.rename(tmpPath, manifestPath);
|
|
25125
25125
|
}
|
|
25126
25126
|
async function readManifest(workspacePath) {
|
|
25127
|
-
const raw = await
|
|
25127
|
+
const raw = await fs24.promises.readFile(path29.join(workspacePath, MANIFEST_FILE), "utf8");
|
|
25128
25128
|
const parsed = JSON.parse(raw);
|
|
25129
25129
|
if (parsed.version !== 1) {
|
|
25130
25130
|
throw new Error(`Unsupported workspace manifest version: ${parsed.version}`);
|
|
@@ -25139,7 +25139,7 @@ async function writeAgentsFile(workspacePath, linkNames) {
|
|
|
25139
25139
|
...linkNames.map((name) => `- ${name}`),
|
|
25140
25140
|
""
|
|
25141
25141
|
];
|
|
25142
|
-
await
|
|
25142
|
+
await fs24.promises.writeFile(path29.join(workspacePath, AGENTS_FILE), lines.join(`
|
|
25143
25143
|
`), "utf8");
|
|
25144
25144
|
}
|
|
25145
25145
|
async function createMemberSymlinks(workspacePath, members) {
|
|
@@ -25149,13 +25149,13 @@ async function createMemberSymlinks(workspacePath, members) {
|
|
|
25149
25149
|
const linkName = memberLinkName(member, taken);
|
|
25150
25150
|
const linkPath = path29.join(workspacePath, linkName);
|
|
25151
25151
|
const target = path29.resolve(member.path);
|
|
25152
|
-
await
|
|
25152
|
+
await fs24.promises.symlink(target, linkPath, process.platform === "win32" ? "junction" : "dir");
|
|
25153
25153
|
linkNames.push(linkName);
|
|
25154
25154
|
}
|
|
25155
25155
|
return linkNames;
|
|
25156
25156
|
}
|
|
25157
25157
|
async function workspaceLinkNames(workspacePath) {
|
|
25158
|
-
const entries = await
|
|
25158
|
+
const entries = await fs24.promises.readdir(workspacePath, { withFileTypes: true });
|
|
25159
25159
|
return entries.filter((entry) => entry.name !== MANIFEST_FILE && entry.name !== AGENTS_FILE && entry.isSymbolicLink()).map((entry) => entry.name).sort();
|
|
25160
25160
|
}
|
|
25161
25161
|
async function rebuildAgentsFile(workspacePath) {
|
|
@@ -25165,7 +25165,7 @@ async function createWorkspace(opts) {
|
|
|
25165
25165
|
validateWorkspaceName(opts.name);
|
|
25166
25166
|
const workspacePath = workspacePathFor(opts.name, opts.config);
|
|
25167
25167
|
assertNoPathNesting(workspacePath, opts.members);
|
|
25168
|
-
await
|
|
25168
|
+
await fs24.promises.mkdir(workspacePath, { recursive: true });
|
|
25169
25169
|
const linkNames = await createMemberSymlinks(workspacePath, opts.members);
|
|
25170
25170
|
await writeManifest(workspacePath, {
|
|
25171
25171
|
version: 1,
|
|
@@ -25183,7 +25183,7 @@ async function addMember(opts) {
|
|
|
25183
25183
|
const taken = new Set(await workspaceLinkNames(opts.workspacePath));
|
|
25184
25184
|
const linkName = memberLinkName(opts.member, taken);
|
|
25185
25185
|
const target = path29.resolve(opts.member.path);
|
|
25186
|
-
await
|
|
25186
|
+
await fs24.promises.symlink(target, path29.join(opts.workspacePath, linkName), process.platform === "win32" ? "junction" : "dir");
|
|
25187
25187
|
manifest.members.push({ repo: opts.member.repo, branch: opts.member.branch });
|
|
25188
25188
|
await writeManifest(opts.workspacePath, manifest);
|
|
25189
25189
|
await rebuildAgentsFile(opts.workspacePath);
|
|
@@ -25197,7 +25197,7 @@ async function removeMember(opts) {
|
|
|
25197
25197
|
const linkNames = await workspaceLinkNames(opts.workspacePath);
|
|
25198
25198
|
for (const linkName of linkNames) {
|
|
25199
25199
|
if (linkName === opts.repo || linkName === `${opts.repo}-${opts.branch.replace(/[\\/]+/g, "-")}`) {
|
|
25200
|
-
await
|
|
25200
|
+
await fs24.promises.unlink(path29.join(opts.workspacePath, linkName));
|
|
25201
25201
|
break;
|
|
25202
25202
|
}
|
|
25203
25203
|
}
|
|
@@ -25206,7 +25206,7 @@ async function removeMember(opts) {
|
|
|
25206
25206
|
}
|
|
25207
25207
|
async function listWorkspaces(workspaceRoot) {
|
|
25208
25208
|
try {
|
|
25209
|
-
const entries = await
|
|
25209
|
+
const entries = await fs24.promises.readdir(path29.resolve(expandTilde(workspaceRoot)), { withFileTypes: true });
|
|
25210
25210
|
const workspaces = [];
|
|
25211
25211
|
for (const entry of entries) {
|
|
25212
25212
|
if (!entry.isDirectory())
|
|
@@ -25235,7 +25235,7 @@ async function verifyLink(linkPath, visited) {
|
|
|
25235
25235
|
visited.add(resolvedLink);
|
|
25236
25236
|
let stats;
|
|
25237
25237
|
try {
|
|
25238
|
-
stats = await
|
|
25238
|
+
stats = await fs24.promises.lstat(linkPath);
|
|
25239
25239
|
} catch {
|
|
25240
25240
|
return "broken";
|
|
25241
25241
|
}
|
|
@@ -25243,13 +25243,13 @@ async function verifyLink(linkPath, visited) {
|
|
|
25243
25243
|
return "ok";
|
|
25244
25244
|
let rawTarget;
|
|
25245
25245
|
try {
|
|
25246
|
-
rawTarget = await
|
|
25246
|
+
rawTarget = await fs24.promises.readlink(linkPath);
|
|
25247
25247
|
} catch {
|
|
25248
25248
|
return "broken";
|
|
25249
25249
|
}
|
|
25250
25250
|
const target = path29.isAbsolute(rawTarget) ? rawTarget : path29.resolve(path29.dirname(linkPath), rawTarget);
|
|
25251
25251
|
try {
|
|
25252
|
-
const targetStats = await
|
|
25252
|
+
const targetStats = await fs24.promises.lstat(target);
|
|
25253
25253
|
if (!targetStats.isSymbolicLink())
|
|
25254
25254
|
return "ok";
|
|
25255
25255
|
} catch {
|
|
@@ -25260,7 +25260,7 @@ async function verifyLink(linkPath, visited) {
|
|
|
25260
25260
|
async function verify(workspacePath) {
|
|
25261
25261
|
const result = { ok: true, broken: [], cycles: [] };
|
|
25262
25262
|
try {
|
|
25263
|
-
const entries = await
|
|
25263
|
+
const entries = await fs24.promises.readdir(workspacePath, { withFileTypes: true });
|
|
25264
25264
|
for (const entry of entries) {
|
|
25265
25265
|
if (entry.name === MANIFEST_FILE || entry.name === AGENTS_FILE || !entry.isSymbolicLink())
|
|
25266
25266
|
continue;
|
|
@@ -25503,20 +25503,22 @@ async function fetchWorktreeData(opts, scope) {
|
|
|
25503
25503
|
}
|
|
25504
25504
|
let repos;
|
|
25505
25505
|
try {
|
|
25506
|
-
|
|
25506
|
+
if (scope && scope.length > 0) {
|
|
25507
|
+
repos = resolveRepos(config2, scope);
|
|
25508
|
+
} else {
|
|
25509
|
+
repos = resolveRepos(config2, Object.keys(config2.repos));
|
|
25510
|
+
}
|
|
25507
25511
|
} catch (err) {
|
|
25508
25512
|
throw new Error(`Failed to resolve repos: ${errorMessage3(err)}`);
|
|
25509
25513
|
}
|
|
25510
|
-
if (scope) {
|
|
25511
|
-
const scopeSet = new Set(scope);
|
|
25512
|
-
repos = repos.filter((r) => scopeSet.has(r.name));
|
|
25513
|
-
}
|
|
25514
25514
|
const semaphore = new Semaphore(4);
|
|
25515
25515
|
const allRows = [];
|
|
25516
25516
|
const processRepo = async (repo) => {
|
|
25517
25517
|
await semaphore.acquire();
|
|
25518
|
+
let mainBranch = config2.default_main_branch;
|
|
25519
|
+
let hasMain = false;
|
|
25518
25520
|
try {
|
|
25519
|
-
|
|
25521
|
+
mainBranch = await resolveMainBranch(repo, config2);
|
|
25520
25522
|
const wts = await getWorktreeList(repo.mainPath);
|
|
25521
25523
|
let stackMetadata = { version: 1, branches: {} };
|
|
25522
25524
|
try {
|
|
@@ -25531,7 +25533,9 @@ async function fetchWorktreeData(opts, scope) {
|
|
|
25531
25533
|
if (wt.isBare)
|
|
25532
25534
|
continue;
|
|
25533
25535
|
let branch = wt.branch;
|
|
25534
|
-
const isMainCheckout = wt.path === repo.mainPath;
|
|
25536
|
+
const isMainCheckout = safeResolve(wt.path) === safeResolve(repo.mainPath);
|
|
25537
|
+
if (isMainCheckout)
|
|
25538
|
+
hasMain = true;
|
|
25535
25539
|
if (isMainCheckout && !branch) {
|
|
25536
25540
|
branch = mainBranch;
|
|
25537
25541
|
}
|
|
@@ -25562,6 +25566,7 @@ async function fetchWorktreeData(opts, scope) {
|
|
|
25562
25566
|
};
|
|
25563
25567
|
if (isMainCheckout) {
|
|
25564
25568
|
allRows.push(row);
|
|
25569
|
+
prRows.push(row);
|
|
25565
25570
|
continue;
|
|
25566
25571
|
}
|
|
25567
25572
|
await Promise.allSettled([
|
|
@@ -25622,8 +25627,56 @@ async function fetchWorktreeData(opts, scope) {
|
|
|
25622
25627
|
if (forge && branches.length > 0) {
|
|
25623
25628
|
prFetchJobs.push({ repo, forge, mainBranch, rows: prRows, branches, stackMetadata });
|
|
25624
25629
|
}
|
|
25630
|
+
if (!hasMain) {
|
|
25631
|
+
allRows.push({
|
|
25632
|
+
repoName: repo.name,
|
|
25633
|
+
branch: mainBranch,
|
|
25634
|
+
path: repo.mainPath,
|
|
25635
|
+
commitShort: "",
|
|
25636
|
+
isMainCheckout: true,
|
|
25637
|
+
isLocked: false,
|
|
25638
|
+
isPrunable: false,
|
|
25639
|
+
isBare: false,
|
|
25640
|
+
dirtyFiles: [],
|
|
25641
|
+
ahead: null,
|
|
25642
|
+
behind: null,
|
|
25643
|
+
prNumber: null,
|
|
25644
|
+
prState: null,
|
|
25645
|
+
prChecks: null,
|
|
25646
|
+
prUrl: null,
|
|
25647
|
+
owner: null,
|
|
25648
|
+
rebaseStatus: null,
|
|
25649
|
+
depsStrategy: "none",
|
|
25650
|
+
base: undefined,
|
|
25651
|
+
baseChanged: false
|
|
25652
|
+
});
|
|
25653
|
+
}
|
|
25625
25654
|
} catch (err) {
|
|
25626
25655
|
warnings.push({ repoName: repo.name, message: `Failed to process repo ${repo.name}: ${errorMessage3(err)}` });
|
|
25656
|
+
if (!hasMain) {
|
|
25657
|
+
allRows.push({
|
|
25658
|
+
repoName: repo.name,
|
|
25659
|
+
branch: mainBranch,
|
|
25660
|
+
path: repo.mainPath,
|
|
25661
|
+
commitShort: "",
|
|
25662
|
+
isMainCheckout: true,
|
|
25663
|
+
isLocked: false,
|
|
25664
|
+
isPrunable: false,
|
|
25665
|
+
isBare: false,
|
|
25666
|
+
dirtyFiles: [],
|
|
25667
|
+
ahead: null,
|
|
25668
|
+
behind: null,
|
|
25669
|
+
prNumber: null,
|
|
25670
|
+
prState: null,
|
|
25671
|
+
prChecks: null,
|
|
25672
|
+
prUrl: null,
|
|
25673
|
+
owner: null,
|
|
25674
|
+
rebaseStatus: null,
|
|
25675
|
+
depsStrategy: "none",
|
|
25676
|
+
base: undefined,
|
|
25677
|
+
baseChanged: false
|
|
25678
|
+
});
|
|
25679
|
+
}
|
|
25627
25680
|
} finally {
|
|
25628
25681
|
semaphore.release();
|
|
25629
25682
|
}
|
|
@@ -25650,6 +25703,7 @@ var init_data = __esm(() => {
|
|
|
25650
25703
|
init_owner();
|
|
25651
25704
|
init_types2();
|
|
25652
25705
|
init_stack();
|
|
25706
|
+
init_path_safety();
|
|
25653
25707
|
PR_CACHE_TTL_MS = 5 * 60 * 1000;
|
|
25654
25708
|
prCache = new Map;
|
|
25655
25709
|
});
|
|
@@ -25946,7 +26000,12 @@ var init_useWorktrees = __esm(() => {
|
|
|
25946
26000
|
import { RGBA } from "@opentui/core";
|
|
25947
26001
|
import { createContext, useContext } from "react";
|
|
25948
26002
|
function useTheme() {
|
|
25949
|
-
|
|
26003
|
+
try {
|
|
26004
|
+
const ctx = useContext(ThemeContext);
|
|
26005
|
+
return ctx ?? tokens;
|
|
26006
|
+
} catch {
|
|
26007
|
+
return tokens;
|
|
26008
|
+
}
|
|
25950
26009
|
}
|
|
25951
26010
|
function truncateBranch(name, max = 42) {
|
|
25952
26011
|
if (name.length <= max)
|
|
@@ -26101,18 +26160,18 @@ async function openInBrowser(url2) {
|
|
|
26101
26160
|
// src/tui/components/WorktreeTable.tsx
|
|
26102
26161
|
import { useEffect as useEffect2, useRef as useRef3 } from "react";
|
|
26103
26162
|
import { jsx, jsxs } from "@opentui/react/jsx-runtime";
|
|
26104
|
-
function statusBadge(row) {
|
|
26163
|
+
function statusBadge(row, theme) {
|
|
26105
26164
|
if (row.isMainCheckout)
|
|
26106
|
-
return { text: "[main]", fg:
|
|
26165
|
+
return { text: "[main]", fg: theme.accent };
|
|
26107
26166
|
if (row.isPrunable)
|
|
26108
|
-
return { text: "missing", fg:
|
|
26167
|
+
return { text: "missing", fg: theme.error };
|
|
26109
26168
|
if (row.isLocked)
|
|
26110
|
-
return { text: "locked", fg:
|
|
26169
|
+
return { text: "locked", fg: theme.error };
|
|
26111
26170
|
if (row.rebaseStatus)
|
|
26112
|
-
return { text: "rebasing", fg:
|
|
26171
|
+
return { text: "rebasing", fg: theme.warning };
|
|
26113
26172
|
if (row.dirtyFiles.length > 0)
|
|
26114
|
-
return { text: `dirty (${row.dirtyFiles.length})`, fg:
|
|
26115
|
-
return { text: "clean", fg:
|
|
26173
|
+
return { text: `dirty (${row.dirtyFiles.length})`, fg: theme.warning };
|
|
26174
|
+
return { text: "clean", fg: theme.dim };
|
|
26116
26175
|
}
|
|
26117
26176
|
function WorktreeItem({
|
|
26118
26177
|
row,
|
|
@@ -26125,6 +26184,7 @@ function WorktreeItem({
|
|
|
26125
26184
|
onRowClick,
|
|
26126
26185
|
onToggleSelect
|
|
26127
26186
|
}) {
|
|
26187
|
+
const theme = useTheme();
|
|
26128
26188
|
const prTap = useTapHandler(() => {
|
|
26129
26189
|
if (row.prUrl)
|
|
26130
26190
|
openInBrowser(row.prUrl);
|
|
@@ -26136,9 +26196,9 @@ function WorktreeItem({
|
|
|
26136
26196
|
e.stopPropagation();
|
|
26137
26197
|
onToggleSelect?.();
|
|
26138
26198
|
});
|
|
26139
|
-
const badge = indicator ? indicator.running ? { text: `${frame} ${indicator.verb}…`, fg:
|
|
26199
|
+
const badge = indicator ? indicator.running ? { text: `${frame} ${indicator.verb}…`, fg: theme.accent } : { text: `◌ ${indicator.verb}`, fg: theme.dim } : row.isPendingCreate ? { text: `${frame} creating…`, fg: theme.accent } : statusBadge(row, theme);
|
|
26140
26200
|
const disabled = indicator !== undefined || row.isPendingCreate === true;
|
|
26141
|
-
const primary = isSelected ?
|
|
26201
|
+
const primary = isSelected ? theme.bright : disabled ? theme.dim : theme.fg;
|
|
26142
26202
|
const divergence = row.ahead !== null && row.behind !== null && (row.ahead > 0 || row.behind > 0) ? ` · ↑${row.ahead} ↓${row.behind}` : "";
|
|
26143
26203
|
const ownerSegment = row.owner ? ` · by ${row.owner}` : "";
|
|
26144
26204
|
const baseSegment = row.base ? ` · base ${row.base}` : "";
|
|
@@ -26156,7 +26216,7 @@ function WorktreeItem({
|
|
|
26156
26216
|
return /* @__PURE__ */ jsxs("box", {
|
|
26157
26217
|
id,
|
|
26158
26218
|
flexDirection: "column",
|
|
26159
|
-
backgroundColor: isSelected ?
|
|
26219
|
+
backgroundColor: isSelected ? theme.selectionBg : undefined,
|
|
26160
26220
|
style: { paddingRight: 1 },
|
|
26161
26221
|
...isClickable && onRowClick ? rowTap : {},
|
|
26162
26222
|
children: [
|
|
@@ -26173,18 +26233,18 @@ function WorktreeItem({
|
|
|
26173
26233
|
width: 2,
|
|
26174
26234
|
...onToggleSelect ? gutterTap : {},
|
|
26175
26235
|
children: /* @__PURE__ */ jsx("text", {
|
|
26176
|
-
fg:
|
|
26236
|
+
fg: theme.accent,
|
|
26177
26237
|
children: isMultiSelected ? "✓ " : " "
|
|
26178
26238
|
})
|
|
26179
26239
|
}),
|
|
26180
26240
|
/* @__PURE__ */ jsxs("text", {
|
|
26181
26241
|
children: [
|
|
26182
26242
|
/* @__PURE__ */ jsx("span", {
|
|
26183
|
-
fg:
|
|
26243
|
+
fg: theme.dim,
|
|
26184
26244
|
children: hierarchyPrefix
|
|
26185
26245
|
}),
|
|
26186
26246
|
isWorkspaceMember && /* @__PURE__ */ jsx("span", {
|
|
26187
|
-
fg:
|
|
26247
|
+
fg: theme.accent,
|
|
26188
26248
|
children: "⬡ "
|
|
26189
26249
|
}),
|
|
26190
26250
|
/* @__PURE__ */ jsx("span", {
|
|
@@ -26205,11 +26265,11 @@ function WorktreeItem({
|
|
|
26205
26265
|
/* @__PURE__ */ jsxs("text", {
|
|
26206
26266
|
children: [
|
|
26207
26267
|
/* @__PURE__ */ jsx("span", {
|
|
26208
|
-
fg:
|
|
26268
|
+
fg: theme.dim,
|
|
26209
26269
|
children: secondary
|
|
26210
26270
|
}),
|
|
26211
26271
|
row.prNumber !== null && secondary ? /* @__PURE__ */ jsx("span", {
|
|
26212
|
-
fg:
|
|
26272
|
+
fg: theme.dim,
|
|
26213
26273
|
children: " · "
|
|
26214
26274
|
}) : null
|
|
26215
26275
|
]
|
|
@@ -26219,26 +26279,26 @@ function WorktreeItem({
|
|
|
26219
26279
|
children: /* @__PURE__ */ jsxs("text", {
|
|
26220
26280
|
children: [
|
|
26221
26281
|
/* @__PURE__ */ jsx("span", {
|
|
26222
|
-
fg:
|
|
26282
|
+
fg: theme.accent,
|
|
26223
26283
|
children: `#${row.prNumber}`
|
|
26224
26284
|
}),
|
|
26225
26285
|
row.prState && /* @__PURE__ */ jsx("span", {
|
|
26226
|
-
fg:
|
|
26286
|
+
fg: theme.dim,
|
|
26227
26287
|
children: ` ${row.prState}`
|
|
26228
26288
|
}),
|
|
26229
26289
|
row.prChecks && /* @__PURE__ */ jsx("span", {
|
|
26230
|
-
fg:
|
|
26290
|
+
fg: theme.dim,
|
|
26231
26291
|
children: ` (${row.prChecks})`
|
|
26232
26292
|
}),
|
|
26233
26293
|
row.prUrl && /* @__PURE__ */ jsx("span", {
|
|
26234
|
-
fg:
|
|
26294
|
+
fg: theme.dim,
|
|
26235
26295
|
children: " ↗"
|
|
26236
26296
|
})
|
|
26237
26297
|
]
|
|
26238
26298
|
})
|
|
26239
26299
|
}),
|
|
26240
26300
|
row.prNumber === null && row.prState === "FETCHING" && /* @__PURE__ */ jsxs("text", {
|
|
26241
|
-
fg:
|
|
26301
|
+
fg: theme.dim,
|
|
26242
26302
|
children: [
|
|
26243
26303
|
secondary ? " · " : "",
|
|
26244
26304
|
"PR …"
|
|
@@ -26247,11 +26307,11 @@ function WorktreeItem({
|
|
|
26247
26307
|
/* @__PURE__ */ jsxs("text", {
|
|
26248
26308
|
children: [
|
|
26249
26309
|
baseChangedSegment && /* @__PURE__ */ jsx("span", {
|
|
26250
|
-
fg:
|
|
26310
|
+
fg: theme.warning,
|
|
26251
26311
|
children: baseChangedSegment
|
|
26252
26312
|
}),
|
|
26253
26313
|
rebaseSegment && /* @__PURE__ */ jsx("span", {
|
|
26254
|
-
fg:
|
|
26314
|
+
fg: theme.error,
|
|
26255
26315
|
children: rebaseSegment
|
|
26256
26316
|
})
|
|
26257
26317
|
]
|
|
@@ -26262,6 +26322,7 @@ function WorktreeItem({
|
|
|
26262
26322
|
});
|
|
26263
26323
|
}
|
|
26264
26324
|
function WorktreeTable({ blocks, selectedIndex, selection = new Set, frame, repoVerbs, rowVerbs, favorites = [], workspaceMemberSet = null, activeWorkspaceName = null, onRowClick, onToggleSelect }) {
|
|
26325
|
+
const theme = useTheme();
|
|
26265
26326
|
const favoriteSet = new Set(favorites);
|
|
26266
26327
|
const scrollRef = useRef3(null);
|
|
26267
26328
|
useEffect2(() => {
|
|
@@ -26285,8 +26346,8 @@ function WorktreeTable({ blocks, selectedIndex, selection = new Set, frame, repo
|
|
|
26285
26346
|
width: "100%",
|
|
26286
26347
|
height: "100%",
|
|
26287
26348
|
border: true,
|
|
26288
|
-
borderColor:
|
|
26289
|
-
focusedBorderColor:
|
|
26349
|
+
borderColor: theme.border,
|
|
26350
|
+
focusedBorderColor: theme.border,
|
|
26290
26351
|
focusable: false,
|
|
26291
26352
|
title: activeWorkspaceName ? `Repositories · workspace: ${activeWorkspaceName}` : "Repositories",
|
|
26292
26353
|
paddingX: 1,
|
|
@@ -26295,7 +26356,7 @@ function WorktreeTable({ blocks, selectedIndex, selection = new Set, frame, repo
|
|
|
26295
26356
|
e.stopPropagation();
|
|
26296
26357
|
},
|
|
26297
26358
|
children: blocks.length === 0 ? /* @__PURE__ */ jsx("text", {
|
|
26298
|
-
fg:
|
|
26359
|
+
fg: theme.dim,
|
|
26299
26360
|
children: "No repositories configured."
|
|
26300
26361
|
}) : blocks.map((block) => {
|
|
26301
26362
|
const repoIndicator = repoVerbs.get(block.repoName);
|
|
@@ -26308,22 +26369,22 @@ function WorktreeTable({ blocks, selectedIndex, selection = new Set, frame, repo
|
|
|
26308
26369
|
children: /* @__PURE__ */ jsxs("text", {
|
|
26309
26370
|
children: [
|
|
26310
26371
|
favoriteSet.has(block.repoName) && /* @__PURE__ */ jsx("span", {
|
|
26311
|
-
fg:
|
|
26372
|
+
fg: theme.accent,
|
|
26312
26373
|
children: "★ "
|
|
26313
26374
|
}),
|
|
26314
26375
|
/* @__PURE__ */ jsx("span", {
|
|
26315
|
-
fg:
|
|
26376
|
+
fg: theme.bright,
|
|
26316
26377
|
children: block.repoName
|
|
26317
26378
|
}),
|
|
26318
26379
|
block.rows.length > 0 && /* @__PURE__ */ jsx("span", {
|
|
26319
|
-
fg:
|
|
26380
|
+
fg: theme.dim,
|
|
26320
26381
|
children: ` · ${block.rows.filter((r) => !r.isPendingCreate).length}`
|
|
26321
26382
|
}),
|
|
26322
26383
|
repoIndicator ? /* @__PURE__ */ jsx("span", {
|
|
26323
|
-
fg: repoIndicator.running ?
|
|
26384
|
+
fg: repoIndicator.running ? theme.accent : theme.dim,
|
|
26324
26385
|
children: repoIndicator.running ? ` ${frame} ${repoIndicator.verb}…` : ` ◌ ${repoIndicator.verb}`
|
|
26325
26386
|
}) : block.rows.length === 0 ? /* @__PURE__ */ jsx("span", {
|
|
26326
|
-
fg:
|
|
26387
|
+
fg: theme.accent,
|
|
26327
26388
|
children: ` ${frame} refreshing…`
|
|
26328
26389
|
}) : null
|
|
26329
26390
|
]
|
|
@@ -26357,6 +26418,7 @@ function WorktreeTable({ blocks, selectedIndex, selection = new Set, frame, repo
|
|
|
26357
26418
|
}
|
|
26358
26419
|
var SECONDARY_INDENT = " ";
|
|
26359
26420
|
var init_WorktreeTable = __esm(() => {
|
|
26421
|
+
init_theme();
|
|
26360
26422
|
init_theme();
|
|
26361
26423
|
init_use_tap();
|
|
26362
26424
|
init_utils();
|
|
@@ -26372,6 +26434,7 @@ function TabItem({
|
|
|
26372
26434
|
onSelect,
|
|
26373
26435
|
onClose
|
|
26374
26436
|
}) {
|
|
26437
|
+
const theme = useTheme();
|
|
26375
26438
|
const tap = useTapHandler(() => onSelect(id));
|
|
26376
26439
|
const closeTap = useTapHandler((e) => {
|
|
26377
26440
|
e.stopPropagation();
|
|
@@ -26383,15 +26446,15 @@ function TabItem({
|
|
|
26383
26446
|
...tap,
|
|
26384
26447
|
children: [
|
|
26385
26448
|
/* @__PURE__ */ jsx2("text", {
|
|
26386
|
-
fg: active ?
|
|
26449
|
+
fg: active ? theme.accent : theme.dim,
|
|
26387
26450
|
attributes: active ? 1 : 0,
|
|
26388
26451
|
children: label
|
|
26389
26452
|
}),
|
|
26390
26453
|
closable ? /* @__PURE__ */ jsx2("box", {
|
|
26391
26454
|
...closeTap,
|
|
26392
|
-
style: { marginLeft: 1 },
|
|
26455
|
+
style: { marginLeft: 1, paddingLeft: 1, paddingRight: 1 },
|
|
26393
26456
|
children: /* @__PURE__ */ jsx2("text", {
|
|
26394
|
-
fg:
|
|
26457
|
+
fg: theme.accent,
|
|
26395
26458
|
children: "✕"
|
|
26396
26459
|
})
|
|
26397
26460
|
}) : null,
|
|
@@ -26426,6 +26489,7 @@ var init_TabBar = __esm(() => {
|
|
|
26426
26489
|
import { useState as useState2, useEffect as useEffect3, useRef as useRef4, useCallback as useCallback3 } from "react";
|
|
26427
26490
|
import { jsx as jsx3, jsxs as jsxs3 } from "@opentui/react/jsx-runtime";
|
|
26428
26491
|
function TerminalView({ session, focused, onFocus, onSend, onResize, registerListener, unregisterListener }) {
|
|
26492
|
+
const theme = useTheme();
|
|
26429
26493
|
const [draft, setDraft] = useState2("");
|
|
26430
26494
|
const focusTap = useTapHandler(() => onFocus());
|
|
26431
26495
|
const termRef = useRef4(null);
|
|
@@ -26568,7 +26632,7 @@ function TerminalView({ session, focused, onFocus, onSend, onResize, registerLis
|
|
|
26568
26632
|
})
|
|
26569
26633
|
}),
|
|
26570
26634
|
session.exited !== null && /* @__PURE__ */ jsxs3("text", {
|
|
26571
|
-
fg:
|
|
26635
|
+
fg: theme.warning,
|
|
26572
26636
|
children: [
|
|
26573
26637
|
"— exited with code ",
|
|
26574
26638
|
session.exited,
|
|
@@ -26576,7 +26640,7 @@ function TerminalView({ session, focused, onFocus, onSend, onResize, registerLis
|
|
|
26576
26640
|
]
|
|
26577
26641
|
}),
|
|
26578
26642
|
!focused && /* @__PURE__ */ jsxs3("text", {
|
|
26579
|
-
fg:
|
|
26643
|
+
fg: theme.dim,
|
|
26580
26644
|
children: [
|
|
26581
26645
|
"Click to focus · Ctrl+G or click table to unfocus · ",
|
|
26582
26646
|
session.label,
|
|
@@ -26584,7 +26648,7 @@ function TerminalView({ session, focused, onFocus, onSend, onResize, registerLis
|
|
|
26584
26648
|
]
|
|
26585
26649
|
}),
|
|
26586
26650
|
focused && /* @__PURE__ */ jsx3("text", {
|
|
26587
|
-
fg:
|
|
26651
|
+
fg: theme.accent,
|
|
26588
26652
|
children: "Focused PTY — all keys go to shell · Ctrl+G to unfocus · scroll wheel to view history"
|
|
26589
26653
|
})
|
|
26590
26654
|
]
|
|
@@ -26602,12 +26666,12 @@ function TerminalView({ session, focused, onFocus, onSend, onResize, registerLis
|
|
|
26602
26666
|
children: [
|
|
26603
26667
|
spawnError && /* @__PURE__ */ jsx3("box", {
|
|
26604
26668
|
border: true,
|
|
26605
|
-
borderColor:
|
|
26669
|
+
borderColor: theme.error,
|
|
26606
26670
|
paddingX: 1,
|
|
26607
26671
|
paddingY: 0,
|
|
26608
26672
|
marginBottom: 1,
|
|
26609
26673
|
children: /* @__PURE__ */ jsx3("text", {
|
|
26610
|
-
fg:
|
|
26674
|
+
fg: theme.error,
|
|
26611
26675
|
children: spawnError
|
|
26612
26676
|
})
|
|
26613
26677
|
}),
|
|
@@ -26622,14 +26686,14 @@ function TerminalView({ session, focused, onFocus, onSend, onResize, registerLis
|
|
|
26622
26686
|
onMouseScroll: handlePipeScroll,
|
|
26623
26687
|
children: [
|
|
26624
26688
|
lines.length === 0 ? /* @__PURE__ */ jsx3("text", {
|
|
26625
|
-
fg:
|
|
26689
|
+
fg: theme.dim,
|
|
26626
26690
|
children: "No output yet — type a command below."
|
|
26627
26691
|
}) : lines.slice(-500).map((line, idx) => /* @__PURE__ */ jsx3("text", {
|
|
26628
|
-
fg: spawnError ?
|
|
26692
|
+
fg: spawnError ? theme.error : session.exited !== null ? theme.dim : theme.fg,
|
|
26629
26693
|
children: line || " "
|
|
26630
26694
|
}, idx)),
|
|
26631
26695
|
session.exited !== null && /* @__PURE__ */ jsxs3("text", {
|
|
26632
|
-
fg:
|
|
26696
|
+
fg: theme.warning,
|
|
26633
26697
|
children: [
|
|
26634
26698
|
"— exited with code ",
|
|
26635
26699
|
session.exited,
|
|
@@ -26644,7 +26708,7 @@ function TerminalView({ session, focused, onFocus, onSend, onResize, registerLis
|
|
|
26644
26708
|
width: "100%",
|
|
26645
26709
|
children: [
|
|
26646
26710
|
/* @__PURE__ */ jsx3("text", {
|
|
26647
|
-
fg:
|
|
26711
|
+
fg: theme.dim,
|
|
26648
26712
|
children: "$"
|
|
26649
26713
|
}),
|
|
26650
26714
|
/* @__PURE__ */ jsx3("box", {
|
|
@@ -26666,11 +26730,11 @@ function TerminalView({ session, focused, onFocus, onSend, onResize, registerLis
|
|
|
26666
26730
|
]
|
|
26667
26731
|
}),
|
|
26668
26732
|
!focused && /* @__PURE__ */ jsx3("text", {
|
|
26669
|
-
fg:
|
|
26733
|
+
fg: theme.dim,
|
|
26670
26734
|
children: "Click terminal or press 't' to focus · Ctrl+G or click table to unfocus"
|
|
26671
26735
|
}),
|
|
26672
26736
|
focused && /* @__PURE__ */ jsx3("text", {
|
|
26673
|
-
fg:
|
|
26737
|
+
fg: theme.accent,
|
|
26674
26738
|
children: "Focused — typing goes to shell · Ctrl+G to unfocus · max 5 per worktree persist across switches"
|
|
26675
26739
|
})
|
|
26676
26740
|
]
|
|
@@ -26694,12 +26758,16 @@ function TabPane({
|
|
|
26694
26758
|
onClose,
|
|
26695
26759
|
allSessionsFlat,
|
|
26696
26760
|
terminalFocused,
|
|
26761
|
+
changesFocused,
|
|
26762
|
+
onChangesFocus,
|
|
26697
26763
|
activeTabId,
|
|
26698
26764
|
recentSessionIds,
|
|
26699
26765
|
terminalSessions
|
|
26700
26766
|
}) {
|
|
26701
|
-
const
|
|
26767
|
+
const theme = useTheme();
|
|
26768
|
+
const borderColor = focused ? theme.accent : theme.border;
|
|
26702
26769
|
const mountedSessions = allSessionsFlat?.filter((s) => activeTabId === s.id || recentSessionIds?.has(s.id));
|
|
26770
|
+
const nonSessionTabs = tabs.filter((t) => !t.closable);
|
|
26703
26771
|
return /* @__PURE__ */ jsxs4("box", {
|
|
26704
26772
|
flexDirection: "column",
|
|
26705
26773
|
flexGrow: 1,
|
|
@@ -26724,13 +26792,28 @@ function TabPane({
|
|
|
26724
26792
|
width: "100%",
|
|
26725
26793
|
flexDirection: "column",
|
|
26726
26794
|
children: [
|
|
26727
|
-
|
|
26728
|
-
|
|
26729
|
-
|
|
26730
|
-
|
|
26731
|
-
|
|
26732
|
-
|
|
26733
|
-
|
|
26795
|
+
nonSessionTabs.map((t) => {
|
|
26796
|
+
const isChanges = t.id === "changes";
|
|
26797
|
+
const tabFocused = isChanges ? !!changesFocused : !terminalFocused && !changesFocused && t.id === activeId;
|
|
26798
|
+
const content = t.render({ worktree: selectedRow, isActive: t.id === activeId, focused: tabFocused });
|
|
26799
|
+
if (isChanges) {
|
|
26800
|
+
return /* @__PURE__ */ jsx4("box", {
|
|
26801
|
+
flexGrow: 1,
|
|
26802
|
+
width: "100%",
|
|
26803
|
+
flexDirection: "column",
|
|
26804
|
+
visible: t.id === activeId,
|
|
26805
|
+
onMouseDown: () => onChangesFocus?.(),
|
|
26806
|
+
children: content
|
|
26807
|
+
}, t.id);
|
|
26808
|
+
}
|
|
26809
|
+
return /* @__PURE__ */ jsx4("box", {
|
|
26810
|
+
flexGrow: 1,
|
|
26811
|
+
width: "100%",
|
|
26812
|
+
flexDirection: "column",
|
|
26813
|
+
visible: t.id === activeId,
|
|
26814
|
+
children: content
|
|
26815
|
+
}, t.id);
|
|
26816
|
+
}),
|
|
26734
26817
|
mountedSessions?.map((s) => /* @__PURE__ */ jsx4("box", {
|
|
26735
26818
|
flexGrow: 1,
|
|
26736
26819
|
width: "100%",
|
|
@@ -26747,8 +26830,8 @@ function TabPane({
|
|
|
26747
26830
|
unregisterListener: terminalSessions?.unregisterListener
|
|
26748
26831
|
})
|
|
26749
26832
|
}, s.id)),
|
|
26750
|
-
|
|
26751
|
-
fg:
|
|
26833
|
+
nonSessionTabs.length === 0 && (!mountedSessions || mountedSessions.length === 0) && /* @__PURE__ */ jsx4("text", {
|
|
26834
|
+
fg: theme.dim,
|
|
26752
26835
|
children: "No tab"
|
|
26753
26836
|
})
|
|
26754
26837
|
]
|
|
@@ -26765,6 +26848,7 @@ var init_TabPane = __esm(() => {
|
|
|
26765
26848
|
// src/tui/components/DetailsTab.tsx
|
|
26766
26849
|
import { jsx as jsx5, jsxs as jsxs5, Fragment } from "@opentui/react/jsx-runtime";
|
|
26767
26850
|
function DetailsContent({ selectedRow }) {
|
|
26851
|
+
const theme = useTheme();
|
|
26768
26852
|
const prTap = useTapHandler(() => {
|
|
26769
26853
|
if (selectedRow?.prUrl)
|
|
26770
26854
|
openInBrowser(selectedRow.prUrl);
|
|
@@ -26777,7 +26861,7 @@ function DetailsContent({ selectedRow }) {
|
|
|
26777
26861
|
justifyContent: "center",
|
|
26778
26862
|
alignItems: "center",
|
|
26779
26863
|
children: /* @__PURE__ */ jsx5("text", {
|
|
26780
|
-
fg:
|
|
26864
|
+
fg: theme.dim,
|
|
26781
26865
|
children: "No worktree selected"
|
|
26782
26866
|
})
|
|
26783
26867
|
});
|
|
@@ -26814,11 +26898,11 @@ function DetailsContent({ selectedRow }) {
|
|
|
26814
26898
|
/* @__PURE__ */ jsxs5("text", {
|
|
26815
26899
|
children: [
|
|
26816
26900
|
/* @__PURE__ */ jsx5("span", {
|
|
26817
|
-
fg:
|
|
26901
|
+
fg: theme.dim,
|
|
26818
26902
|
children: "Repo:"
|
|
26819
26903
|
}),
|
|
26820
26904
|
/* @__PURE__ */ jsxs5("span", {
|
|
26821
|
-
fg:
|
|
26905
|
+
fg: theme.fg,
|
|
26822
26906
|
children: [
|
|
26823
26907
|
" ",
|
|
26824
26908
|
repoName
|
|
@@ -26829,11 +26913,11 @@ function DetailsContent({ selectedRow }) {
|
|
|
26829
26913
|
/* @__PURE__ */ jsxs5("text", {
|
|
26830
26914
|
children: [
|
|
26831
26915
|
/* @__PURE__ */ jsx5("span", {
|
|
26832
|
-
fg:
|
|
26916
|
+
fg: theme.dim,
|
|
26833
26917
|
children: "Branch:"
|
|
26834
26918
|
}),
|
|
26835
26919
|
/* @__PURE__ */ jsxs5("span", {
|
|
26836
|
-
fg:
|
|
26920
|
+
fg: theme.fg,
|
|
26837
26921
|
children: [
|
|
26838
26922
|
" ",
|
|
26839
26923
|
branch,
|
|
@@ -26846,11 +26930,11 @@ function DetailsContent({ selectedRow }) {
|
|
|
26846
26930
|
/* @__PURE__ */ jsxs5("text", {
|
|
26847
26931
|
children: [
|
|
26848
26932
|
/* @__PURE__ */ jsx5("span", {
|
|
26849
|
-
fg:
|
|
26933
|
+
fg: theme.dim,
|
|
26850
26934
|
children: "Path:"
|
|
26851
26935
|
}),
|
|
26852
26936
|
/* @__PURE__ */ jsxs5("span", {
|
|
26853
|
-
fg:
|
|
26937
|
+
fg: theme.fg,
|
|
26854
26938
|
children: [
|
|
26855
26939
|
" ",
|
|
26856
26940
|
path37
|
|
@@ -26861,11 +26945,11 @@ function DetailsContent({ selectedRow }) {
|
|
|
26861
26945
|
/* @__PURE__ */ jsxs5("text", {
|
|
26862
26946
|
children: [
|
|
26863
26947
|
/* @__PURE__ */ jsx5("span", {
|
|
26864
|
-
fg:
|
|
26948
|
+
fg: theme.dim,
|
|
26865
26949
|
children: "Commit:"
|
|
26866
26950
|
}),
|
|
26867
26951
|
/* @__PURE__ */ jsxs5("span", {
|
|
26868
|
-
fg:
|
|
26952
|
+
fg: theme.fg,
|
|
26869
26953
|
children: [
|
|
26870
26954
|
" ",
|
|
26871
26955
|
commitShort
|
|
@@ -26876,11 +26960,11 @@ function DetailsContent({ selectedRow }) {
|
|
|
26876
26960
|
owner && /* @__PURE__ */ jsxs5("text", {
|
|
26877
26961
|
children: [
|
|
26878
26962
|
/* @__PURE__ */ jsx5("span", {
|
|
26879
|
-
fg:
|
|
26963
|
+
fg: theme.dim,
|
|
26880
26964
|
children: "Owner:"
|
|
26881
26965
|
}),
|
|
26882
26966
|
/* @__PURE__ */ jsxs5("span", {
|
|
26883
|
-
fg:
|
|
26967
|
+
fg: theme.fg,
|
|
26884
26968
|
children: [
|
|
26885
26969
|
" ",
|
|
26886
26970
|
owner
|
|
@@ -26891,11 +26975,11 @@ function DetailsContent({ selectedRow }) {
|
|
|
26891
26975
|
/* @__PURE__ */ jsxs5("text", {
|
|
26892
26976
|
children: [
|
|
26893
26977
|
/* @__PURE__ */ jsx5("span", {
|
|
26894
|
-
fg:
|
|
26978
|
+
fg: theme.dim,
|
|
26895
26979
|
children: "Status:"
|
|
26896
26980
|
}),
|
|
26897
26981
|
/* @__PURE__ */ jsxs5("span", {
|
|
26898
|
-
fg:
|
|
26982
|
+
fg: theme.fg,
|
|
26899
26983
|
children: [
|
|
26900
26984
|
" ",
|
|
26901
26985
|
isLocked ? "LOCKED" : isPrunable ? "PRUNABLE" : "Active"
|
|
@@ -26906,11 +26990,11 @@ function DetailsContent({ selectedRow }) {
|
|
|
26906
26990
|
base2 && /* @__PURE__ */ jsxs5("text", {
|
|
26907
26991
|
children: [
|
|
26908
26992
|
/* @__PURE__ */ jsx5("span", {
|
|
26909
|
-
fg:
|
|
26993
|
+
fg: theme.dim,
|
|
26910
26994
|
children: "Base:"
|
|
26911
26995
|
}),
|
|
26912
26996
|
/* @__PURE__ */ jsxs5("span", {
|
|
26913
|
-
fg:
|
|
26997
|
+
fg: theme.accent,
|
|
26914
26998
|
children: [
|
|
26915
26999
|
" ",
|
|
26916
27000
|
base2
|
|
@@ -26921,11 +27005,11 @@ function DetailsContent({ selectedRow }) {
|
|
|
26921
27005
|
baseChanged && /* @__PURE__ */ jsxs5("text", {
|
|
26922
27006
|
children: [
|
|
26923
27007
|
/* @__PURE__ */ jsx5("span", {
|
|
26924
|
-
fg:
|
|
27008
|
+
fg: theme.dim,
|
|
26925
27009
|
children: "Base state:"
|
|
26926
27010
|
}),
|
|
26927
27011
|
/* @__PURE__ */ jsx5("span", {
|
|
26928
|
-
fg:
|
|
27012
|
+
fg: theme.warning,
|
|
26929
27013
|
children: " moved since recorded"
|
|
26930
27014
|
})
|
|
26931
27015
|
]
|
|
@@ -26934,11 +27018,11 @@ function DetailsContent({ selectedRow }) {
|
|
|
26934
27018
|
style: { marginTop: 1 },
|
|
26935
27019
|
children: [
|
|
26936
27020
|
/* @__PURE__ */ jsx5("span", {
|
|
26937
|
-
fg:
|
|
27021
|
+
fg: theme.dim,
|
|
26938
27022
|
children: base2 ? "vs base:" : "vs main:"
|
|
26939
27023
|
}),
|
|
26940
27024
|
/* @__PURE__ */ jsxs5("span", {
|
|
26941
|
-
fg:
|
|
27025
|
+
fg: theme.fg,
|
|
26942
27026
|
children: [
|
|
26943
27027
|
" ",
|
|
26944
27028
|
aheadBehindStr
|
|
@@ -26949,11 +27033,11 @@ function DetailsContent({ selectedRow }) {
|
|
|
26949
27033
|
/* @__PURE__ */ jsxs5("text", {
|
|
26950
27034
|
children: [
|
|
26951
27035
|
/* @__PURE__ */ jsx5("span", {
|
|
26952
|
-
fg:
|
|
27036
|
+
fg: theme.dim,
|
|
26953
27037
|
children: "Deps:"
|
|
26954
27038
|
}),
|
|
26955
27039
|
/* @__PURE__ */ jsxs5("span", {
|
|
26956
|
-
fg:
|
|
27040
|
+
fg: theme.fg,
|
|
26957
27041
|
children: [
|
|
26958
27042
|
" ",
|
|
26959
27043
|
depsStrategy
|
|
@@ -26964,11 +27048,11 @@ function DetailsContent({ selectedRow }) {
|
|
|
26964
27048
|
rebaseStatus && /* @__PURE__ */ jsxs5("text", {
|
|
26965
27049
|
children: [
|
|
26966
27050
|
/* @__PURE__ */ jsx5("span", {
|
|
26967
|
-
fg:
|
|
27051
|
+
fg: theme.dim,
|
|
26968
27052
|
children: "Rebase:"
|
|
26969
27053
|
}),
|
|
26970
27054
|
/* @__PURE__ */ jsxs5("span", {
|
|
26971
|
-
fg:
|
|
27055
|
+
fg: theme.error,
|
|
26972
27056
|
children: [
|
|
26973
27057
|
" ",
|
|
26974
27058
|
rebaseStatus
|
|
@@ -26982,7 +27066,7 @@ function DetailsContent({ selectedRow }) {
|
|
|
26982
27066
|
style: { marginTop: 1 },
|
|
26983
27067
|
children: [
|
|
26984
27068
|
/* @__PURE__ */ jsxs5("span", {
|
|
26985
|
-
fg:
|
|
27069
|
+
fg: theme.accent,
|
|
26986
27070
|
children: [
|
|
26987
27071
|
"PR #",
|
|
26988
27072
|
prNumber,
|
|
@@ -26990,7 +27074,7 @@ function DetailsContent({ selectedRow }) {
|
|
|
26990
27074
|
]
|
|
26991
27075
|
}),
|
|
26992
27076
|
/* @__PURE__ */ jsxs5("span", {
|
|
26993
|
-
fg:
|
|
27077
|
+
fg: theme.dim,
|
|
26994
27078
|
children: [
|
|
26995
27079
|
" ",
|
|
26996
27080
|
prState
|
|
@@ -27001,11 +27085,11 @@ function DetailsContent({ selectedRow }) {
|
|
|
27001
27085
|
prChecks && /* @__PURE__ */ jsxs5("text", {
|
|
27002
27086
|
children: [
|
|
27003
27087
|
/* @__PURE__ */ jsx5("span", {
|
|
27004
|
-
fg:
|
|
27088
|
+
fg: theme.dim,
|
|
27005
27089
|
children: "Checks:"
|
|
27006
27090
|
}),
|
|
27007
27091
|
/* @__PURE__ */ jsxs5("span", {
|
|
27008
|
-
fg:
|
|
27092
|
+
fg: theme.fg,
|
|
27009
27093
|
children: [
|
|
27010
27094
|
" ",
|
|
27011
27095
|
prChecks
|
|
@@ -27019,15 +27103,15 @@ function DetailsContent({ selectedRow }) {
|
|
|
27019
27103
|
selectable: false,
|
|
27020
27104
|
children: [
|
|
27021
27105
|
/* @__PURE__ */ jsx5("span", {
|
|
27022
|
-
fg:
|
|
27106
|
+
fg: theme.dim,
|
|
27023
27107
|
children: "URL:"
|
|
27024
27108
|
}),
|
|
27025
27109
|
/* @__PURE__ */ jsx5("span", {
|
|
27026
|
-
fg:
|
|
27110
|
+
fg: theme.accent,
|
|
27027
27111
|
children: ` ${prUrl}`
|
|
27028
27112
|
}),
|
|
27029
27113
|
/* @__PURE__ */ jsx5("span", {
|
|
27030
|
-
fg:
|
|
27114
|
+
fg: theme.dim,
|
|
27031
27115
|
children: " ↗ click"
|
|
27032
27116
|
})
|
|
27033
27117
|
]
|
|
@@ -27040,7 +27124,7 @@ function DetailsContent({ selectedRow }) {
|
|
|
27040
27124
|
style: { marginTop: 1 },
|
|
27041
27125
|
children: [
|
|
27042
27126
|
/* @__PURE__ */ jsxs5("text", {
|
|
27043
|
-
fg:
|
|
27127
|
+
fg: theme.warning,
|
|
27044
27128
|
children: [
|
|
27045
27129
|
"Dirty Files (",
|
|
27046
27130
|
dirtyFiles.length,
|
|
@@ -27048,14 +27132,14 @@ function DetailsContent({ selectedRow }) {
|
|
|
27048
27132
|
]
|
|
27049
27133
|
}),
|
|
27050
27134
|
dirtyFiles.slice(0, 20).map((file2, idx) => /* @__PURE__ */ jsxs5("text", {
|
|
27051
|
-
fg:
|
|
27135
|
+
fg: theme.warning,
|
|
27052
27136
|
children: [
|
|
27053
27137
|
" ",
|
|
27054
27138
|
file2
|
|
27055
27139
|
]
|
|
27056
27140
|
}, idx)),
|
|
27057
27141
|
dirtyFiles.length > 20 && /* @__PURE__ */ jsxs5("text", {
|
|
27058
|
-
fg:
|
|
27142
|
+
fg: theme.warning,
|
|
27059
27143
|
children: [
|
|
27060
27144
|
" ...and ",
|
|
27061
27145
|
dirtyFiles.length - 20,
|
|
@@ -27069,13 +27153,13 @@ function DetailsContent({ selectedRow }) {
|
|
|
27069
27153
|
style: { marginTop: 1 },
|
|
27070
27154
|
children: [
|
|
27071
27155
|
/* @__PURE__ */ jsx5("text", {
|
|
27072
|
-
fg:
|
|
27156
|
+
fg: theme.dim,
|
|
27073
27157
|
children: "Actions:"
|
|
27074
27158
|
}),
|
|
27075
27159
|
!isMainCheckout && /* @__PURE__ */ jsxs5(Fragment, {
|
|
27076
27160
|
children: [
|
|
27077
27161
|
/* @__PURE__ */ jsxs5("text", {
|
|
27078
|
-
fg:
|
|
27162
|
+
fg: theme.dim,
|
|
27079
27163
|
children: [
|
|
27080
27164
|
" i install deps (",
|
|
27081
27165
|
depsStrategy,
|
|
@@ -27083,21 +27167,21 @@ function DetailsContent({ selectedRow }) {
|
|
|
27083
27167
|
]
|
|
27084
27168
|
}),
|
|
27085
27169
|
/* @__PURE__ */ jsx5("text", {
|
|
27086
|
-
fg:
|
|
27170
|
+
fg: theme.dim,
|
|
27087
27171
|
children: " m rename worktree"
|
|
27088
27172
|
})
|
|
27089
27173
|
]
|
|
27090
27174
|
}),
|
|
27091
27175
|
/* @__PURE__ */ jsx5("text", {
|
|
27092
|
-
fg:
|
|
27176
|
+
fg: theme.dim,
|
|
27093
27177
|
children: " p pull branch"
|
|
27094
27178
|
}),
|
|
27095
27179
|
/* @__PURE__ */ jsx5("text", {
|
|
27096
|
-
fg:
|
|
27180
|
+
fg: theme.dim,
|
|
27097
27181
|
children: " b rebase · s sync · o open in IDE · d remove"
|
|
27098
27182
|
}),
|
|
27099
27183
|
/* @__PURE__ */ jsx5("text", {
|
|
27100
|
-
fg:
|
|
27184
|
+
fg: theme.dim,
|
|
27101
27185
|
children: " t new terminal session (max 5)"
|
|
27102
27186
|
})
|
|
27103
27187
|
]
|
|
@@ -27111,6 +27195,8 @@ var init_DetailsTab = __esm(() => {
|
|
|
27111
27195
|
});
|
|
27112
27196
|
|
|
27113
27197
|
// src/lib/changes.ts
|
|
27198
|
+
import fs34 from "node:fs";
|
|
27199
|
+
import path37 from "node:path";
|
|
27114
27200
|
function cacheKey(opts, filePath) {
|
|
27115
27201
|
return [opts.repoPath, opts.branch, opts.scope, filePath ?? ""].join("\x00");
|
|
27116
27202
|
}
|
|
@@ -27214,7 +27300,7 @@ async function getChangedFiles(opts) {
|
|
|
27214
27300
|
return [];
|
|
27215
27301
|
const key = cacheKey(opts);
|
|
27216
27302
|
const cached2 = listCache.get(key);
|
|
27217
|
-
if (!opts.invalidate && cached2?.head === head)
|
|
27303
|
+
if (!opts.invalidate && cached2?.head === head && opts.scope !== "worktree")
|
|
27218
27304
|
return cached2.value;
|
|
27219
27305
|
try {
|
|
27220
27306
|
const baseArgs = await diffArgs(opts);
|
|
@@ -27231,6 +27317,29 @@ async function getChangedFiles(opts) {
|
|
|
27231
27317
|
if (!await isSubmodule(opts.repoPath, file2.path))
|
|
27232
27318
|
filtered.push(file2);
|
|
27233
27319
|
}
|
|
27320
|
+
if (opts.scope === "worktree") {
|
|
27321
|
+
try {
|
|
27322
|
+
const untrackedRaw = await gitExec(["-C", opts.repoPath, "ls-files", "--others", "--exclude-standard", "-z"]);
|
|
27323
|
+
const untrackedPaths = untrackedRaw.split("\x00").filter(Boolean);
|
|
27324
|
+
for (const p of untrackedPaths) {
|
|
27325
|
+
if (filtered.some((f) => f.path === p))
|
|
27326
|
+
continue;
|
|
27327
|
+
if (await isSubmodule(opts.repoPath, p))
|
|
27328
|
+
continue;
|
|
27329
|
+
try {
|
|
27330
|
+
const full = path37.join(opts.repoPath, p);
|
|
27331
|
+
const buf = fs34.readFileSync(full);
|
|
27332
|
+
const binary = buf.includes(0);
|
|
27333
|
+
const str = binary ? "" : buf.toString("utf8");
|
|
27334
|
+
const added = binary ? 0 : str.split(`
|
|
27335
|
+
`).length;
|
|
27336
|
+
filtered.push({ path: p, status: "A", added, removed: 0, binary });
|
|
27337
|
+
} catch {
|
|
27338
|
+
filtered.push({ path: p, status: "A", added: 1, removed: 0, binary: false });
|
|
27339
|
+
}
|
|
27340
|
+
}
|
|
27341
|
+
} catch {}
|
|
27342
|
+
}
|
|
27234
27343
|
listCache.set(key, { head, value: filtered });
|
|
27235
27344
|
return filtered;
|
|
27236
27345
|
} catch {
|
|
@@ -27266,7 +27375,26 @@ async function getFileDiff(opts) {
|
|
|
27266
27375
|
const args = await diffArgs(opts, opts.filePath);
|
|
27267
27376
|
if (!args)
|
|
27268
27377
|
return empty;
|
|
27269
|
-
|
|
27378
|
+
let rawDiff = await gitExec(args);
|
|
27379
|
+
if (!rawDiff && opts.scope === "worktree" && changedFile.status === "A") {
|
|
27380
|
+
try {
|
|
27381
|
+
const full = path37.join(opts.repoPath, opts.filePath);
|
|
27382
|
+
const buf = fs34.readFileSync(full);
|
|
27383
|
+
if (!buf.includes(0)) {
|
|
27384
|
+
const content = buf.toString("utf8");
|
|
27385
|
+
const lines = content.split(`
|
|
27386
|
+
`);
|
|
27387
|
+
const header = `diff --git a/${opts.filePath} b/${opts.filePath}
|
|
27388
|
+
new file mode 100644
|
|
27389
|
+
--- /dev/null
|
|
27390
|
+
+++ b/${opts.filePath}
|
|
27391
|
+
@@ -0,0 +1,${lines.length} @@
|
|
27392
|
+
`;
|
|
27393
|
+
rawDiff = header + lines.map((l) => `+${l}`).join(`
|
|
27394
|
+
`);
|
|
27395
|
+
}
|
|
27396
|
+
} catch {}
|
|
27397
|
+
}
|
|
27270
27398
|
const { diff, truncated } = truncateDiff(rawDiff);
|
|
27271
27399
|
const value = { ...empty, diff, truncated };
|
|
27272
27400
|
diffCache.set(key, { head, value });
|
|
@@ -27932,7 +28060,9 @@ var init_Divider = __esm(() => {
|
|
|
27932
28060
|
|
|
27933
28061
|
// src/tui/components/Overlay.tsx
|
|
27934
28062
|
import { jsx as jsx9 } from "@opentui/react/jsx-runtime";
|
|
27935
|
-
function Overlay({ title, borderColor
|
|
28063
|
+
function Overlay({ title, borderColor, width = 64, children }) {
|
|
28064
|
+
const theme = useTheme();
|
|
28065
|
+
const resolvedBorder = borderColor ?? theme.border;
|
|
27936
28066
|
return /* @__PURE__ */ jsx9("box", {
|
|
27937
28067
|
id: "overlay-scrim",
|
|
27938
28068
|
position: "absolute",
|
|
@@ -27940,7 +28070,7 @@ function Overlay({ title, borderColor = tokens.border, width = 64, children }) {
|
|
|
27940
28070
|
height: "100%",
|
|
27941
28071
|
top: 0,
|
|
27942
28072
|
left: 0,
|
|
27943
|
-
backgroundColor:
|
|
28073
|
+
backgroundColor: theme.scrim,
|
|
27944
28074
|
zIndex: 100,
|
|
27945
28075
|
flexDirection: "row",
|
|
27946
28076
|
justifyContent: "center",
|
|
@@ -27949,13 +28079,13 @@ function Overlay({ title, borderColor = tokens.border, width = 64, children }) {
|
|
|
27949
28079
|
id: "overlay-panel",
|
|
27950
28080
|
width,
|
|
27951
28081
|
border: true,
|
|
27952
|
-
borderColor,
|
|
28082
|
+
borderColor: resolvedBorder,
|
|
27953
28083
|
title,
|
|
27954
|
-
titleColor:
|
|
28084
|
+
titleColor: resolvedBorder,
|
|
27955
28085
|
paddingX: 2,
|
|
27956
28086
|
paddingY: 1,
|
|
27957
28087
|
flexDirection: "column",
|
|
27958
|
-
backgroundColor:
|
|
28088
|
+
backgroundColor: theme.panelBg,
|
|
27959
28089
|
children
|
|
27960
28090
|
})
|
|
27961
28091
|
});
|
|
@@ -27967,85 +28097,132 @@ var init_Overlay = __esm(() => {
|
|
|
27967
28097
|
// src/tui/components/HelpOverlay.tsx
|
|
27968
28098
|
import { jsx as jsx10, jsxs as jsxs8 } from "@opentui/react/jsx-runtime";
|
|
27969
28099
|
function HelpOverlay() {
|
|
28100
|
+
const theme = useTheme();
|
|
27970
28101
|
return /* @__PURE__ */ jsxs8(Overlay, {
|
|
27971
28102
|
title: "Help",
|
|
27972
|
-
borderColor:
|
|
27973
|
-
width:
|
|
28103
|
+
borderColor: theme.border,
|
|
28104
|
+
width: 100,
|
|
27974
28105
|
children: [
|
|
27975
28106
|
/* @__PURE__ */ jsx10("box", {
|
|
27976
28107
|
flexDirection: "column",
|
|
27977
|
-
style: { maxHeight:
|
|
28108
|
+
style: { maxHeight: 38 },
|
|
27978
28109
|
children: /* @__PURE__ */ jsx10("scrollbox", {
|
|
27979
28110
|
flexGrow: 1,
|
|
27980
|
-
children:
|
|
27981
|
-
flexDirection: "
|
|
27982
|
-
|
|
28111
|
+
children: HELP_GROUPS.map((group) => /* @__PURE__ */ jsxs8("box", {
|
|
28112
|
+
flexDirection: "column",
|
|
28113
|
+
style: { marginBottom: 1 },
|
|
27983
28114
|
children: [
|
|
27984
28115
|
/* @__PURE__ */ jsx10("box", {
|
|
27985
|
-
|
|
27986
|
-
flexShrink: 0,
|
|
27987
|
-
justifyContent: "flex-end",
|
|
28116
|
+
style: { marginBottom: 1 },
|
|
27988
28117
|
children: /* @__PURE__ */ jsx10("text", {
|
|
27989
|
-
fg:
|
|
27990
|
-
|
|
28118
|
+
fg: theme.accent,
|
|
28119
|
+
attributes: 1,
|
|
28120
|
+
children: group.title
|
|
27991
28121
|
})
|
|
27992
28122
|
}),
|
|
27993
|
-
/* @__PURE__ */
|
|
27994
|
-
|
|
27995
|
-
|
|
27996
|
-
|
|
27997
|
-
|
|
27998
|
-
|
|
27999
|
-
|
|
28000
|
-
|
|
28001
|
-
|
|
28002
|
-
|
|
28003
|
-
|
|
28123
|
+
group.entries.map(([key, desc]) => /* @__PURE__ */ jsxs8("box", {
|
|
28124
|
+
flexDirection: "row",
|
|
28125
|
+
gap: 1,
|
|
28126
|
+
children: [
|
|
28127
|
+
/* @__PURE__ */ jsx10("box", {
|
|
28128
|
+
width: 22,
|
|
28129
|
+
flexShrink: 0,
|
|
28130
|
+
justifyContent: "flex-end",
|
|
28131
|
+
children: /* @__PURE__ */ jsx10("text", {
|
|
28132
|
+
fg: theme.accent,
|
|
28133
|
+
children: key
|
|
28134
|
+
})
|
|
28135
|
+
}),
|
|
28136
|
+
/* @__PURE__ */ jsx10("text", {
|
|
28137
|
+
fg: theme.dim,
|
|
28138
|
+
children: "-"
|
|
28139
|
+
}),
|
|
28140
|
+
/* @__PURE__ */ jsx10("box", {
|
|
28141
|
+
flexGrow: 1,
|
|
28142
|
+
children: /* @__PURE__ */ jsx10("text", {
|
|
28143
|
+
fg: theme.fg,
|
|
28144
|
+
children: desc
|
|
28145
|
+
})
|
|
28146
|
+
})
|
|
28147
|
+
]
|
|
28148
|
+
}, key))
|
|
28004
28149
|
]
|
|
28005
|
-
},
|
|
28150
|
+
}, group.title))
|
|
28006
28151
|
})
|
|
28007
28152
|
}),
|
|
28008
28153
|
/* @__PURE__ */ jsx10("text", {
|
|
28009
|
-
style: { marginTop: 1, fg:
|
|
28154
|
+
style: { marginTop: 1, fg: theme.dim },
|
|
28010
28155
|
children: "Press any key to close"
|
|
28011
28156
|
})
|
|
28012
28157
|
]
|
|
28013
28158
|
});
|
|
28014
28159
|
}
|
|
28015
|
-
var
|
|
28160
|
+
var HELP_GROUPS;
|
|
28016
28161
|
var init_HelpOverlay = __esm(() => {
|
|
28017
28162
|
init_Overlay();
|
|
28018
28163
|
init_theme();
|
|
28019
|
-
|
|
28020
|
-
|
|
28021
|
-
|
|
28022
|
-
|
|
28023
|
-
|
|
28024
|
-
|
|
28025
|
-
|
|
28026
|
-
|
|
28027
|
-
|
|
28028
|
-
|
|
28029
|
-
|
|
28030
|
-
|
|
28031
|
-
|
|
28032
|
-
|
|
28033
|
-
|
|
28034
|
-
|
|
28035
|
-
|
|
28036
|
-
|
|
28037
|
-
|
|
28038
|
-
|
|
28039
|
-
|
|
28040
|
-
|
|
28041
|
-
|
|
28042
|
-
|
|
28043
|
-
|
|
28044
|
-
|
|
28045
|
-
|
|
28046
|
-
|
|
28047
|
-
|
|
28048
|
-
|
|
28164
|
+
HELP_GROUPS = [
|
|
28165
|
+
{
|
|
28166
|
+
title: "Navigation & Filtering",
|
|
28167
|
+
entries: [
|
|
28168
|
+
["↑/↓, k/j", "Navigate list"],
|
|
28169
|
+
["/", "Filter list"],
|
|
28170
|
+
["Space", "Multi-select"],
|
|
28171
|
+
["q/esc", "Quit"]
|
|
28172
|
+
]
|
|
28173
|
+
},
|
|
28174
|
+
{
|
|
28175
|
+
title: "Worktree Lifecycle",
|
|
28176
|
+
entries: [
|
|
28177
|
+
["n", "Create new worktree (pick dependency strategy)"],
|
|
28178
|
+
["d", "Remove selected worktree(s)"],
|
|
28179
|
+
["m", "Rename selected worktree (branch + directory)"],
|
|
28180
|
+
["o", "Open selected in IDE"],
|
|
28181
|
+
["f", "Fetch main for selected repo(s)"],
|
|
28182
|
+
["p", "Pull latest changes for selected branch(es)"],
|
|
28183
|
+
["P (shift+p)", "Pull PR by link (force-override if exists)"],
|
|
28184
|
+
["b", "Rebase selected onto base (or main)"],
|
|
28185
|
+
["s", "Sync selected (env files + hooks)"],
|
|
28186
|
+
["i", "Install dependencies in selection (worktrees and main)"]
|
|
28187
|
+
]
|
|
28188
|
+
},
|
|
28189
|
+
{
|
|
28190
|
+
title: "Favorites & Workspaces",
|
|
28191
|
+
entries: [
|
|
28192
|
+
["F (shift+f)", "Pin/unpin selected repo (favorites float to top)"],
|
|
28193
|
+
["W (shift+w)", "Scope list to a workspace's members (press again to clear)"]
|
|
28194
|
+
]
|
|
28195
|
+
},
|
|
28196
|
+
{
|
|
28197
|
+
title: "Terminal & Tabs",
|
|
28198
|
+
entries: [
|
|
28199
|
+
["t", "New terminal session (max 5 per worktree)"],
|
|
28200
|
+
["click tab", "Switch Details / Changes / Session tabs"],
|
|
28201
|
+
["Ctrl+G / click table", "Leave terminal focus — focused terminal gets all keys"]
|
|
28202
|
+
]
|
|
28203
|
+
},
|
|
28204
|
+
{
|
|
28205
|
+
title: "Changes Explorer (only for dirty worktrees)",
|
|
28206
|
+
entries: [
|
|
28207
|
+
["click Changes", "Focus pane — j/k navigates files, s/Tab cycles scope"],
|
|
28208
|
+
["s / Tab", "Cycle scope: worktree (dirty+untracked) → staged → base (vs base branch)"],
|
|
28209
|
+
["j / k, ↑/↓", "Navigate files (when Changes focused, Ctrl+G/Esc to unfocus)"]
|
|
28210
|
+
]
|
|
28211
|
+
},
|
|
28212
|
+
{
|
|
28213
|
+
title: "View & System",
|
|
28214
|
+
entries: [
|
|
28215
|
+
["T (shift+t)", "Cycle theme presets"],
|
|
28216
|
+
["c", "Open configuration"],
|
|
28217
|
+
["e", "View data warnings (when count > 0)"],
|
|
28218
|
+
["r", "Refresh data"],
|
|
28219
|
+
["H", "Action history"],
|
|
28220
|
+
["?", "Toggle help"],
|
|
28221
|
+
["mouse drag", "Select text — copied to clipboard automatically"],
|
|
28222
|
+
["ctrl+shift+c", "Copy selection again (terminals reserve cmd+c)"],
|
|
28223
|
+
["click PR #/URL", "Open pull request in browser"]
|
|
28224
|
+
]
|
|
28225
|
+
}
|
|
28049
28226
|
];
|
|
28050
28227
|
});
|
|
28051
28228
|
|
|
@@ -28231,38 +28408,38 @@ var init_ActionLogModal = __esm(() => {
|
|
|
28231
28408
|
});
|
|
28232
28409
|
|
|
28233
28410
|
// src/lib/history.ts
|
|
28234
|
-
import
|
|
28235
|
-
import
|
|
28411
|
+
import fs35 from "fs";
|
|
28412
|
+
import path38 from "path";
|
|
28236
28413
|
import os4 from "os";
|
|
28237
28414
|
function getHistoryDir() {
|
|
28238
|
-
const stateHome = process.env.XDG_STATE_HOME ??
|
|
28239
|
-
return
|
|
28415
|
+
const stateHome = process.env.XDG_STATE_HOME ?? path38.join(os4.homedir(), ".local", "state");
|
|
28416
|
+
return path38.join(stateHome, "wtx");
|
|
28240
28417
|
}
|
|
28241
28418
|
function getHistoryPath() {
|
|
28242
|
-
return
|
|
28419
|
+
return path38.join(getHistoryDir(), "history.jsonl");
|
|
28243
28420
|
}
|
|
28244
28421
|
function rotateHistory(maxBytes = HISTORY_MAX_BYTES, keepLines = HISTORY_ROTATE_KEEP_LINES) {
|
|
28245
28422
|
const historyPath = getHistoryPath();
|
|
28246
28423
|
let size = 0;
|
|
28247
28424
|
try {
|
|
28248
|
-
size =
|
|
28425
|
+
size = fs35.statSync(historyPath).size;
|
|
28249
28426
|
} catch {
|
|
28250
28427
|
return;
|
|
28251
28428
|
}
|
|
28252
28429
|
if (size <= maxBytes)
|
|
28253
28430
|
return;
|
|
28254
|
-
const lines =
|
|
28431
|
+
const lines = fs35.readFileSync(historyPath, "utf-8").split(`
|
|
28255
28432
|
`).filter(Boolean);
|
|
28256
28433
|
const kept = lines.slice(-keepLines);
|
|
28257
28434
|
const tmpPath = `${historyPath}.tmp.${process.pid}`;
|
|
28258
28435
|
try {
|
|
28259
|
-
|
|
28436
|
+
fs35.writeFileSync(tmpPath, kept.length > 0 ? `${kept.join(`
|
|
28260
28437
|
`)}
|
|
28261
28438
|
` : "", "utf-8");
|
|
28262
|
-
|
|
28439
|
+
fs35.renameSync(tmpPath, historyPath);
|
|
28263
28440
|
} catch (err) {
|
|
28264
|
-
if (
|
|
28265
|
-
|
|
28441
|
+
if (fs35.existsSync(tmpPath)) {
|
|
28442
|
+
fs35.unlinkSync(tmpPath);
|
|
28266
28443
|
}
|
|
28267
28444
|
throw err;
|
|
28268
28445
|
}
|
|
@@ -28270,18 +28447,18 @@ function rotateHistory(maxBytes = HISTORY_MAX_BYTES, keepLines = HISTORY_ROTATE_
|
|
|
28270
28447
|
function appendHistory(entry) {
|
|
28271
28448
|
try {
|
|
28272
28449
|
const dir = getHistoryDir();
|
|
28273
|
-
if (!
|
|
28274
|
-
|
|
28450
|
+
if (!fs35.existsSync(dir)) {
|
|
28451
|
+
fs35.mkdirSync(dir, { recursive: true });
|
|
28275
28452
|
}
|
|
28276
28453
|
rotateHistory();
|
|
28277
|
-
|
|
28454
|
+
fs35.appendFileSync(getHistoryPath(), `${JSON.stringify(entry)}
|
|
28278
28455
|
`, "utf-8");
|
|
28279
28456
|
} catch {}
|
|
28280
28457
|
}
|
|
28281
28458
|
function readRecentHistory(limit = 50) {
|
|
28282
28459
|
let content;
|
|
28283
28460
|
try {
|
|
28284
|
-
content =
|
|
28461
|
+
content = fs35.readFileSync(getHistoryPath(), "utf-8");
|
|
28285
28462
|
} catch {
|
|
28286
28463
|
return [];
|
|
28287
28464
|
}
|
|
@@ -28419,13 +28596,19 @@ function InputModal({ title, placeholder, initialValue, errorMessage: errorMessa
|
|
|
28419
28596
|
borderColor: tokens.accent,
|
|
28420
28597
|
children: /* @__PURE__ */ jsxs12("box", {
|
|
28421
28598
|
flexDirection: "column",
|
|
28599
|
+
width: "100%",
|
|
28422
28600
|
children: [
|
|
28423
|
-
/* @__PURE__ */ jsx14("
|
|
28424
|
-
|
|
28425
|
-
|
|
28426
|
-
|
|
28427
|
-
|
|
28428
|
-
|
|
28601
|
+
/* @__PURE__ */ jsx14("box", {
|
|
28602
|
+
flexGrow: 1,
|
|
28603
|
+
width: "100%",
|
|
28604
|
+
children: /* @__PURE__ */ jsx14("input", {
|
|
28605
|
+
focused: true,
|
|
28606
|
+
value,
|
|
28607
|
+
placeholder,
|
|
28608
|
+
onInput: (val) => setValue(val),
|
|
28609
|
+
onSubmit: () => onSubmit(value),
|
|
28610
|
+
width: "100%"
|
|
28611
|
+
})
|
|
28429
28612
|
}),
|
|
28430
28613
|
errorMessage4 ? /* @__PURE__ */ jsx14("text", {
|
|
28431
28614
|
fg: tokens.error,
|
|
@@ -28992,8 +29175,8 @@ function hasDirectStdinWrite(proc) {
|
|
|
28992
29175
|
function hasWritableStreamStdin(proc) {
|
|
28993
29176
|
return !!proc && proc.stdin instanceof WritableStream;
|
|
28994
29177
|
}
|
|
28995
|
-
function worktreeKeyFor(repoName, branch,
|
|
28996
|
-
return
|
|
29178
|
+
function worktreeKeyFor(repoName, branch, path39) {
|
|
29179
|
+
return path39 || `${repoName}:${branch}`;
|
|
28997
29180
|
}
|
|
28998
29181
|
function nextTerminalSessionLabel(existingCount) {
|
|
28999
29182
|
return `Session ${existingCount + 1}`;
|
|
@@ -29011,13 +29194,13 @@ function useTerminalSessions() {
|
|
|
29011
29194
|
const [sessionsByKey, setSessionsByKey] = useState10(() => new Map);
|
|
29012
29195
|
const procsRef = useRef7(new Map);
|
|
29013
29196
|
const listenersRef = useRef7(new Map);
|
|
29014
|
-
const getSessions = useCallback6((repoName, branch,
|
|
29015
|
-
const key = worktreeKeyFor(repoName, branch,
|
|
29197
|
+
const getSessions = useCallback6((repoName, branch, path39) => {
|
|
29198
|
+
const key = worktreeKeyFor(repoName, branch, path39);
|
|
29016
29199
|
return sessionsByKey.get(key) ?? [];
|
|
29017
29200
|
}, [sessionsByKey]);
|
|
29018
|
-
const getKey = useCallback6((repoName, branch,
|
|
29019
|
-
const createSession = useCallback6((repoName, branch,
|
|
29020
|
-
const key = worktreeKeyFor(repoName, branch,
|
|
29201
|
+
const getKey = useCallback6((repoName, branch, path39) => worktreeKeyFor(repoName, branch, path39), []);
|
|
29202
|
+
const createSession = useCallback6((repoName, branch, path39, opts) => {
|
|
29203
|
+
const key = worktreeKeyFor(repoName, branch, path39);
|
|
29021
29204
|
const existing = sessionsByKey.get(key) ?? [];
|
|
29022
29205
|
if (existing.length >= MAX_TERMINAL_SESSIONS) {
|
|
29023
29206
|
return { session: null, error: `Max ${MAX_TERMINAL_SESSIONS} sessions per worktree` };
|
|
@@ -29030,7 +29213,7 @@ function useTerminalSessions() {
|
|
|
29030
29213
|
id,
|
|
29031
29214
|
label,
|
|
29032
29215
|
worktreeKey: key,
|
|
29033
|
-
worktreePath:
|
|
29216
|
+
worktreePath: path39,
|
|
29034
29217
|
repoName,
|
|
29035
29218
|
branch,
|
|
29036
29219
|
lines: [],
|
|
@@ -29115,7 +29298,7 @@ function useTerminalSessions() {
|
|
|
29115
29298
|
throw new Error("pty not available");
|
|
29116
29299
|
}
|
|
29117
29300
|
const proc = Bun.spawn([shell], {
|
|
29118
|
-
cwd:
|
|
29301
|
+
cwd: path39 || process.cwd(),
|
|
29119
29302
|
env: { ...process.env, TERM: "xterm-256color", COLORTERM: "truecolor", WTX_SESSION: label },
|
|
29120
29303
|
terminal: {
|
|
29121
29304
|
cols,
|
|
@@ -29150,7 +29333,7 @@ function useTerminalSessions() {
|
|
|
29150
29333
|
session.usePty = false;
|
|
29151
29334
|
try {
|
|
29152
29335
|
const proc = Bun.spawn([shell], {
|
|
29153
|
-
cwd:
|
|
29336
|
+
cwd: path39 || process.cwd(),
|
|
29154
29337
|
stdin: "pipe",
|
|
29155
29338
|
stdout: "pipe",
|
|
29156
29339
|
stderr: "pipe",
|
|
@@ -29198,9 +29381,9 @@ function useTerminalSessions() {
|
|
|
29198
29381
|
} catch {}
|
|
29199
29382
|
}
|
|
29200
29383
|
if (session.usePty) {
|
|
29201
|
-
session.lines = [`${label} — ${
|
|
29384
|
+
session.lines = [`${label} — ${path39} (${shell}) [pty]`, ""];
|
|
29202
29385
|
} else if (!session.spawnError && session.lines.length === 0) {
|
|
29203
|
-
session.lines = [`${label} — ${
|
|
29386
|
+
session.lines = [`${label} — ${path39} (${shell})`, ""];
|
|
29204
29387
|
}
|
|
29205
29388
|
setSessionsByKey((prev) => {
|
|
29206
29389
|
const next = new Map(prev);
|
|
@@ -29210,8 +29393,8 @@ function useTerminalSessions() {
|
|
|
29210
29393
|
});
|
|
29211
29394
|
return { session };
|
|
29212
29395
|
}, [sessionsByKey]);
|
|
29213
|
-
const removeSession = useCallback6((repoName, branch,
|
|
29214
|
-
const key = worktreeKeyFor(repoName, branch,
|
|
29396
|
+
const removeSession = useCallback6((repoName, branch, path39, id) => {
|
|
29397
|
+
const key = worktreeKeyFor(repoName, branch, path39);
|
|
29215
29398
|
setSessionsByKey((prev) => {
|
|
29216
29399
|
const next = new Map(prev);
|
|
29217
29400
|
const list = next.get(key);
|
|
@@ -29240,8 +29423,8 @@ function useTerminalSessions() {
|
|
|
29240
29423
|
return next;
|
|
29241
29424
|
});
|
|
29242
29425
|
}, []);
|
|
29243
|
-
const sendInput = useCallback6((repoName, branch,
|
|
29244
|
-
const key = worktreeKeyFor(repoName, branch,
|
|
29426
|
+
const sendInput = useCallback6((repoName, branch, path39, id, data) => {
|
|
29427
|
+
const key = worktreeKeyFor(repoName, branch, path39);
|
|
29245
29428
|
const list = sessionsByKey.get(key) ?? [];
|
|
29246
29429
|
const sess = list.find((s) => s.id === id);
|
|
29247
29430
|
if (!sess?.proc)
|
|
@@ -29289,8 +29472,8 @@ function useTerminalSessions() {
|
|
|
29289
29472
|
}
|
|
29290
29473
|
}
|
|
29291
29474
|
}, [sessionsByKey]);
|
|
29292
|
-
const resizeSession = useCallback6((repoName, branch,
|
|
29293
|
-
const key = worktreeKeyFor(repoName, branch,
|
|
29475
|
+
const resizeSession = useCallback6((repoName, branch, path39, id, cols, rows) => {
|
|
29476
|
+
const key = worktreeKeyFor(repoName, branch, path39);
|
|
29294
29477
|
setSessionsByKey((prev) => {
|
|
29295
29478
|
const list = prev.get(key);
|
|
29296
29479
|
if (!list)
|
|
@@ -29682,6 +29865,7 @@ function App({ opts }) {
|
|
|
29682
29865
|
const [activeTabByWorktree, setActiveTabByWorktree] = useState11(() => new Map);
|
|
29683
29866
|
const [recentTerminalSessionIds, setRecentTerminalSessionIds] = useState11([]);
|
|
29684
29867
|
const [terminalFocused, setTerminalFocused] = useState11(false);
|
|
29868
|
+
const [changesFocused, setChangesFocused] = useState11(false);
|
|
29685
29869
|
const { width: termW, height: termH } = useTerminalDimensions();
|
|
29686
29870
|
const totalWidth = termW || renderer.width || 80;
|
|
29687
29871
|
const totalHeight = termH || renderer.terminalHeight || 24;
|
|
@@ -29799,7 +29983,17 @@ function App({ opts }) {
|
|
|
29799
29983
|
const baseFiltered = useMemo3(() => blocks.map((b) => ({
|
|
29800
29984
|
...b,
|
|
29801
29985
|
rows: b.rows.filter((r) => matchesFilter(r, filterText) && matchesWorkspace(r, workspaceMemberSet))
|
|
29802
|
-
})).filter((b) =>
|
|
29986
|
+
})).filter((b) => {
|
|
29987
|
+
if (b.rows.length > 0)
|
|
29988
|
+
return true;
|
|
29989
|
+
if (workspaceMemberSet !== null)
|
|
29990
|
+
return false;
|
|
29991
|
+
if (pendingRepos.includes(b.repoName))
|
|
29992
|
+
return true;
|
|
29993
|
+
if (!filterText)
|
|
29994
|
+
return true;
|
|
29995
|
+
return matchesFilter({ repoName: b.repoName, branch: "" }, filterText);
|
|
29996
|
+
}), [blocks, filterText, pendingRepos, workspaceMemberSet]);
|
|
29803
29997
|
const missingMemberWarnings = useMemo3(() => {
|
|
29804
29998
|
if (!activeWorkspace)
|
|
29805
29999
|
return [];
|
|
@@ -29818,7 +30012,7 @@ function App({ opts }) {
|
|
|
29818
30012
|
const displayBlocks = useMemo3(() => sortBlocks([
|
|
29819
30013
|
...withCreatePlaceholders(baseFiltered, ops.filter((o2) => o2.branch !== undefined).map((o2) => ({ repoName: o2.repoNames[0], branch: o2.branch }))),
|
|
29820
30014
|
...pendingBlocks
|
|
29821
|
-
]), [baseFiltered, ops, pendingBlocks]);
|
|
30015
|
+
], favorites), [baseFiltered, ops, pendingBlocks, favorites]);
|
|
29822
30016
|
const flatRows = useMemo3(() => displayBlocks.flatMap((b) => b.rows.filter((r) => !r.isPendingCreate)), [displayBlocks]);
|
|
29823
30017
|
const totalRows = blocks.flatMap((b) => b.rows).length;
|
|
29824
30018
|
const maxIndex = Math.max(0, flatRows.length - 1);
|
|
@@ -29843,7 +30037,15 @@ function App({ opts }) {
|
|
|
29843
30037
|
}
|
|
29844
30038
|
}, [worktreeKey, activeTabByWorktree]);
|
|
29845
30039
|
useEffect9(() => {
|
|
29846
|
-
if (
|
|
30040
|
+
if (activeTabId === "changes" && selectedRow && (selectedRow.isMainCheckout || selectedRow.dirtyFiles.length === 0)) {
|
|
30041
|
+
setActiveTabByWorktree((prev) => {
|
|
30042
|
+
const next = new Map(prev);
|
|
30043
|
+
next.set(worktreeKey, "details");
|
|
30044
|
+
return next;
|
|
30045
|
+
});
|
|
30046
|
+
return;
|
|
30047
|
+
}
|
|
30048
|
+
if (sessionsForSelected.length === 0 && activeTabId !== "details" && activeTabId !== "changes") {
|
|
29847
30049
|
setActiveTabByWorktree((prev) => {
|
|
29848
30050
|
const next = new Map(prev);
|
|
29849
30051
|
next.set(worktreeKey, "details");
|
|
@@ -29851,7 +30053,7 @@ function App({ opts }) {
|
|
|
29851
30053
|
});
|
|
29852
30054
|
setTerminalFocused(false);
|
|
29853
30055
|
} else if (sessionsForSelected.length > 0) {
|
|
29854
|
-
const exists = sessionsForSelected.some((s) => s.id === activeTabId) || activeTabId === "details";
|
|
30056
|
+
const exists = sessionsForSelected.some((s) => s.id === activeTabId) || activeTabId === "details" || selectedRow && !selectedRow.isMainCheckout && selectedRow.dirtyFiles.length > 0 && activeTabId === "changes";
|
|
29855
30057
|
if (!exists) {
|
|
29856
30058
|
setActiveTabByWorktree((prev) => {
|
|
29857
30059
|
const next = new Map(prev);
|
|
@@ -29860,7 +30062,15 @@ function App({ opts }) {
|
|
|
29860
30062
|
});
|
|
29861
30063
|
}
|
|
29862
30064
|
}
|
|
29863
|
-
}, [sessionsForSelected, activeTabId, worktreeKey]);
|
|
30065
|
+
}, [sessionsForSelected, activeTabId, worktreeKey, selectedRow]);
|
|
30066
|
+
useEffect9(() => {
|
|
30067
|
+
setChangesFocused(false);
|
|
30068
|
+
setTerminalFocused(false);
|
|
30069
|
+
}, [worktreeKey]);
|
|
30070
|
+
useEffect9(() => {
|
|
30071
|
+
if (activeTabId !== "changes")
|
|
30072
|
+
setChangesFocused(false);
|
|
30073
|
+
}, [activeTabId]);
|
|
29864
30074
|
usePaste((event) => {
|
|
29865
30075
|
if (!terminalFocused)
|
|
29866
30076
|
return;
|
|
@@ -29884,7 +30094,8 @@ function App({ opts }) {
|
|
|
29884
30094
|
return next;
|
|
29885
30095
|
});
|
|
29886
30096
|
setTerminalFocused(isSession);
|
|
29887
|
-
|
|
30097
|
+
setChangesFocused(id === "changes" && selectedRow != null && !selectedRow.isMainCheckout && selectedRow.dirtyFiles.length > 0);
|
|
30098
|
+
}, [activeTabId, renderer, worktreeKey, sessionsForSelected, selectedRow]);
|
|
29888
30099
|
const handleAddSession = useCallback7(() => {
|
|
29889
30100
|
if (!selectedRow) {
|
|
29890
30101
|
flash("No worktree selected");
|
|
@@ -29908,13 +30119,31 @@ function App({ opts }) {
|
|
|
29908
30119
|
return next;
|
|
29909
30120
|
});
|
|
29910
30121
|
setTerminalFocused(true);
|
|
30122
|
+
setChangesFocused(false);
|
|
29911
30123
|
flash(`Session ${session.label} created`, 2000);
|
|
29912
30124
|
}, [selectedRow, terminalSessions, flash, totalWidth, splitRatio, totalHeight, activeTabId, sessionsForSelected]);
|
|
29913
30125
|
const handleCloseSession = useCallback7((id) => {
|
|
29914
|
-
|
|
29915
|
-
|
|
29916
|
-
|
|
29917
|
-
const
|
|
30126
|
+
let targetRepo;
|
|
30127
|
+
let targetBranch;
|
|
30128
|
+
let targetPath;
|
|
30129
|
+
for (const sessions of terminalSessions.sessionsByKey.values()) {
|
|
30130
|
+
const found = sessions.find((s) => s.id === id);
|
|
30131
|
+
if (found) {
|
|
30132
|
+
targetRepo = found.repoName;
|
|
30133
|
+
targetBranch = found.branch;
|
|
30134
|
+
targetPath = found.worktreePath;
|
|
30135
|
+
break;
|
|
30136
|
+
}
|
|
30137
|
+
}
|
|
30138
|
+
if (!targetRepo || !targetBranch || !targetPath) {
|
|
30139
|
+
if (!selectedRow)
|
|
30140
|
+
return;
|
|
30141
|
+
targetRepo = selectedRow.repoName;
|
|
30142
|
+
targetBranch = selectedRow.branch;
|
|
30143
|
+
targetPath = selectedRow.path;
|
|
30144
|
+
}
|
|
30145
|
+
terminalSessions.removeSession(targetRepo, targetBranch, targetPath, id);
|
|
30146
|
+
const key = terminalSessions.getKey(targetRepo, targetBranch, targetPath);
|
|
29918
30147
|
setActiveTabByWorktree((prev) => {
|
|
29919
30148
|
const next = new Map(prev);
|
|
29920
30149
|
const current = next.get(key);
|
|
@@ -29950,8 +30179,10 @@ function App({ opts }) {
|
|
|
29950
30179
|
render: ({ worktree }) => /* @__PURE__ */ jsx18(DetailsContent, {
|
|
29951
30180
|
selectedRow: worktree
|
|
29952
30181
|
})
|
|
29953
|
-
}
|
|
29954
|
-
|
|
30182
|
+
}
|
|
30183
|
+
];
|
|
30184
|
+
if (selectedRow != null && !selectedRow.isMainCheckout && selectedRow.dirtyFiles.length > 0) {
|
|
30185
|
+
base2.push({
|
|
29955
30186
|
id: "changes",
|
|
29956
30187
|
label: "Changes",
|
|
29957
30188
|
render: ({ worktree, isActive, focused }) => /* @__PURE__ */ jsx18(ChangesContent, {
|
|
@@ -29960,8 +30191,8 @@ function App({ opts }) {
|
|
|
29960
30191
|
focused,
|
|
29961
30192
|
worktreeKey
|
|
29962
30193
|
})
|
|
29963
|
-
}
|
|
29964
|
-
|
|
30194
|
+
});
|
|
30195
|
+
}
|
|
29965
30196
|
for (const s of sessionsForSelected) {
|
|
29966
30197
|
base2.push({
|
|
29967
30198
|
id: s.id,
|
|
@@ -29975,7 +30206,7 @@ function App({ opts }) {
|
|
|
29975
30206
|
});
|
|
29976
30207
|
}
|
|
29977
30208
|
return base2;
|
|
29978
|
-
}, [sessionsForSelected, worktreeKey]);
|
|
30209
|
+
}, [selectedRow, sessionsForSelected, worktreeKey]);
|
|
29979
30210
|
const { repoVerbs, rowVerbs } = useMemo3(() => {
|
|
29980
30211
|
const rv = new Map;
|
|
29981
30212
|
const nv = new Map;
|
|
@@ -30279,6 +30510,15 @@ function App({ opts }) {
|
|
|
30279
30510
|
}
|
|
30280
30511
|
}, [selectedRow, selection, combinedWarnings, busyRepos, busyRowPaths, doRefresh, flash, blocks, terminalSessions]);
|
|
30281
30512
|
useKeyboard4((key) => {
|
|
30513
|
+
if (changesFocused) {
|
|
30514
|
+
if (key.ctrl && key.name === "g" || key.name === "escape") {
|
|
30515
|
+
setChangesFocused(false);
|
|
30516
|
+
return;
|
|
30517
|
+
}
|
|
30518
|
+
if (["j", "k", "down", "up", "s", "tab"].includes(key.name)) {
|
|
30519
|
+
return;
|
|
30520
|
+
}
|
|
30521
|
+
}
|
|
30282
30522
|
if (terminalFocused) {
|
|
30283
30523
|
if (key.ctrl && key.name === "g") {
|
|
30284
30524
|
setTerminalFocused(false);
|
|
@@ -30720,6 +30960,7 @@ function App({ opts }) {
|
|
|
30720
30960
|
useEffect9(() => () => terminalResizeSchedulerRef.current?.flush(), []);
|
|
30721
30961
|
useEffect9(() => {
|
|
30722
30962
|
setTerminalFocused(false);
|
|
30963
|
+
setChangesFocused(false);
|
|
30723
30964
|
}, [selectedIndex]);
|
|
30724
30965
|
return /* @__PURE__ */ jsx18(ThemeContext.Provider, {
|
|
30725
30966
|
value: currentTheme,
|
|
@@ -30737,7 +30978,10 @@ function App({ opts }) {
|
|
|
30737
30978
|
width: leftCols,
|
|
30738
30979
|
height: "100%",
|
|
30739
30980
|
flexDirection: "column",
|
|
30740
|
-
onMouseDown: () =>
|
|
30981
|
+
onMouseDown: () => {
|
|
30982
|
+
setTerminalFocused(false);
|
|
30983
|
+
setChangesFocused(false);
|
|
30984
|
+
},
|
|
30741
30985
|
children: /* @__PURE__ */ jsx18(WorktreeTable, {
|
|
30742
30986
|
blocks: displayBlocks,
|
|
30743
30987
|
selectedIndex,
|
|
@@ -30750,9 +30994,10 @@ function App({ opts }) {
|
|
|
30750
30994
|
activeWorkspaceName: activeWorkspace?.name ?? null,
|
|
30751
30995
|
onRowClick: (idx) => {
|
|
30752
30996
|
setTerminalFocused(false);
|
|
30997
|
+
setChangesFocused(false);
|
|
30753
30998
|
setSelectedIndex(idx);
|
|
30754
30999
|
},
|
|
30755
|
-
onToggleSelect: (
|
|
31000
|
+
onToggleSelect: (path39) => setSelection((prev) => toggleSelection(prev, path39))
|
|
30756
31001
|
})
|
|
30757
31002
|
}),
|
|
30758
31003
|
/* @__PURE__ */ jsx18(Divider, {
|
|
@@ -30769,13 +31014,18 @@ function App({ opts }) {
|
|
|
30769
31014
|
selectedRow,
|
|
30770
31015
|
tabs: tabsForSelected,
|
|
30771
31016
|
activeId: activeTabId,
|
|
30772
|
-
focused: terminalFocused
|
|
31017
|
+
focused: (terminalFocused || changesFocused) && activeTabId !== "details",
|
|
30773
31018
|
canAdd: sessionsForSelected.length < MAX_TERMINAL_SESSIONS,
|
|
30774
31019
|
onSelect: handleSelectTab,
|
|
30775
31020
|
onAdd: handleAddSession,
|
|
30776
31021
|
onClose: handleCloseSession,
|
|
30777
31022
|
allSessionsFlat,
|
|
30778
31023
|
terminalFocused,
|
|
31024
|
+
changesFocused,
|
|
31025
|
+
onChangesFocus: () => {
|
|
31026
|
+
setChangesFocused(true);
|
|
31027
|
+
setTerminalFocused(false);
|
|
31028
|
+
},
|
|
30779
31029
|
activeTabId,
|
|
30780
31030
|
recentSessionIds: mountedRecentSessionIds,
|
|
30781
31031
|
terminalSessions
|
|
@@ -30788,15 +31038,20 @@ function App({ opts }) {
|
|
|
30788
31038
|
paddingX: 1,
|
|
30789
31039
|
border: true,
|
|
30790
31040
|
borderColor: "magenta",
|
|
31041
|
+
width: "100%",
|
|
30791
31042
|
children: [
|
|
30792
31043
|
/* @__PURE__ */ jsx18("text", {
|
|
30793
31044
|
children: "filter: "
|
|
30794
31045
|
}),
|
|
30795
|
-
/* @__PURE__ */ jsx18("
|
|
30796
|
-
|
|
30797
|
-
|
|
30798
|
-
|
|
30799
|
-
|
|
31046
|
+
/* @__PURE__ */ jsx18("box", {
|
|
31047
|
+
flexGrow: 1,
|
|
31048
|
+
children: /* @__PURE__ */ jsx18("input", {
|
|
31049
|
+
focused: true,
|
|
31050
|
+
value: filterText,
|
|
31051
|
+
placeholder: "Type to filter...",
|
|
31052
|
+
onInput: (v) => setFilterText(v),
|
|
31053
|
+
width: "100%"
|
|
31054
|
+
})
|
|
30800
31055
|
})
|
|
30801
31056
|
]
|
|
30802
31057
|
}),
|
|
@@ -33882,8 +34137,66 @@ init_config();
|
|
|
33882
34137
|
init_log();
|
|
33883
34138
|
init_git();
|
|
33884
34139
|
init_resolver();
|
|
33885
|
-
import
|
|
34140
|
+
import fs23 from "fs";
|
|
33886
34141
|
import path28 from "path";
|
|
34142
|
+
|
|
34143
|
+
// src/lib/worktree-remove.ts
|
|
34144
|
+
init_git();
|
|
34145
|
+
import fs22 from "fs";
|
|
34146
|
+
function isSubmoduleWorktreeError(message) {
|
|
34147
|
+
return message.includes("submodule") || message.includes("containing submodules");
|
|
34148
|
+
}
|
|
34149
|
+
function isMissingWorktreePathError(message) {
|
|
34150
|
+
return message.includes("ENOENT") || message.includes("No such file") || message.includes("does not exist") || message.includes("not a git repository") || message.includes("cannot change to");
|
|
34151
|
+
}
|
|
34152
|
+
function isRecoverableWorktreeRemoveError(message) {
|
|
34153
|
+
return message.includes("not a working tree") || message.includes("already removed") || isSubmoduleWorktreeError(message) || isMissingWorktreePathError(message);
|
|
34154
|
+
}
|
|
34155
|
+
function removePathIfExists(wtPath, opts) {
|
|
34156
|
+
if (!opts.dryRun && fs22.existsSync(wtPath)) {
|
|
34157
|
+
fs22.rmSync(wtPath, { recursive: true, force: true });
|
|
34158
|
+
}
|
|
34159
|
+
}
|
|
34160
|
+
async function pruneWorktreeAdmin(mainPath, opts) {
|
|
34161
|
+
try {
|
|
34162
|
+
await gitExec(["-C", mainPath, "worktree", "prune"], opts);
|
|
34163
|
+
} catch {}
|
|
34164
|
+
}
|
|
34165
|
+
async function removeWorktreeSafely(params) {
|
|
34166
|
+
const { mainPath, wtPath, force, opts } = params;
|
|
34167
|
+
const args = ["-C", mainPath, "worktree", "remove", wtPath];
|
|
34168
|
+
if (force) {
|
|
34169
|
+
args.push("--force");
|
|
34170
|
+
}
|
|
34171
|
+
try {
|
|
34172
|
+
await gitExec(args, opts);
|
|
34173
|
+
return { usedForce: force, manualCleanup: false };
|
|
34174
|
+
} catch (err) {
|
|
34175
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
34176
|
+
if (isSubmoduleWorktreeError(msg) && !force) {
|
|
34177
|
+
try {
|
|
34178
|
+
await gitExec(["-C", mainPath, "worktree", "remove", "--force", wtPath], opts);
|
|
34179
|
+
return { usedForce: true, manualCleanup: false };
|
|
34180
|
+
} catch (forceErr) {
|
|
34181
|
+
const forceMsg = forceErr instanceof Error ? forceErr.message : String(forceErr);
|
|
34182
|
+
if (isRecoverableWorktreeRemoveError(forceMsg)) {
|
|
34183
|
+
await pruneWorktreeAdmin(mainPath, opts);
|
|
34184
|
+
removePathIfExists(wtPath, opts);
|
|
34185
|
+
return { usedForce: true, manualCleanup: true };
|
|
34186
|
+
}
|
|
34187
|
+
throw forceErr;
|
|
34188
|
+
}
|
|
34189
|
+
}
|
|
34190
|
+
if (isRecoverableWorktreeRemoveError(msg)) {
|
|
34191
|
+
await pruneWorktreeAdmin(mainPath, opts);
|
|
34192
|
+
removePathIfExists(wtPath, opts);
|
|
34193
|
+
return { usedForce: force, manualCleanup: true };
|
|
34194
|
+
}
|
|
34195
|
+
throw err;
|
|
34196
|
+
}
|
|
34197
|
+
}
|
|
34198
|
+
|
|
34199
|
+
// src/commands/pull.ts
|
|
33887
34200
|
init_forge();
|
|
33888
34201
|
init_stack();
|
|
33889
34202
|
function registerPullCommand(program2) {
|
|
@@ -34000,7 +34313,7 @@ function registerPullCommand(program2) {
|
|
|
34000
34313
|
const worktrees = await getWorktreeList(target.mainPath);
|
|
34001
34314
|
const existingWorktree = worktrees.find((w) => w.path === wtPath || w.branch === branch);
|
|
34002
34315
|
const localBranchFound = await localBranchExists(target.mainPath, branch, globalOpts);
|
|
34003
|
-
const dirExists = Boolean(existingWorktree) ||
|
|
34316
|
+
const dirExists = Boolean(existingWorktree) || fs23.existsSync(wtPath);
|
|
34004
34317
|
if (localBranchFound || dirExists) {
|
|
34005
34318
|
if (options.force) {
|
|
34006
34319
|
stepWarning(`Branch '${branch}' already exists — --force: will override`, dirExists ? wtPath : "local branch exists");
|
|
@@ -34013,17 +34326,22 @@ function registerPullCommand(program2) {
|
|
|
34013
34326
|
if (options.force && (localBranchFound || dirExists)) {
|
|
34014
34327
|
if (existingWorktree) {
|
|
34015
34328
|
try {
|
|
34016
|
-
await
|
|
34329
|
+
await removeWorktreeSafely({
|
|
34330
|
+
mainPath: target.mainPath,
|
|
34331
|
+
wtPath: existingWorktree.path,
|
|
34332
|
+
force: true,
|
|
34333
|
+
opts: { verbose: globalOpts.verbose, dryRun: globalOpts.dryRun }
|
|
34334
|
+
});
|
|
34017
34335
|
} catch (err) {
|
|
34018
34336
|
const message = err instanceof Error ? err.message : String(err);
|
|
34019
|
-
if (!message
|
|
34337
|
+
if (!isRecoverableWorktreeRemoveError(message)) {
|
|
34020
34338
|
stepWarning("Worktree removal failed", message.split(`
|
|
34021
34339
|
`)[0] ?? message);
|
|
34022
34340
|
}
|
|
34023
34341
|
}
|
|
34024
34342
|
}
|
|
34025
|
-
if (!globalOpts.dryRun &&
|
|
34026
|
-
|
|
34343
|
+
if (!globalOpts.dryRun && fs23.existsSync(wtPath)) {
|
|
34344
|
+
fs23.rmSync(wtPath, { recursive: true, force: true });
|
|
34027
34345
|
}
|
|
34028
34346
|
if (localBranchFound) {
|
|
34029
34347
|
try {
|
|
@@ -34067,7 +34385,7 @@ function registerPullCommand(program2) {
|
|
|
34067
34385
|
}
|
|
34068
34386
|
stepSuccess("Fetched");
|
|
34069
34387
|
if (!globalOpts.dryRun) {
|
|
34070
|
-
|
|
34388
|
+
fs23.mkdirSync(path28.dirname(wtPath), { recursive: true });
|
|
34071
34389
|
}
|
|
34072
34390
|
try {
|
|
34073
34391
|
const createBranchFlag = options.force ? "-B" : "-b";
|
|
@@ -34201,10 +34519,10 @@ function registerPullBranchCommand(program2) {
|
|
|
34201
34519
|
init_config();
|
|
34202
34520
|
init_log();
|
|
34203
34521
|
init_git();
|
|
34522
|
+
import fs25 from "fs";
|
|
34523
|
+
import path30 from "path";
|
|
34204
34524
|
init_resolver();
|
|
34205
34525
|
init_path_safety();
|
|
34206
|
-
import fs24 from "fs";
|
|
34207
|
-
import path30 from "path";
|
|
34208
34526
|
|
|
34209
34527
|
// src/lib/prompts.ts
|
|
34210
34528
|
import * as readline2 from "readline";
|
|
@@ -34252,15 +34570,9 @@ init_workspace();
|
|
|
34252
34570
|
function errorMessage2(err) {
|
|
34253
34571
|
return err instanceof Error ? err.message : String(err);
|
|
34254
34572
|
}
|
|
34255
|
-
function isMissingWorktreePathError(message) {
|
|
34256
|
-
return message.includes("ENOENT") || message.includes("No such file") || message.includes("does not exist") || message.includes("not a git repository") || message.includes("cannot change to");
|
|
34257
|
-
}
|
|
34258
|
-
function isRecoverableWorktreeRemoveError(message) {
|
|
34259
|
-
return message.includes("not a working tree") || message.includes("already removed") || message.includes("submodule") || message.includes("containing submodules") || isMissingWorktreePathError(message);
|
|
34260
|
-
}
|
|
34261
34573
|
function removeExistingPath(wtPath, opts) {
|
|
34262
|
-
if (!opts.dryRun &&
|
|
34263
|
-
|
|
34574
|
+
if (!opts.dryRun && fs25.existsSync(wtPath)) {
|
|
34575
|
+
fs25.rmSync(wtPath, { recursive: true, force: true });
|
|
34264
34576
|
}
|
|
34265
34577
|
}
|
|
34266
34578
|
async function removeLocalBranchIfExists(repoPath, branch, opts) {
|
|
@@ -34349,7 +34661,7 @@ function registerRemoveCommand(program2) {
|
|
|
34349
34661
|
const worktrees = await getWorktreeList(repo.mainPath);
|
|
34350
34662
|
const target = findWorktreeForBranch(worktrees, branch, repo.mainPath, candidatePath);
|
|
34351
34663
|
if (!target) {
|
|
34352
|
-
if (options.force && (
|
|
34664
|
+
if (options.force && (fs25.existsSync(candidatePath) || await localBranchExists(repo.mainPath, branch, globalOpts))) {
|
|
34353
34665
|
if (!await confirmDeletionIfNeeded(repo, candidatePath, options, globalOpts)) {
|
|
34354
34666
|
skipCount++;
|
|
34355
34667
|
continue;
|
|
@@ -34391,7 +34703,7 @@ function registerRemoveCommand(program2) {
|
|
|
34391
34703
|
}
|
|
34392
34704
|
} catch (err) {
|
|
34393
34705
|
const msg = errorMessage2(err);
|
|
34394
|
-
if (isMissingWorktreePathError(msg) || !
|
|
34706
|
+
if (isMissingWorktreePathError(msg) || !fs25.existsSync(wtPath)) {
|
|
34395
34707
|
stepWarning("Worktree path missing", "continuing with removal cleanup");
|
|
34396
34708
|
} else {
|
|
34397
34709
|
stepError("Failed to check for uncommitted changes", msg);
|
|
@@ -34400,7 +34712,7 @@ function registerRemoveCommand(program2) {
|
|
|
34400
34712
|
}
|
|
34401
34713
|
}
|
|
34402
34714
|
}
|
|
34403
|
-
if (options.force && !globalOpts.dryRun && !
|
|
34715
|
+
if (options.force && !globalOpts.dryRun && !fs25.existsSync(wtPath)) {
|
|
34404
34716
|
if (!await confirmDeletionIfNeeded(repo, wtPath, options, globalOpts)) {
|
|
34405
34717
|
skipCount++;
|
|
34406
34718
|
continue;
|
|
@@ -34424,52 +34736,16 @@ function registerRemoveCommand(program2) {
|
|
|
34424
34736
|
skipCount++;
|
|
34425
34737
|
continue;
|
|
34426
34738
|
}
|
|
34427
|
-
const
|
|
34428
|
-
|
|
34429
|
-
|
|
34430
|
-
|
|
34431
|
-
|
|
34432
|
-
|
|
34433
|
-
|
|
34434
|
-
|
|
34435
|
-
const msg = errorMessage2(err);
|
|
34436
|
-
const isSubmodule = msg.includes("submodule") || msg.includes("containing submodules");
|
|
34437
|
-
if (isSubmodule && !options.force && !args.includes("--force")) {
|
|
34438
|
-
stepWarning("Worktree contains submodules — retrying with --force", msg.split(`
|
|
34439
|
-
`)[0] ?? msg);
|
|
34440
|
-
try {
|
|
34441
|
-
await gitExec(["-C", repo.mainPath, "worktree", "remove", "--force", wtPath], globalOpts);
|
|
34442
|
-
stepSuccess("Worktree removed", wtPath);
|
|
34443
|
-
} catch (forceErr) {
|
|
34444
|
-
const forceMsg = errorMessage2(forceErr);
|
|
34445
|
-
if (isRecoverableWorktreeRemoveError(forceMsg)) {
|
|
34446
|
-
stepWarning("Worktree already removed or invalid", forceMsg);
|
|
34447
|
-
try {
|
|
34448
|
-
await gitExec(["-C", repo.mainPath, "worktree", "prune"], globalOpts);
|
|
34449
|
-
} catch (pruneErr) {
|
|
34450
|
-
stepWarning("Worktree prune failed", errorMessage2(pruneErr));
|
|
34451
|
-
}
|
|
34452
|
-
removeExistingPath(wtPath, globalOpts);
|
|
34453
|
-
await removeLocalBranchBestEffort(repo.mainPath, branch, globalOpts);
|
|
34454
|
-
stepSuccess("Worktree removed", wtPath);
|
|
34455
|
-
} else {
|
|
34456
|
-
throw forceErr;
|
|
34457
|
-
}
|
|
34458
|
-
}
|
|
34459
|
-
} else if (isRecoverableWorktreeRemoveError(msg)) {
|
|
34460
|
-
stepWarning("Worktree already removed or invalid", msg);
|
|
34461
|
-
try {
|
|
34462
|
-
await gitExec(["-C", repo.mainPath, "worktree", "prune"], globalOpts);
|
|
34463
|
-
} catch (pruneErr) {
|
|
34464
|
-
stepWarning("Worktree prune failed", errorMessage2(pruneErr));
|
|
34465
|
-
}
|
|
34466
|
-
removeExistingPath(wtPath, globalOpts);
|
|
34467
|
-
await removeLocalBranchBestEffort(repo.mainPath, branch, globalOpts);
|
|
34468
|
-
stepSuccess("Worktree removed", wtPath);
|
|
34469
|
-
} else {
|
|
34470
|
-
throw err;
|
|
34471
|
-
}
|
|
34739
|
+
const result = await removeWorktreeSafely({
|
|
34740
|
+
mainPath: repo.mainPath,
|
|
34741
|
+
wtPath,
|
|
34742
|
+
force: !!options.force,
|
|
34743
|
+
opts: globalOpts
|
|
34744
|
+
});
|
|
34745
|
+
if (result.manualCleanup) {
|
|
34746
|
+
await removeLocalBranchBestEffort(repo.mainPath, branch, globalOpts);
|
|
34472
34747
|
}
|
|
34748
|
+
stepSuccess("Worktree removed", wtPath);
|
|
34473
34749
|
await finishCleanup(repo, wtPath, branch, globalOpts);
|
|
34474
34750
|
await unlinkRemovedWorktreeFromWorkspaces({ workspaceRoot, repo: repo.name, branch });
|
|
34475
34751
|
if (children.length > 0) {
|
|
@@ -34496,9 +34772,9 @@ init_source();
|
|
|
34496
34772
|
init_config();
|
|
34497
34773
|
init_log();
|
|
34498
34774
|
init_git();
|
|
34775
|
+
import path31 from "path";
|
|
34499
34776
|
init_resolver();
|
|
34500
34777
|
init_forge();
|
|
34501
|
-
import path31 from "path";
|
|
34502
34778
|
|
|
34503
34779
|
// src/lib/prune.ts
|
|
34504
34780
|
function selectMergedCandidates(worktrees, mainPath, prMap) {
|
|
@@ -34635,16 +34911,17 @@ function registerPruneCommand(program2) {
|
|
|
34635
34911
|
repoHeader(repo.name);
|
|
34636
34912
|
currentRepo = repo.name;
|
|
34637
34913
|
}
|
|
34638
|
-
const args = ["-C", repo.mainPath, "worktree", "remove", candidate.path];
|
|
34639
|
-
if (options.force) {
|
|
34640
|
-
args.push("--force");
|
|
34641
|
-
}
|
|
34642
34914
|
try {
|
|
34643
|
-
await
|
|
34915
|
+
await removeWorktreeSafely({
|
|
34916
|
+
mainPath: repo.mainPath,
|
|
34917
|
+
wtPath: candidate.path,
|
|
34918
|
+
force: !!options.force,
|
|
34919
|
+
opts: globalOpts
|
|
34920
|
+
});
|
|
34644
34921
|
stepSuccess("Worktree removed", label);
|
|
34645
34922
|
} catch (err) {
|
|
34646
34923
|
const msg = err instanceof Error ? err.message : String(err);
|
|
34647
|
-
if (
|
|
34924
|
+
if (isRecoverableWorktreeRemoveError(msg)) {
|
|
34648
34925
|
stepWarning("Worktree already removed or invalid", msg);
|
|
34649
34926
|
skippedCount++;
|
|
34650
34927
|
continue;
|
|
@@ -34747,7 +35024,7 @@ init_source();
|
|
|
34747
35024
|
init_stack();
|
|
34748
35025
|
init_stack();
|
|
34749
35026
|
import path32 from "path";
|
|
34750
|
-
import
|
|
35027
|
+
import fs26 from "fs";
|
|
34751
35028
|
function buildLsJson(reposData) {
|
|
34752
35029
|
const result = [];
|
|
34753
35030
|
for (const repo of reposData) {
|
|
@@ -34857,7 +35134,7 @@ function registerLsCommand(program2) {
|
|
|
34857
35134
|
statusStr = source_default.blue("[main checkout]");
|
|
34858
35135
|
} else if (wt.isLocked) {
|
|
34859
35136
|
statusStr = source_default.red("locked \uD83D\uDD12");
|
|
34860
|
-
} else if (
|
|
35137
|
+
} else if (fs26.existsSync(wt.path)) {
|
|
34861
35138
|
try {
|
|
34862
35139
|
dirtyFiles = await getDirtyFiles(wt.path);
|
|
34863
35140
|
if (dirtyFiles.length > 0) {
|
|
@@ -34983,7 +35260,7 @@ end
|
|
|
34983
35260
|
init_config();
|
|
34984
35261
|
init_log();
|
|
34985
35262
|
init_git();
|
|
34986
|
-
import
|
|
35263
|
+
import fs27 from "fs";
|
|
34987
35264
|
init_resolver();
|
|
34988
35265
|
init_stack();
|
|
34989
35266
|
function registerRebaseCommand(program2) {
|
|
@@ -34998,7 +35275,7 @@ function registerRebaseCommand(program2) {
|
|
|
34998
35275
|
repoHeader(repo.name);
|
|
34999
35276
|
const mainBranch = await resolveMainBranch(repo, config2);
|
|
35000
35277
|
const wtPath = getWorktreePath(repo, branch);
|
|
35001
|
-
if (!
|
|
35278
|
+
if (!fs27.existsSync(wtPath)) {
|
|
35002
35279
|
stepError("No worktree found", `${wtPath} (skipped)`);
|
|
35003
35280
|
failCount++;
|
|
35004
35281
|
continue;
|
|
@@ -35124,7 +35401,7 @@ init_execa();
|
|
|
35124
35401
|
init_config();
|
|
35125
35402
|
init_log();
|
|
35126
35403
|
init_resolver();
|
|
35127
|
-
import
|
|
35404
|
+
import fs28 from "fs";
|
|
35128
35405
|
import path33 from "path";
|
|
35129
35406
|
function registerSyncCommand(program2) {
|
|
35130
35407
|
program2.command("sync <branch>").description("re-copy sync files + run post_sync").option("--repo <repos...>", "comma-separated list of repos to target").action(async (branch, _options, cmd) => {
|
|
@@ -35136,14 +35413,14 @@ function registerSyncCommand(program2) {
|
|
|
35136
35413
|
for (const repo of repos) {
|
|
35137
35414
|
repoHeader(repo.name);
|
|
35138
35415
|
const wtPath = getWorktreePath(repo, branch);
|
|
35139
|
-
if (!
|
|
35416
|
+
if (!fs28.existsSync(wtPath)) {
|
|
35140
35417
|
stepWarning("No worktree found", `${wtPath} (skipped)`);
|
|
35141
35418
|
continue;
|
|
35142
35419
|
}
|
|
35143
35420
|
try {
|
|
35144
35421
|
if (repo.config.sync_files) {
|
|
35145
35422
|
for (const file2 of repo.config.sync_files) {
|
|
35146
|
-
if (
|
|
35423
|
+
if (fs28.existsSync(path33.join(repo.mainPath, file2))) {
|
|
35147
35424
|
if (!opts.dryRun) {
|
|
35148
35425
|
syncEntry(repo.mainPath, wtPath, file2);
|
|
35149
35426
|
}
|
|
@@ -35196,14 +35473,14 @@ function registerSyncCommand(program2) {
|
|
|
35196
35473
|
}
|
|
35197
35474
|
}
|
|
35198
35475
|
const wtNodeModules = path33.join(wtPath, "node_modules");
|
|
35199
|
-
if (
|
|
35476
|
+
if (fs28.existsSync(wtNodeModules) && fs28.lstatSync(wtNodeModules).isSymbolicLink()) {
|
|
35200
35477
|
const lockfiles = ["yarn.lock", "package-lock.json", "pnpm-lock.yaml", "bun.lockb", "bun.lock"];
|
|
35201
35478
|
for (const lock of lockfiles) {
|
|
35202
35479
|
const mainLock = path33.join(repo.mainPath, lock);
|
|
35203
35480
|
const wtLock = path33.join(wtPath, lock);
|
|
35204
|
-
if (
|
|
35205
|
-
const mainContent =
|
|
35206
|
-
const wtContent =
|
|
35481
|
+
if (fs28.existsSync(mainLock) && fs28.existsSync(wtLock)) {
|
|
35482
|
+
const mainContent = fs28.readFileSync(mainLock);
|
|
35483
|
+
const wtContent = fs28.readFileSync(wtLock);
|
|
35207
35484
|
if (!mainContent.equals(wtContent)) {
|
|
35208
35485
|
stepWarning(`${lock} differs from main — node_modules is symlinked`);
|
|
35209
35486
|
indented(`Run: wtx deps ${branch} --repo ${repo.name} --install`);
|
|
@@ -35232,7 +35509,7 @@ init_config();
|
|
|
35232
35509
|
init_log();
|
|
35233
35510
|
init_resolver();
|
|
35234
35511
|
init_deps();
|
|
35235
|
-
import
|
|
35512
|
+
import fs29 from "fs";
|
|
35236
35513
|
function registerDepsCommand(program2) {
|
|
35237
35514
|
program2.command("deps [branch]").description("Manage node_modules strategy per worktree").option("-r, --repo <repos...>", "Target specific repo(s)").option("--install", "Switch to independent node_modules (run install)").option("--symlink", "Switch to legacy symlinked node_modules").option("--json", "Output machine-readable JSON state").action(async (branch, options) => {
|
|
35238
35515
|
const globalOpts = program2.opts();
|
|
@@ -35269,7 +35546,7 @@ function registerDepsCommand(program2) {
|
|
|
35269
35546
|
continue;
|
|
35270
35547
|
}
|
|
35271
35548
|
const wtPath = getWorktreePath(repo, branch);
|
|
35272
|
-
if (!
|
|
35549
|
+
if (!fs29.existsSync(wtPath)) {
|
|
35273
35550
|
if (options.json) {
|
|
35274
35551
|
jsonResults[repo.name] = { error: "No worktree found", branch };
|
|
35275
35552
|
} else {
|
|
@@ -35327,7 +35604,7 @@ function registerDepsCommand(program2) {
|
|
|
35327
35604
|
init_config();
|
|
35328
35605
|
init_log();
|
|
35329
35606
|
init_resolver();
|
|
35330
|
-
import
|
|
35607
|
+
import fs30 from "fs";
|
|
35331
35608
|
init_git();
|
|
35332
35609
|
async function resolveMainCheckoutPath(repoCtx, branch) {
|
|
35333
35610
|
try {
|
|
@@ -35352,7 +35629,7 @@ function registerOpenCommand(program2) {
|
|
|
35352
35629
|
for (const repo of repos) {
|
|
35353
35630
|
const wtPath = getWorktreePath(repo, branch);
|
|
35354
35631
|
let targetPath = wtPath;
|
|
35355
|
-
if (!
|
|
35632
|
+
if (!fs30.existsSync(wtPath)) {
|
|
35356
35633
|
const mainCheckoutPath = await resolveMainCheckoutPath(repo, branch);
|
|
35357
35634
|
if (!mainCheckoutPath) {
|
|
35358
35635
|
continue;
|
|
@@ -35388,7 +35665,7 @@ import path35 from "path";
|
|
|
35388
35665
|
init_git();
|
|
35389
35666
|
init_resolver();
|
|
35390
35667
|
init_path_safety();
|
|
35391
|
-
import
|
|
35668
|
+
import fs31 from "fs";
|
|
35392
35669
|
import path34 from "path";
|
|
35393
35670
|
init_workspace();
|
|
35394
35671
|
async function getUpstream(wtPath) {
|
|
@@ -35431,7 +35708,7 @@ async function planRename(repo, oldBranch, newBranch, opts) {
|
|
|
35431
35708
|
throw new Error(`Worktree '${oldBranch}' is locked — unlock it before renaming`);
|
|
35432
35709
|
}
|
|
35433
35710
|
const newPath = `${repo.wtRoot}/${newBranch}`;
|
|
35434
|
-
if (
|
|
35711
|
+
if (fs31.existsSync(newPath)) {
|
|
35435
35712
|
throw new Error(`Target path already exists: ${newPath}`);
|
|
35436
35713
|
}
|
|
35437
35714
|
if (!opts.dryRun && await localBranchExists(repo.mainPath, newBranch, opts)) {
|
|
@@ -35464,7 +35741,7 @@ async function renameWorktree(params) {
|
|
|
35464
35741
|
}
|
|
35465
35742
|
await gitExec(["-C", planned.worktreePath, "branch", "-m", oldBranch, newBranch], opts);
|
|
35466
35743
|
try {
|
|
35467
|
-
|
|
35744
|
+
fs31.mkdirSync(path34.dirname(planned.newPath), { recursive: true });
|
|
35468
35745
|
await gitExec(["-C", repo.mainPath, "worktree", "move", planned.worktreePath, planned.newPath], opts);
|
|
35469
35746
|
} catch (err) {
|
|
35470
35747
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -35483,10 +35760,10 @@ async function renameWorktree(params) {
|
|
|
35483
35760
|
const resyncedFiles = [];
|
|
35484
35761
|
const keptLocalSyncFiles = [];
|
|
35485
35762
|
for (const entry of repo.config.sync_files ?? []) {
|
|
35486
|
-
if (!
|
|
35763
|
+
if (!fs31.existsSync(path34.join(repo.mainPath, entry)))
|
|
35487
35764
|
continue;
|
|
35488
35765
|
const destPath = path34.join(planned.newPath, entry);
|
|
35489
|
-
if (!
|
|
35766
|
+
if (!fs31.existsSync(destPath)) {
|
|
35490
35767
|
if (syncEntry(repo.mainPath, planned.newPath, entry)) {
|
|
35491
35768
|
resyncedFiles.push(entry);
|
|
35492
35769
|
}
|
|
@@ -35625,7 +35902,7 @@ init_resolver();
|
|
|
35625
35902
|
init_deps();
|
|
35626
35903
|
init_forge();
|
|
35627
35904
|
init_types2();
|
|
35628
|
-
import
|
|
35905
|
+
import fs32 from "fs";
|
|
35629
35906
|
init_owner();
|
|
35630
35907
|
init_stack();
|
|
35631
35908
|
init_source();
|
|
@@ -35675,7 +35952,7 @@ function registerStatusCommand(program2) {
|
|
|
35675
35952
|
const jsonOutputs = [];
|
|
35676
35953
|
for (const repo of repos) {
|
|
35677
35954
|
const wtPath = getWorktreePath(repo, branch);
|
|
35678
|
-
if (!
|
|
35955
|
+
if (!fs32.existsSync(wtPath)) {
|
|
35679
35956
|
continue;
|
|
35680
35957
|
}
|
|
35681
35958
|
found++;
|
|
@@ -35975,7 +36252,7 @@ function registerPrsCommand(program2) {
|
|
|
35975
36252
|
|
|
35976
36253
|
// src/commands/skill.ts
|
|
35977
36254
|
init_log();
|
|
35978
|
-
import
|
|
36255
|
+
import fs33 from "fs";
|
|
35979
36256
|
import path36 from "path";
|
|
35980
36257
|
import { URL as URL2 } from "url";
|
|
35981
36258
|
var __dirname2 = new URL2(".", import.meta.url).pathname;
|
|
@@ -36132,7 +36409,7 @@ function registerSkillCommand(program2) {
|
|
|
36132
36409
|
}
|
|
36133
36410
|
let projectRoot = path36.resolve(__dirname2, "..", "..");
|
|
36134
36411
|
let skillPath = path36.join(projectRoot, "skills", `${platform2}.md`);
|
|
36135
|
-
if (
|
|
36412
|
+
if (fs33.existsSync(skillPath)) {
|
|
36136
36413
|
process.stdout.write(skillPath + `
|
|
36137
36414
|
`);
|
|
36138
36415
|
} else {
|
|
@@ -36145,8 +36422,8 @@ function registerSkillCommand(program2) {
|
|
|
36145
36422
|
|
|
36146
36423
|
// src/commands/terminal.ts
|
|
36147
36424
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
36148
|
-
import
|
|
36149
|
-
import
|
|
36425
|
+
import fs36 from "node:fs";
|
|
36426
|
+
import path39 from "node:path";
|
|
36150
36427
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
36151
36428
|
function findEntry() {
|
|
36152
36429
|
const candidates = [];
|
|
@@ -36155,26 +36432,26 @@ function findEntry() {
|
|
|
36155
36432
|
try {
|
|
36156
36433
|
const thisPath = fileURLToPath4(import.meta.url);
|
|
36157
36434
|
candidates.push(thisPath);
|
|
36158
|
-
candidates.push(
|
|
36159
|
-
candidates.push(
|
|
36435
|
+
candidates.push(path39.resolve(path39.dirname(thisPath), "../../dist/cli.mjs"));
|
|
36436
|
+
candidates.push(path39.resolve(path39.dirname(thisPath), "../cli.mjs"));
|
|
36160
36437
|
} catch {}
|
|
36161
36438
|
for (const p of candidates) {
|
|
36162
36439
|
try {
|
|
36163
36440
|
if (p.endsWith(".mjs") || p.endsWith(".js")) {
|
|
36164
|
-
if (
|
|
36441
|
+
if (fs36.existsSync(p) && fs36.statSync(p).isFile())
|
|
36165
36442
|
return p;
|
|
36166
|
-
} else if (
|
|
36167
|
-
const stat =
|
|
36443
|
+
} else if (fs36.existsSync(p)) {
|
|
36444
|
+
const stat = fs36.statSync(p);
|
|
36168
36445
|
if (stat.isFile()) {
|
|
36169
|
-
const head =
|
|
36446
|
+
const head = fs36.readFileSync(p, "utf8").slice(0, 1024);
|
|
36170
36447
|
if (head.includes("cli.mjs") || head.includes("#!/usr/bin/env node")) {
|
|
36171
36448
|
if (p.endsWith("wtx") || p.endsWith("cli.mjs")) {
|
|
36172
|
-
const dir =
|
|
36173
|
-
const cli =
|
|
36174
|
-
if (
|
|
36449
|
+
const dir = path39.dirname(fs36.realpathSync(p));
|
|
36450
|
+
const cli = path39.join(dir, "cli.mjs");
|
|
36451
|
+
if (fs36.existsSync(cli))
|
|
36175
36452
|
return cli;
|
|
36176
|
-
const cli2 =
|
|
36177
|
-
if (
|
|
36453
|
+
const cli2 = path39.resolve(dir, "../dist/cli.mjs");
|
|
36454
|
+
if (fs36.existsSync(cli2))
|
|
36178
36455
|
return cli2;
|
|
36179
36456
|
if (head.includes("import") || head.includes("commander"))
|
|
36180
36457
|
return p;
|
|
@@ -36186,9 +36463,9 @@ function findEntry() {
|
|
|
36186
36463
|
}
|
|
36187
36464
|
try {
|
|
36188
36465
|
const thisPath = fileURLToPath4(import.meta.url);
|
|
36189
|
-
const pkgRoot =
|
|
36190
|
-
const cli =
|
|
36191
|
-
if (
|
|
36466
|
+
const pkgRoot = path39.resolve(path39.dirname(thisPath), "../..");
|
|
36467
|
+
const cli = path39.join(pkgRoot, "dist", "cli.mjs");
|
|
36468
|
+
if (fs36.existsSync(cli))
|
|
36192
36469
|
return cli;
|
|
36193
36470
|
} catch {}
|
|
36194
36471
|
return null;
|
|
@@ -36292,7 +36569,7 @@ init_deps();
|
|
|
36292
36569
|
import readline3 from "readline";
|
|
36293
36570
|
init_path_safety();
|
|
36294
36571
|
init_stack();
|
|
36295
|
-
import
|
|
36572
|
+
import fs37 from "fs";
|
|
36296
36573
|
async function runMcpServer(opts = {}) {
|
|
36297
36574
|
const input = opts.input ?? process.stdin;
|
|
36298
36575
|
const output = opts.output ?? process.stdout;
|
|
@@ -36475,7 +36752,7 @@ async function handleToolCall(name, args, config2, _opts) {
|
|
|
36475
36752
|
for (const wt of wts) {
|
|
36476
36753
|
if (!wt.path.startsWith(repo.wtRoot))
|
|
36477
36754
|
continue;
|
|
36478
|
-
const dirtyCount =
|
|
36755
|
+
const dirtyCount = fs37.existsSync(wt.path) ? (await getDirtyFiles(wt.path)).length : 0;
|
|
36479
36756
|
items.push({
|
|
36480
36757
|
repo: repo.name,
|
|
36481
36758
|
branch: wt.branch || null,
|
|
@@ -36503,7 +36780,7 @@ async function handleToolCall(name, args, config2, _opts) {
|
|
|
36503
36780
|
}
|
|
36504
36781
|
const repo = resolveRepos(config2, [args.repo])[0];
|
|
36505
36782
|
const wtPath = getWorktreePath(repo, args.branch);
|
|
36506
|
-
if (!
|
|
36783
|
+
if (!fs37.existsSync(wtPath)) {
|
|
36507
36784
|
throw { isToolError: true, message: `Worktree not found for branch ${args.branch}` };
|
|
36508
36785
|
}
|
|
36509
36786
|
const mainBranch = await resolveMainBranch(repo, config2);
|
|
@@ -36622,13 +36899,18 @@ async function handleToolCall(name, args, config2, _opts) {
|
|
|
36622
36899
|
if (children.length > 0 && args.force !== true) {
|
|
36623
36900
|
throw { isToolError: true, message: `Branch has dependent worktrees: ${children.join(", ")}. Use force:true to override.` };
|
|
36624
36901
|
}
|
|
36625
|
-
if (!args.force &&
|
|
36902
|
+
if (!args.force && fs37.existsSync(wtPath)) {
|
|
36626
36903
|
const dirty = await getDirtyFiles(wtPath);
|
|
36627
36904
|
if (dirty.length > 0) {
|
|
36628
36905
|
throw { isToolError: true, message: `Worktree is dirty. Use force:true to remove it.` };
|
|
36629
36906
|
}
|
|
36630
36907
|
}
|
|
36631
|
-
await
|
|
36908
|
+
await removeWorktreeSafely({
|
|
36909
|
+
mainPath: repo.mainPath,
|
|
36910
|
+
wtPath,
|
|
36911
|
+
force: args.force === true,
|
|
36912
|
+
opts: { verbose: _opts.verbose === true, dryRun: false }
|
|
36913
|
+
});
|
|
36632
36914
|
cleanupEmptyParents(repo.wtRoot, repo.mainPath, wtPath);
|
|
36633
36915
|
await removeStackEntry(repo.mainPath, args.branch, { verbose: _opts.verbose === true, dryRun: false });
|
|
36634
36916
|
return {
|
|
@@ -36641,7 +36923,7 @@ async function handleToolCall(name, args, config2, _opts) {
|
|
|
36641
36923
|
}
|
|
36642
36924
|
const repo = resolveRepos(config2, [args.repo])[0];
|
|
36643
36925
|
const wtPath = getWorktreePath(repo, args.branch);
|
|
36644
|
-
if (!
|
|
36926
|
+
if (!fs37.existsSync(wtPath)) {
|
|
36645
36927
|
throw { isToolError: true, message: `Worktree not found for branch ${args.branch}` };
|
|
36646
36928
|
}
|
|
36647
36929
|
const mainBranch = await resolveMainBranch(repo, config2);
|
|
@@ -36863,8 +37145,8 @@ init_config();
|
|
|
36863
37145
|
init_log();
|
|
36864
37146
|
init_git();
|
|
36865
37147
|
init_resolver();
|
|
36866
|
-
import
|
|
36867
|
-
import
|
|
37148
|
+
import fs38 from "fs";
|
|
37149
|
+
import path40 from "path";
|
|
36868
37150
|
init_workspace();
|
|
36869
37151
|
var DEPS_STRATEGIES2 = ["auto", "link", "symlink", "install", "off"];
|
|
36870
37152
|
function errorMessage4(err) {
|
|
@@ -36874,7 +37156,7 @@ function workspaceRootFor(config2) {
|
|
|
36874
37156
|
return getWorkspaceRoot(config2);
|
|
36875
37157
|
}
|
|
36876
37158
|
function workspacePathFor2(config2, name) {
|
|
36877
|
-
return
|
|
37159
|
+
return path40.join(workspaceRootFor(config2), name);
|
|
36878
37160
|
}
|
|
36879
37161
|
async function resolveWorktreePathForMember(config2, repoName, branch) {
|
|
36880
37162
|
const repos = resolveRepos(config2, [repoName]);
|
|
@@ -36886,7 +37168,7 @@ async function resolveWorktreePathForMember(config2, repoName, branch) {
|
|
|
36886
37168
|
if (match)
|
|
36887
37169
|
return match.path;
|
|
36888
37170
|
const candidate = getWorktreePath(repo, branch);
|
|
36889
|
-
if (
|
|
37171
|
+
if (fs38.existsSync(candidate))
|
|
36890
37172
|
return candidate;
|
|
36891
37173
|
return null;
|
|
36892
37174
|
}
|
|
@@ -36944,7 +37226,7 @@ function registerCreateSub(workspace) {
|
|
|
36944
37226
|
process.exit(1);
|
|
36945
37227
|
}
|
|
36946
37228
|
const wsPath = workspacePathFor2(config2, name);
|
|
36947
|
-
if (
|
|
37229
|
+
if (fs38.existsSync(wsPath)) {
|
|
36948
37230
|
stepError("Workspace already exists", wsPath);
|
|
36949
37231
|
process.exit(1);
|
|
36950
37232
|
}
|
|
@@ -37045,7 +37327,7 @@ function registerAddSub(workspace) {
|
|
|
37045
37327
|
const globalOpts = workspace.parent.opts();
|
|
37046
37328
|
const config2 = loadConfig();
|
|
37047
37329
|
const wsPath = workspacePathFor2(config2, name);
|
|
37048
|
-
if (!
|
|
37330
|
+
if (!fs38.existsSync(wsPath)) {
|
|
37049
37331
|
stepError("Workspace not found", wsPath);
|
|
37050
37332
|
process.exit(1);
|
|
37051
37333
|
}
|
|
@@ -37081,7 +37363,7 @@ function registerRmSub(workspace) {
|
|
|
37081
37363
|
const globalOpts = workspace.parent.opts();
|
|
37082
37364
|
const config2 = loadConfig();
|
|
37083
37365
|
const wsPath = workspacePathFor2(config2, name);
|
|
37084
|
-
if (!
|
|
37366
|
+
if (!fs38.existsSync(wsPath)) {
|
|
37085
37367
|
stepError("Workspace not found", wsPath);
|
|
37086
37368
|
process.exit(1);
|
|
37087
37369
|
}
|
|
@@ -37103,7 +37385,7 @@ function registerRemoveSub(workspace) {
|
|
|
37103
37385
|
const globalOpts = workspace.parent.opts();
|
|
37104
37386
|
const config2 = loadConfig();
|
|
37105
37387
|
const wsPath = workspacePathFor2(config2, name);
|
|
37106
|
-
if (!
|
|
37388
|
+
if (!fs38.existsSync(wsPath)) {
|
|
37107
37389
|
stepError("Workspace not found", wsPath);
|
|
37108
37390
|
process.exit(1);
|
|
37109
37391
|
}
|
|
@@ -37117,7 +37399,7 @@ function registerRemoveSub(workspace) {
|
|
|
37117
37399
|
return;
|
|
37118
37400
|
}
|
|
37119
37401
|
try {
|
|
37120
|
-
|
|
37402
|
+
fs38.rmSync(wsPath, { recursive: true, force: true });
|
|
37121
37403
|
stepSuccess("Workspace removed", wsPath);
|
|
37122
37404
|
summary(`Done — workspace ${name} deleted (member worktrees preserved)`);
|
|
37123
37405
|
} catch (err) {
|
|
@@ -37130,7 +37412,7 @@ function registerVerifySub(workspace) {
|
|
|
37130
37412
|
workspace.command("verify <name>").description("Verify that every workspace member link resolves").action(async (name) => {
|
|
37131
37413
|
const config2 = loadConfig();
|
|
37132
37414
|
const wsPath = workspacePathFor2(config2, name);
|
|
37133
|
-
if (!
|
|
37415
|
+
if (!fs38.existsSync(wsPath)) {
|
|
37134
37416
|
stepError("Workspace not found", wsPath);
|
|
37135
37417
|
process.exit(1);
|
|
37136
37418
|
}
|
|
@@ -37165,7 +37447,7 @@ init_config();
|
|
|
37165
37447
|
init_resolver();
|
|
37166
37448
|
init_log();
|
|
37167
37449
|
init_history();
|
|
37168
|
-
import
|
|
37450
|
+
import fs39 from "fs";
|
|
37169
37451
|
var program2 = new Command;
|
|
37170
37452
|
program2.name("wtx").description("Multi-repo git worktree manager").version(VERSION).option("-q, --quiet", "Suppress progress indicators", false).option("--verbose", "Show git commands as they run", false).option("--dry-run", "Show what would happen", false);
|
|
37171
37453
|
var MUTATING_COMMANDS = new Set([
|
|
@@ -37275,7 +37557,7 @@ program2.command("_resolve-path <repo> <branch>", { hidden: true }).description(
|
|
|
37275
37557
|
}
|
|
37276
37558
|
const repo = repos[0];
|
|
37277
37559
|
const wtPath = getWorktreePath(repo, branch);
|
|
37278
|
-
if (!
|
|
37560
|
+
if (!fs39.existsSync(wtPath)) {
|
|
37279
37561
|
process.stderr.write(`✗ No worktree at ${wtPath}
|
|
37280
37562
|
`);
|
|
37281
37563
|
process.exit(1);
|