@norman-else/dsh-claude 0.1.39 → 0.1.40
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/lib/client.d.ts +1 -0
- package/lib/client.js +56 -38
- package/lib/client.js.map +1 -1
- package/lib/index.mjs +46 -2
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -1
package/lib/index.mjs
CHANGED
|
@@ -4164,6 +4164,22 @@ function parseWorktreeBranches(value) {
|
|
|
4164
4164
|
function slug(value, fallback) {
|
|
4165
4165
|
return value.toLocaleLowerCase("en-US").replace(/[^a-z0-9._-]+/gu, "-").replace(/^-+|-+$/gu, "").slice(0, 48) || fallback;
|
|
4166
4166
|
}
|
|
4167
|
+
/** A branch name as one path segment: `/` and anything else unsafe folds to
|
|
4168
|
+
* `-`, case is kept so a ticket key stays scannable (`PSOS-5683`, not
|
|
4169
|
+
* `psos-5683`). Flattened rather than nested on purpose -- see
|
|
4170
|
+
* {@link worktreeDirectoryName}. */
|
|
4171
|
+
function branchSegment(branch) {
|
|
4172
|
+
return branch.replace(/[^A-Za-z0-9._-]+/gu, "-").replace(/^[-.]+/u, "").replace(/-+$/u, "").slice(0, 48).replace(/-+$/u, "") || "branch";
|
|
4173
|
+
}
|
|
4174
|
+
/** Name a worktree directory after the repository and the branch it holds, so
|
|
4175
|
+
* the workspace list reads without opening a session.
|
|
4176
|
+
*
|
|
4177
|
+
* One flat segment, never a `feature/x` subdirectory: the orphan sweep lists
|
|
4178
|
+
* this root one level deep, and a prefix directory holds no lease of its own,
|
|
4179
|
+
* so it would be removed along with every live worktree inside it. */
|
|
4180
|
+
function worktreeDirectoryName(root, branch) {
|
|
4181
|
+
return `${slug(basename(root), "repository")}-${branchSegment(branch)}`;
|
|
4182
|
+
}
|
|
4167
4183
|
/** Comparable form for path identity: resolved, forward slashes, case-folded
|
|
4168
4184
|
* so Windows drive-letter or case spelling differences cannot hide a match. */
|
|
4169
4185
|
function comparablePath(value) {
|
|
@@ -4463,6 +4479,18 @@ var RepositorySetupService = class {
|
|
|
4463
4479
|
], root, GIT_FETCH_TIMEOUT_MS);
|
|
4464
4480
|
if (fetched.exitCode !== 0 || fetched.lossy) throw new RepositorySetupError("fetch-failed", "Git could not refresh remote references.");
|
|
4465
4481
|
}
|
|
4482
|
+
/** The remote-tracking ref a local base branch follows, when that ref still
|
|
4483
|
+
* exists. Falls back to the local branch for a branch with no upstream. */
|
|
4484
|
+
async #upstreamRef(git, info, branch) {
|
|
4485
|
+
const upstream = await this.#run(git, [
|
|
4486
|
+
"for-each-ref",
|
|
4487
|
+
"--format=%(upstream:short)",
|
|
4488
|
+
`refs/heads/${branch}`
|
|
4489
|
+
], info.root);
|
|
4490
|
+
if (upstream.exitCode !== 0 || upstream.lossy) return void 0;
|
|
4491
|
+
const name = upstream.stdout.trim();
|
|
4492
|
+
return name.length > 0 && info.remoteBranches.includes(name) ? `refs/remotes/${name}` : void 0;
|
|
4493
|
+
}
|
|
4466
4494
|
async #checkout(info, branch) {
|
|
4467
4495
|
if (info.current !== branch) {
|
|
4468
4496
|
if (info.dirty) throw new RepositorySetupError("dirty-workspace", "Commit or stash workspace changes before switching branches.");
|
|
@@ -4493,10 +4521,11 @@ var RepositorySetupService = class {
|
|
|
4493
4521
|
const git = await this.#git();
|
|
4494
4522
|
progress("fetching");
|
|
4495
4523
|
await this.#fetchRemotes(git, root);
|
|
4524
|
+
const startRef = reuseExistingBranch ? baseRef : await this.#upstreamRef(git, info, baseBranch) ?? baseRef;
|
|
4496
4525
|
const suffix = randomUUID().slice(0, 8);
|
|
4497
4526
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/gu, "").replace(/\.\d{3}Z$/u, "Z");
|
|
4498
4527
|
const branch = explicitBranchName ?? await this.#generatedBranch(info, baseBranch, intent, stamp, suffix, progress);
|
|
4499
|
-
const path = join(this.#worktreeRoot,
|
|
4528
|
+
const path = join(this.#worktreeRoot, await this.#freeDirectoryName(root, branch));
|
|
4500
4529
|
progress("creating-worktree");
|
|
4501
4530
|
await mkdir(this.#worktreeRoot, { recursive: true });
|
|
4502
4531
|
if (reuseExistingBranch) await this.#run(git, ["worktree", "prune"], root).catch(() => void 0);
|
|
@@ -4512,7 +4541,7 @@ var RepositorySetupService = class {
|
|
|
4512
4541
|
"-b",
|
|
4513
4542
|
branch,
|
|
4514
4543
|
path,
|
|
4515
|
-
|
|
4544
|
+
startRef
|
|
4516
4545
|
], root);
|
|
4517
4546
|
if (created.exitCode !== 0) {
|
|
4518
4547
|
const detail = created.stderr.split(/\r?\n/u).map((line) => line.trim()).filter((line) => line.length > 0).at(-1);
|
|
@@ -4559,6 +4588,21 @@ var RepositorySetupService = class {
|
|
|
4559
4588
|
/** `<prefix>/<what the draft is about>`, falling back to
|
|
4560
4589
|
* `<prefix>/<base branch>-<stamp>-<random>` whenever the summary is missing
|
|
4561
4590
|
* or unusable. The prefix and the fallback shape are unchanged. */
|
|
4591
|
+
/** The branch-named directory, or the first `-2`, `-3`, ... spelling free of
|
|
4592
|
+
* anything already on disk. Names no longer carry a timestamp, so a stale
|
|
4593
|
+
* directory a crash left behind -- or two branches that fold to the same
|
|
4594
|
+
* segment -- would otherwise fail `worktree add` outright. Compared
|
|
4595
|
+
* case-folded, since a case-insensitive filesystem would collide anyway. */
|
|
4596
|
+
async #freeDirectoryName(root, branch) {
|
|
4597
|
+
const base = worktreeDirectoryName(root, branch);
|
|
4598
|
+
const taken = await readdir(this.#worktreeRoot).then((entries) => new Set(entries.map((entry) => entry.toLocaleLowerCase("en-US"))), () => /* @__PURE__ */ new Set());
|
|
4599
|
+
if (!taken.has(base.toLocaleLowerCase("en-US"))) return base;
|
|
4600
|
+
for (let index = 2; index < 100; index += 1) {
|
|
4601
|
+
const candidate = `${base}-${index}`;
|
|
4602
|
+
if (!taken.has(candidate.toLocaleLowerCase("en-US"))) return candidate;
|
|
4603
|
+
}
|
|
4604
|
+
return base;
|
|
4605
|
+
}
|
|
4562
4606
|
async #generatedBranch(info, baseBranch, intent, stamp, suffix, progress) {
|
|
4563
4607
|
const prefix = safeBranch(await this.#branchPrefix());
|
|
4564
4608
|
if (intent !== void 0 && intent.trim().length > 0) {
|