@ogpoyraz/wtx 0.8.4 → 0.8.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +147 -15
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -31096,7 +31096,7 @@ function isMissingWorktreePathError(message) {
|
|
|
31096
31096
|
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");
|
|
31097
31097
|
}
|
|
31098
31098
|
function isRecoverableWorktreeRemoveError(message) {
|
|
31099
|
-
return message.includes("not a working tree") || message.includes("already removed") || isMissingWorktreePathError(message);
|
|
31099
|
+
return message.includes("not a working tree") || message.includes("already removed") || message.includes("submodule") || message.includes("containing submodules") || isMissingWorktreePathError(message);
|
|
31100
31100
|
}
|
|
31101
31101
|
function removeExistingPath(wtPath, opts) {
|
|
31102
31102
|
if (!opts.dryRun && fs23.existsSync(wtPath)) {
|
|
@@ -31253,7 +31253,30 @@ function registerRemoveCommand(program2) {
|
|
|
31253
31253
|
stepSuccess("Worktree removed", wtPath);
|
|
31254
31254
|
} catch (err) {
|
|
31255
31255
|
const msg = errorMessage(err);
|
|
31256
|
-
|
|
31256
|
+
const isSubmodule = msg.includes("submodule") || msg.includes("containing submodules");
|
|
31257
|
+
if (isSubmodule && !options.force && !args.includes("--force")) {
|
|
31258
|
+
stepWarning("Worktree contains submodules — retrying with --force", msg.split(`
|
|
31259
|
+
`)[0] ?? msg);
|
|
31260
|
+
try {
|
|
31261
|
+
await gitExec(["-C", repo.mainPath, "worktree", "remove", "--force", wtPath], globalOpts);
|
|
31262
|
+
stepSuccess("Worktree removed", wtPath);
|
|
31263
|
+
} catch (forceErr) {
|
|
31264
|
+
const forceMsg = errorMessage(forceErr);
|
|
31265
|
+
if (isRecoverableWorktreeRemoveError(forceMsg)) {
|
|
31266
|
+
stepWarning("Worktree already removed or invalid", forceMsg);
|
|
31267
|
+
try {
|
|
31268
|
+
await gitExec(["-C", repo.mainPath, "worktree", "prune"], globalOpts);
|
|
31269
|
+
} catch (pruneErr) {
|
|
31270
|
+
stepWarning("Worktree prune failed", errorMessage(pruneErr));
|
|
31271
|
+
}
|
|
31272
|
+
removeExistingPath(wtPath, globalOpts);
|
|
31273
|
+
await removeLocalBranchBestEffort(repo.mainPath, branch, globalOpts);
|
|
31274
|
+
stepSuccess("Worktree removed", wtPath);
|
|
31275
|
+
} else {
|
|
31276
|
+
throw forceErr;
|
|
31277
|
+
}
|
|
31278
|
+
}
|
|
31279
|
+
} else if (isRecoverableWorktreeRemoveError(msg)) {
|
|
31257
31280
|
stepWarning("Worktree already removed or invalid", msg);
|
|
31258
31281
|
try {
|
|
31259
31282
|
await gitExec(["-C", repo.mainPath, "worktree", "prune"], globalOpts);
|
|
@@ -32889,21 +32912,130 @@ function registerSkillCommand(program2) {
|
|
|
32889
32912
|
}
|
|
32890
32913
|
|
|
32891
32914
|
// src/commands/terminal.ts
|
|
32915
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
32916
|
+
import fs33 from "node:fs";
|
|
32917
|
+
import path37 from "node:path";
|
|
32918
|
+
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
32919
|
+
function findEntry() {
|
|
32920
|
+
const candidates = [];
|
|
32921
|
+
if (process.argv[1])
|
|
32922
|
+
candidates.push(process.argv[1]);
|
|
32923
|
+
try {
|
|
32924
|
+
const thisPath = fileURLToPath4(import.meta.url);
|
|
32925
|
+
candidates.push(thisPath);
|
|
32926
|
+
candidates.push(path37.resolve(path37.dirname(thisPath), "../../dist/cli.mjs"));
|
|
32927
|
+
candidates.push(path37.resolve(path37.dirname(thisPath), "../cli.mjs"));
|
|
32928
|
+
} catch {}
|
|
32929
|
+
for (const p of candidates) {
|
|
32930
|
+
try {
|
|
32931
|
+
if (p.endsWith(".mjs") || p.endsWith(".js")) {
|
|
32932
|
+
if (fs33.existsSync(p) && fs33.statSync(p).isFile())
|
|
32933
|
+
return p;
|
|
32934
|
+
} else if (fs33.existsSync(p)) {
|
|
32935
|
+
const stat = fs33.statSync(p);
|
|
32936
|
+
if (stat.isFile()) {
|
|
32937
|
+
const head = fs33.readFileSync(p, "utf8").slice(0, 1024);
|
|
32938
|
+
if (head.includes("cli.mjs") || head.includes("#!/usr/bin/env node")) {
|
|
32939
|
+
if (p.endsWith("wtx") || p.endsWith("cli.mjs")) {
|
|
32940
|
+
const dir = path37.dirname(fs33.realpathSync(p));
|
|
32941
|
+
const cli = path37.join(dir, "cli.mjs");
|
|
32942
|
+
if (fs33.existsSync(cli))
|
|
32943
|
+
return cli;
|
|
32944
|
+
const cli2 = path37.resolve(dir, "../dist/cli.mjs");
|
|
32945
|
+
if (fs33.existsSync(cli2))
|
|
32946
|
+
return cli2;
|
|
32947
|
+
if (head.includes("import") || head.includes("commander"))
|
|
32948
|
+
return p;
|
|
32949
|
+
}
|
|
32950
|
+
}
|
|
32951
|
+
}
|
|
32952
|
+
}
|
|
32953
|
+
} catch {}
|
|
32954
|
+
}
|
|
32955
|
+
try {
|
|
32956
|
+
const thisPath = fileURLToPath4(import.meta.url);
|
|
32957
|
+
const pkgRoot = path37.resolve(path37.dirname(thisPath), "../..");
|
|
32958
|
+
const cli = path37.join(pkgRoot, "dist", "cli.mjs");
|
|
32959
|
+
if (fs33.existsSync(cli))
|
|
32960
|
+
return cli;
|
|
32961
|
+
} catch {}
|
|
32962
|
+
return null;
|
|
32963
|
+
}
|
|
32964
|
+
function tryRelaunchViaBun(forwardedArgs) {
|
|
32965
|
+
if (process.env.WTX_BUN_RERUN === "1")
|
|
32966
|
+
return null;
|
|
32967
|
+
const env2 = { ...process.env, WTX_BUN_RERUN: "1" };
|
|
32968
|
+
const entry = findEntry();
|
|
32969
|
+
if (entry) {
|
|
32970
|
+
const result = spawnSync3("bun", [entry, ...forwardedArgs], {
|
|
32971
|
+
stdio: "inherit",
|
|
32972
|
+
env: env2
|
|
32973
|
+
});
|
|
32974
|
+
if (!result.error) {
|
|
32975
|
+
process.exit(result.status ?? 0);
|
|
32976
|
+
}
|
|
32977
|
+
if (result.error.code !== "ENOENT") {
|
|
32978
|
+
process.stderr.write(`✗ Failed to relaunch via Bun: ${result.error.message}
|
|
32979
|
+
`);
|
|
32980
|
+
process.exit(1);
|
|
32981
|
+
}
|
|
32982
|
+
}
|
|
32983
|
+
const attempts = [
|
|
32984
|
+
{ cmd: "bunx", args: ["--bun", "wtx", ...forwardedArgs] },
|
|
32985
|
+
{ cmd: "bun", args: ["x", "--bun", "wtx", ...forwardedArgs] }
|
|
32986
|
+
];
|
|
32987
|
+
for (const { cmd, args } of attempts) {
|
|
32988
|
+
const result = spawnSync3(cmd, args, { stdio: "inherit", env: env2 });
|
|
32989
|
+
if (!result.error) {
|
|
32990
|
+
process.exit(result.status ?? 0);
|
|
32991
|
+
}
|
|
32992
|
+
if (result.error.code !== "ENOENT") {
|
|
32993
|
+
process.stderr.write(`✗ Failed to relaunch via ${cmd}: ${result.error.message}
|
|
32994
|
+
`);
|
|
32995
|
+
process.exit(1);
|
|
32996
|
+
}
|
|
32997
|
+
}
|
|
32998
|
+
return null;
|
|
32999
|
+
}
|
|
32892
33000
|
function registerTerminalCommand(program2) {
|
|
32893
33001
|
program2.command("terminal").description("Interactive worktree dashboard (requires Bun)").action(async () => {
|
|
32894
33002
|
const opts = program2.optsWithGlobals();
|
|
32895
33003
|
if (typeof Bun === "undefined") {
|
|
32896
|
-
process.
|
|
33004
|
+
const forwarded = process.argv.slice(2);
|
|
33005
|
+
const relaunched = tryRelaunchViaBun(forwarded);
|
|
33006
|
+
if (relaunched === null) {
|
|
33007
|
+
const probe2 = spawnSync3("bun", ["--version"], { stdio: "ignore" });
|
|
33008
|
+
const hasBun = !probe2.error && probe2.status === 0;
|
|
33009
|
+
if (!hasBun) {
|
|
33010
|
+
process.stderr.write(`✗ wtx terminal dashboard requires the Bun runtime
|
|
32897
33011
|
`);
|
|
32898
|
-
|
|
33012
|
+
process.stderr.write(` The TUI relies on Bun's FFI and React renderer which cannot run in Node/compiled binary.
|
|
32899
33013
|
`);
|
|
32900
|
-
|
|
33014
|
+
process.stderr.write(` Bun was not found on your PATH. Install it from https://bun.sh, then run:
|
|
32901
33015
|
`);
|
|
32902
|
-
|
|
33016
|
+
process.stderr.write(` wtx terminal
|
|
32903
33017
|
|
|
32904
33018
|
`);
|
|
32905
|
-
|
|
32906
|
-
|
|
33019
|
+
process.stderr.write(" Note: `wtx ls` provides a fast list view and works everywhere.\n");
|
|
33020
|
+
process.exit(1);
|
|
33021
|
+
}
|
|
33022
|
+
process.stderr.write(`✗ wtx terminal dashboard requires the Bun runtime
|
|
33023
|
+
`);
|
|
33024
|
+
process.stderr.write(` The TUI relies on Bun's FFI and React renderer which cannot run in Node/compiled binary.
|
|
33025
|
+
`);
|
|
33026
|
+
process.stderr.write(` Automatic relaunch via Bun failed. Try running directly:
|
|
33027
|
+
`);
|
|
33028
|
+
process.stderr.write(` bunx --bun wtx terminal
|
|
33029
|
+
`);
|
|
33030
|
+
const entry = findEntry();
|
|
33031
|
+
if (entry) {
|
|
33032
|
+
process.stderr.write(` bun ${entry} terminal
|
|
33033
|
+
`);
|
|
33034
|
+
}
|
|
33035
|
+
process.stderr.write("\n Note: `wtx ls` provides a fast list view and works everywhere.\n");
|
|
33036
|
+
process.exit(1);
|
|
33037
|
+
}
|
|
33038
|
+
return;
|
|
32907
33039
|
}
|
|
32908
33040
|
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
32909
33041
|
process.stderr.write(`✗ wtx terminal requires an interactive terminal
|
|
@@ -32924,7 +33056,7 @@ init_deps();
|
|
|
32924
33056
|
import readline3 from "readline";
|
|
32925
33057
|
init_path_safety();
|
|
32926
33058
|
init_stack();
|
|
32927
|
-
import
|
|
33059
|
+
import fs34 from "fs";
|
|
32928
33060
|
async function runMcpServer(opts = {}) {
|
|
32929
33061
|
const input = opts.input ?? process.stdin;
|
|
32930
33062
|
const output = opts.output ?? process.stdout;
|
|
@@ -33107,7 +33239,7 @@ async function handleToolCall(name, args, config2, _opts) {
|
|
|
33107
33239
|
for (const wt of wts) {
|
|
33108
33240
|
if (!wt.path.startsWith(repo.wtRoot))
|
|
33109
33241
|
continue;
|
|
33110
|
-
const dirtyCount =
|
|
33242
|
+
const dirtyCount = fs34.existsSync(wt.path) ? (await getDirtyFiles(wt.path)).length : 0;
|
|
33111
33243
|
items.push({
|
|
33112
33244
|
repo: repo.name,
|
|
33113
33245
|
branch: wt.branch || null,
|
|
@@ -33135,7 +33267,7 @@ async function handleToolCall(name, args, config2, _opts) {
|
|
|
33135
33267
|
}
|
|
33136
33268
|
const repo = resolveRepos(config2, [args.repo])[0];
|
|
33137
33269
|
const wtPath = getWorktreePath(repo, args.branch);
|
|
33138
|
-
if (!
|
|
33270
|
+
if (!fs34.existsSync(wtPath)) {
|
|
33139
33271
|
throw { isToolError: true, message: `Worktree not found for branch ${args.branch}` };
|
|
33140
33272
|
}
|
|
33141
33273
|
const mainBranch = await resolveMainBranch(repo, config2);
|
|
@@ -33254,7 +33386,7 @@ async function handleToolCall(name, args, config2, _opts) {
|
|
|
33254
33386
|
if (children.length > 0 && args.force !== true) {
|
|
33255
33387
|
throw { isToolError: true, message: `Branch has dependent worktrees: ${children.join(", ")}. Use force:true to override.` };
|
|
33256
33388
|
}
|
|
33257
|
-
if (!args.force &&
|
|
33389
|
+
if (!args.force && fs34.existsSync(wtPath)) {
|
|
33258
33390
|
const dirty = await getDirtyFiles(wtPath);
|
|
33259
33391
|
if (dirty.length > 0) {
|
|
33260
33392
|
throw { isToolError: true, message: `Worktree is dirty. Use force:true to remove it.` };
|
|
@@ -33273,7 +33405,7 @@ async function handleToolCall(name, args, config2, _opts) {
|
|
|
33273
33405
|
}
|
|
33274
33406
|
const repo = resolveRepos(config2, [args.repo])[0];
|
|
33275
33407
|
const wtPath = getWorktreePath(repo, args.branch);
|
|
33276
|
-
if (!
|
|
33408
|
+
if (!fs34.existsSync(wtPath)) {
|
|
33277
33409
|
throw { isToolError: true, message: `Worktree not found for branch ${args.branch}` };
|
|
33278
33410
|
}
|
|
33279
33411
|
const mainBranch = await resolveMainBranch(repo, config2);
|
|
@@ -33495,7 +33627,7 @@ init_config();
|
|
|
33495
33627
|
init_resolver();
|
|
33496
33628
|
init_log();
|
|
33497
33629
|
init_history();
|
|
33498
|
-
import
|
|
33630
|
+
import fs35 from "fs";
|
|
33499
33631
|
var program2 = new Command;
|
|
33500
33632
|
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);
|
|
33501
33633
|
var MUTATING_COMMANDS = new Set([
|
|
@@ -33599,7 +33731,7 @@ program2.command("_resolve-path <repo> <branch>", { hidden: true }).description(
|
|
|
33599
33731
|
}
|
|
33600
33732
|
const repo = repos[0];
|
|
33601
33733
|
const wtPath = getWorktreePath(repo, branch);
|
|
33602
|
-
if (!
|
|
33734
|
+
if (!fs35.existsSync(wtPath)) {
|
|
33603
33735
|
process.stderr.write(`✗ No worktree at ${wtPath}
|
|
33604
33736
|
`);
|
|
33605
33737
|
process.exit(1);
|