@tablation/crew 0.77.0 → 0.78.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/cli.js +191 -53
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -263,6 +263,7 @@ crew inbox [--member NAME] your tickets across every workspace
|
|
|
263
263
|
crew connect WS[/PROJECT] resolve a workspace/project's ids into the state tree
|
|
264
264
|
crew repos add [ROUTE] [NAME] [PATH] attach a local checkout to a route in crew.yaml, creating its Repos row if missing (--dry-run previews)
|
|
265
265
|
crew repos list ROUTE the checkouts a route has, and the tracker Repos row each matches
|
|
266
|
+
crew repos check [PATH] validate a checkout's .crew.yaml (parse, release readiness, hook syntax) without attaching it (--route ROUTE, --strict, --json)
|
|
266
267
|
crew repos hooks [ROUTE] NAME suggest test/build hooks for a checkout (--write route|repo [--replace] writes them, --clear route removes them, --recipes, --hook KEY=CMD, --agent, --json)
|
|
267
268
|
crew install [--replace] write and load this platform's scheduler unit (macOS: clears a crew installed from another path)
|
|
268
269
|
crew uninstall [--all] unload and remove it (--all: every crew launchd unit, whichever path it came from)
|
package/dist/cli.js
CHANGED
|
@@ -9041,10 +9041,10 @@ function doctorSelftestLines(stored, toolchains) {
|
|
|
9041
9041
|
}
|
|
9042
9042
|
|
|
9043
9043
|
// src/cli.ts
|
|
9044
|
-
import { resolve as resolve5, dirname as dirname20, join as join37, basename as
|
|
9044
|
+
import { resolve as resolve5, dirname as dirname20, join as join37, basename as basename12 } from "path";
|
|
9045
9045
|
import { fileURLToPath } from "url";
|
|
9046
9046
|
import { hostname as hostname2, homedir as homedir10 } from "os";
|
|
9047
|
-
import { spawnSync as
|
|
9047
|
+
import { spawnSync as spawnSync7 } from "child_process";
|
|
9048
9048
|
import { parse as parseYaml2 } from "yaml";
|
|
9049
9049
|
|
|
9050
9050
|
// src/argv.ts
|
|
@@ -14393,12 +14393,107 @@ async function showLogEntry(route, entryId, opts = {}) {
|
|
|
14393
14393
|
};
|
|
14394
14394
|
}
|
|
14395
14395
|
|
|
14396
|
+
// src/repos-check.ts
|
|
14397
|
+
import { spawnSync as spawnSync6 } from "child_process";
|
|
14398
|
+
import { readFileSync as readFileSync24 } from "fs";
|
|
14399
|
+
import { basename as basename8 } from "path";
|
|
14400
|
+
var POSIX_SHELL = /^(?:ba|z|da|k|a|mk)?sh$|^fish$/;
|
|
14401
|
+
function checkHookSyntax(script, shell) {
|
|
14402
|
+
const spec = resolveShell(shell);
|
|
14403
|
+
const name = basename8(spec.bin).replace(/\.exe$/i, "").toLowerCase();
|
|
14404
|
+
let run2;
|
|
14405
|
+
if (POSIX_SHELL.test(name)) {
|
|
14406
|
+
run2 = spawnSync6(spec.bin, ["-n"], { input: script, encoding: "utf8" });
|
|
14407
|
+
} else if (/^(pwsh|powershell)$/.test(name)) {
|
|
14408
|
+
const parse3 = "$e=$null;[void][System.Management.Automation.Language.Parser]::ParseInput($env:CREW_CHECK_SCRIPT,[ref]$null,[ref]$e);if($e){$e|ForEach-Object{[Console]::Error.WriteLine($_.Message)};exit 1}";
|
|
14409
|
+
run2 = spawnSync6(spec.bin, ["-NoProfile", "-NonInteractive", "-Command", parse3], {
|
|
14410
|
+
encoding: "utf8",
|
|
14411
|
+
env: { ...process.env, CREW_CHECK_SCRIPT: script }
|
|
14412
|
+
});
|
|
14413
|
+
} else {
|
|
14414
|
+
return { status: "unchecked", message: `no syntax check for ${spec.bin}` };
|
|
14415
|
+
}
|
|
14416
|
+
if (run2.error) return { status: "unchecked", message: `cannot run ${spec.bin}: ${run2.error.message}` };
|
|
14417
|
+
if (run2.status === 0) return { status: "ok" };
|
|
14418
|
+
const message = `${run2.stderr ?? ""}${run2.stdout ?? ""}`.trim() || `${spec.bin} exited ${run2.status}`;
|
|
14419
|
+
return { status: "error", message };
|
|
14420
|
+
}
|
|
14421
|
+
function renderSamples(cfg, dir) {
|
|
14422
|
+
const checkout = basename8(dir.replace(/[/\\]+$/, ""));
|
|
14423
|
+
const tagged = { key: "ISSUE-123", title: "Fix the widget", role: "dev", tag: "TABL-123", prefix: "tabl", lowercase: cfg.branch.lowercase };
|
|
14424
|
+
const untagged = { key: "ISSUE-123", title: "Fix the widget", role: "dev", lowercase: cfg.branch.lowercase };
|
|
14425
|
+
return [["with a project tag", tagged], ["without a project tag", untagged]].map(([label, ctx]) => {
|
|
14426
|
+
const branch = renderBranchName(effectiveBranchTemplate(cfg, ctx.prefix), ctx);
|
|
14427
|
+
const worktree = effectiveWorktreeDirName(cfg, dir, branch, "123", (t) => renderWorktreeName(t, { ...ctx, dir: checkout }));
|
|
14428
|
+
return { case: label, branch, worktree };
|
|
14429
|
+
});
|
|
14430
|
+
}
|
|
14431
|
+
function checkRepoSpec(dir, opts = {}) {
|
|
14432
|
+
const empty = { parsed: null, effective: null, readiness: [], hooks: [], shadowed: [], samples: [] };
|
|
14433
|
+
const file = findRepoConfig(dir);
|
|
14434
|
+
if (!file) {
|
|
14435
|
+
return { ...empty, file: null, error: `no .crew.yaml, .crew.yml or .crew.json in ${dir}`, exitCode: 1 };
|
|
14436
|
+
}
|
|
14437
|
+
let parsed;
|
|
14438
|
+
try {
|
|
14439
|
+
parsed = parseRepoConfig(readFileSync24(file, "utf8"), file);
|
|
14440
|
+
} catch (e) {
|
|
14441
|
+
if (!(e instanceof RepoConfigError)) throw e;
|
|
14442
|
+
return { ...empty, file, error: e.message, exitCode: 1 };
|
|
14443
|
+
}
|
|
14444
|
+
const effective = resolveRepoConfig(parsed, opts.ship, dir);
|
|
14445
|
+
const readiness = validateEffective(effective);
|
|
14446
|
+
const hooks = Object.keys(effective.hooks).map((name) => ({
|
|
14447
|
+
name,
|
|
14448
|
+
label: hookLabel(effective, name),
|
|
14449
|
+
syntax: checkHookSyntax(effective.hooks[name], effective.shell)
|
|
14450
|
+
}));
|
|
14451
|
+
const failed = hooks.some((h) => h.syntax.status === "error");
|
|
14452
|
+
return {
|
|
14453
|
+
file,
|
|
14454
|
+
parsed,
|
|
14455
|
+
effective,
|
|
14456
|
+
readiness,
|
|
14457
|
+
hooks,
|
|
14458
|
+
shadowed: effective.shadowed,
|
|
14459
|
+
samples: renderSamples(effective, dir),
|
|
14460
|
+
error: null,
|
|
14461
|
+
exitCode: failed || opts.strict && readiness.length > 0 ? 1 : 0
|
|
14462
|
+
};
|
|
14463
|
+
}
|
|
14464
|
+
function renderRepoCheck(r) {
|
|
14465
|
+
if (r.error) return `${r.file ?? "crew repos check"}: ${r.error}
|
|
14466
|
+
`;
|
|
14467
|
+
const e = r.effective;
|
|
14468
|
+
const lines = [
|
|
14469
|
+
`${r.file}`,
|
|
14470
|
+
` platform ${e.platform}`,
|
|
14471
|
+
` release.mode ${e.release.mode}`,
|
|
14472
|
+
` release.versioning ${e.release.versioning}`,
|
|
14473
|
+
` branch.base ${e.branch.base}`,
|
|
14474
|
+
` branch.name ${e.branch.name}`,
|
|
14475
|
+
` branch.push ${e.branch.push}`
|
|
14476
|
+
];
|
|
14477
|
+
for (const s of r.samples) lines.push(` ${s.case}: branch ${s.branch}, worktree ../${s.worktree}`);
|
|
14478
|
+
lines.push(r.hooks.length ? " hooks" : " hooks none");
|
|
14479
|
+
for (const h of r.hooks) {
|
|
14480
|
+
const named = h.label !== e.hooks[h.name] ? ` (${h.label})` : "";
|
|
14481
|
+
const syntax = h.syntax.status === "ok" ? "syntax ok" : h.syntax.status === "unchecked" ? `syntax not checked: ${h.syntax.message}` : "syntax ERROR";
|
|
14482
|
+
lines.push(` ${h.name}${named}: ${syntax}`);
|
|
14483
|
+
if (h.syntax.status === "error") lines.push(...(h.syntax.message ?? "").split("\n").map((l) => ` ${l}`));
|
|
14484
|
+
}
|
|
14485
|
+
if (r.shadowed.length) lines.push(` this ship declares ${r.shadowed.join(", ")}, and the file overrides it \u2014 the file wins`);
|
|
14486
|
+
for (const p of r.readiness) lines.push(` - release readiness: ${p}`);
|
|
14487
|
+
return `${lines.join("\n")}
|
|
14488
|
+
`;
|
|
14489
|
+
}
|
|
14490
|
+
|
|
14396
14491
|
// src/repos-hooks.ts
|
|
14397
14492
|
import { isMap as isMap2, parseDocument as parseDocument3, YAMLMap } from "yaml";
|
|
14398
14493
|
|
|
14399
14494
|
// src/repos-cmd.ts
|
|
14400
14495
|
import { homedir as homedir9 } from "os";
|
|
14401
|
-
import { basename as
|
|
14496
|
+
import { basename as basename9, join as join28, resolve as resolve3 } from "path";
|
|
14402
14497
|
import { isMap, isSeq, parseDocument as parseDocument2, stringify as stringify2 } from "yaml";
|
|
14403
14498
|
var ReposError = class extends Error {
|
|
14404
14499
|
};
|
|
@@ -14470,7 +14565,7 @@ function configuredRepos(text, route, base) {
|
|
|
14470
14565
|
});
|
|
14471
14566
|
}
|
|
14472
14567
|
const dir = m.get("dir");
|
|
14473
|
-
return dir ? [{ name:
|
|
14568
|
+
return dir ? [{ name: basename9(String(dir)), dir: expandDir(String(dir), base) }] : [];
|
|
14474
14569
|
}
|
|
14475
14570
|
function cloneBaseOf(text, route, base, isCheckout) {
|
|
14476
14571
|
const m = routeMapOf(parseDocument2(text), route);
|
|
@@ -14559,7 +14654,7 @@ function planRepoAdd(i) {
|
|
|
14559
14654
|
throw new ReposError(`${target} is already configured on route ${i.route}`);
|
|
14560
14655
|
}
|
|
14561
14656
|
const matched = matchRepoName(i.origin, i.resolved);
|
|
14562
|
-
const name = matched ?? i.name ?? normalizeRemote(i.origin)?.split("/").pop() ??
|
|
14657
|
+
const name = matched ?? i.name ?? normalizeRemote(i.origin)?.split("/").pop() ?? basename9(target);
|
|
14563
14658
|
let warning;
|
|
14564
14659
|
if (!matched) {
|
|
14565
14660
|
const known = Object.values(i.resolved?.repoNames ?? {});
|
|
@@ -15075,7 +15170,7 @@ ${run2.err}` });
|
|
|
15075
15170
|
}
|
|
15076
15171
|
|
|
15077
15172
|
// src/sample-ticket.ts
|
|
15078
|
-
import { basename as
|
|
15173
|
+
import { basename as basename10, dirname as dirname17, join as join29 } from "path";
|
|
15079
15174
|
var SAMPLE_REPO = "crew-sample";
|
|
15080
15175
|
var SAMPLE_REMOTE = `local/${SAMPLE_REPO}`;
|
|
15081
15176
|
var SAMPLE_MARKER = "sample: true";
|
|
@@ -15191,8 +15286,8 @@ function schedulerInstalled(plan, exists, crontab) {
|
|
|
15191
15286
|
}
|
|
15192
15287
|
|
|
15193
15288
|
// src/worktree-sweep.ts
|
|
15194
|
-
import { existsSync as existsSync27, readFileSync as
|
|
15195
|
-
import { basename as
|
|
15289
|
+
import { existsSync as existsSync27, readFileSync as readFileSync25, readdirSync as readdirSync8 } from "fs";
|
|
15290
|
+
import { basename as basename11, dirname as dirname18, join as join30, resolve as resolve4 } from "path";
|
|
15196
15291
|
function planWorktreeSweep(target, tickets, contract) {
|
|
15197
15292
|
const terminal = new Set(contract.statuses.resolved.filter((s) => s !== contract.statuses.verified));
|
|
15198
15293
|
const verified = contract.statuses.verified;
|
|
@@ -15223,7 +15318,7 @@ function ticketForWorktree(w, byNumber, contract) {
|
|
|
15223
15318
|
const t = num ? byNumber.get(num) : void 0;
|
|
15224
15319
|
if (t && ticketStems(t).has(stem)) return t;
|
|
15225
15320
|
}
|
|
15226
|
-
const dir =
|
|
15321
|
+
const dir = basename11(w.path);
|
|
15227
15322
|
const m = DIR_NUMBER.exec(dir);
|
|
15228
15323
|
if (m && byNumber.has(m[1])) return byNumber.get(m[1]);
|
|
15229
15324
|
if (!w.branch) {
|
|
@@ -15239,7 +15334,7 @@ function worktreeTicketNumbers(target) {
|
|
|
15239
15334
|
const n = /-(\d+)$/.exec(BRANCH_STEM.exec(w.branch)?.[1] ?? "")?.[1];
|
|
15240
15335
|
if (n) nums.add(n);
|
|
15241
15336
|
}
|
|
15242
|
-
const m = DIR_NUMBER_SUFFIXED.exec(
|
|
15337
|
+
const m = DIR_NUMBER_SUFFIXED.exec(basename11(w.path));
|
|
15243
15338
|
if (m) nums.add(m[1]);
|
|
15244
15339
|
}
|
|
15245
15340
|
for (const d of siblingWorktreeDirs(target)) nums.add(d.number);
|
|
@@ -15247,7 +15342,7 @@ function worktreeTicketNumbers(target) {
|
|
|
15247
15342
|
}
|
|
15248
15343
|
function siblingWorktreeDirs(target) {
|
|
15249
15344
|
const parent = dirname18(target.dir.replace(/[/\\]+$/, ""));
|
|
15250
|
-
const stem = `${
|
|
15345
|
+
const stem = `${basename11(target.dir.replace(/[/\\]+$/, ""))}-`;
|
|
15251
15346
|
const registered = new Set(worktrees(target.dir).map((w) => resolve4(w.path)));
|
|
15252
15347
|
let names;
|
|
15253
15348
|
try {
|
|
@@ -15272,7 +15367,7 @@ function orphanedWorktreeDirs(target, tickets, contract) {
|
|
|
15272
15367
|
}
|
|
15273
15368
|
function warnOrphanedWorktreeDirs(paths, log, held) {
|
|
15274
15369
|
for (const p of paths) {
|
|
15275
|
-
const number = DIR_NUMBER_SUFFIXED.exec(
|
|
15370
|
+
const number = DIR_NUMBER_SUFFIXED.exec(basename11(p))?.[1];
|
|
15276
15371
|
const when = held && number ? noteHeldWorktree(held.state, { repo: held.repo, path: p, ticket: number, status: "deployed", reason: "orphaned directory" }) : "first";
|
|
15277
15372
|
if (when) {
|
|
15278
15373
|
log.warn(`orphaned directory ${p}: its ticket is deployed but it is not a registered worktree; the sweep will not delete it \u2014 remove it by hand`, { step: "worktree" });
|
|
@@ -15362,7 +15457,7 @@ function isHandBackMerge(path, base) {
|
|
|
15362
15457
|
if (!msgPath) return false;
|
|
15363
15458
|
let first;
|
|
15364
15459
|
try {
|
|
15365
|
-
first =
|
|
15460
|
+
first = readFileSync25(resolve4(path, msgPath), "utf8").split("\n")[0] ?? "";
|
|
15366
15461
|
} catch {
|
|
15367
15462
|
return false;
|
|
15368
15463
|
}
|
|
@@ -15416,7 +15511,7 @@ function isTicketBranch(name, base, stems) {
|
|
|
15416
15511
|
}
|
|
15417
15512
|
function sweepCandidatesExist(cwd, base) {
|
|
15418
15513
|
const linked = worktrees(cwd).slice(1);
|
|
15419
|
-
if (linked.some((w) => DIR_NUMBER_SUFFIXED.test(
|
|
15514
|
+
if (linked.some((w) => DIR_NUMBER_SUFFIXED.test(basename11(w.path)) || w.branch && BRANCH_STEM.test(w.branch))) return true;
|
|
15420
15515
|
if (siblingWorktreeDirs({ name: "", dir: cwd }).length) return true;
|
|
15421
15516
|
return localBranches(cwd).some((b) => b !== base && /-\d+(?:-.*)?$/.test(b));
|
|
15422
15517
|
}
|
|
@@ -15512,13 +15607,13 @@ function planEventsSweep(stateDir, retentionDays, now = Date.now()) {
|
|
|
15512
15607
|
}
|
|
15513
15608
|
|
|
15514
15609
|
// src/cli.ts
|
|
15515
|
-
import { realpathSync as realpathSync3, readFileSync as
|
|
15610
|
+
import { realpathSync as realpathSync3, readFileSync as readFileSync31, existsSync as existsSync34, mkdirSync as mkdirSync22, writeFileSync as writeFileSync23, rmSync as rmSync15, unlinkSync as unlinkSync5, copyFileSync as copyFileSync2, renameSync as renameSync3, statSync as statSync10, readdirSync as readdirSync14 } from "fs";
|
|
15516
15611
|
import { createInterface } from "readline/promises";
|
|
15517
15612
|
import { dirname as dirOf, resolve as resolvePath } from "path";
|
|
15518
15613
|
|
|
15519
15614
|
// src/ssh-keys.ts
|
|
15520
15615
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
15521
|
-
import { existsSync as existsSync29, mkdirSync as mkdirSync18, readFileSync as
|
|
15616
|
+
import { existsSync as existsSync29, mkdirSync as mkdirSync18, readFileSync as readFileSync26, chmodSync } from "fs";
|
|
15522
15617
|
import { dirname as dirname19, join as join32 } from "path";
|
|
15523
15618
|
var SshKeyError = class extends Error {
|
|
15524
15619
|
};
|
|
@@ -15552,7 +15647,7 @@ function ensureShipSshKeypair(stateDir) {
|
|
|
15552
15647
|
if (!existsSync29(publicKeyPath)) {
|
|
15553
15648
|
throw new SshKeyError(`${privateKeyPath} exists but ${publicKeyPath} does not \u2014 regenerate or restore it`);
|
|
15554
15649
|
}
|
|
15555
|
-
const publicKey =
|
|
15650
|
+
const publicKey = readFileSync26(publicKeyPath, "utf8").trim();
|
|
15556
15651
|
return { privateKeyPath, publicKeyPath, publicKey };
|
|
15557
15652
|
}
|
|
15558
15653
|
function sshKeygenAvailable() {
|
|
@@ -15769,7 +15864,7 @@ function syncAllPassengerCheckouts(cfg) {
|
|
|
15769
15864
|
|
|
15770
15865
|
// src/tunnel.ts
|
|
15771
15866
|
import { spawn as spawn5 } from "child_process";
|
|
15772
|
-
import { existsSync as existsSync30, mkdirSync as mkdirSync19, readFileSync as
|
|
15867
|
+
import { existsSync as existsSync30, mkdirSync as mkdirSync19, readFileSync as readFileSync27, writeFileSync as writeFileSync20, unlinkSync as unlinkSync3, readdirSync as readdirSync10 } from "fs";
|
|
15773
15868
|
import { randomInt } from "crypto";
|
|
15774
15869
|
import { join as join33 } from "path";
|
|
15775
15870
|
var DEFAULT_SPAWN_OPTIONS = { stdio: "ignore", detached: true };
|
|
@@ -15844,7 +15939,7 @@ function readPersistedTunnel(stateDir, workspaceId) {
|
|
|
15844
15939
|
const path = tunnelStatePath(stateDir, workspaceId);
|
|
15845
15940
|
if (!existsSync30(path)) return void 0;
|
|
15846
15941
|
try {
|
|
15847
|
-
return JSON.parse(
|
|
15942
|
+
return JSON.parse(readFileSync27(path, "utf8"));
|
|
15848
15943
|
} catch {
|
|
15849
15944
|
return void 0;
|
|
15850
15945
|
}
|
|
@@ -16071,7 +16166,7 @@ async function unregisterEndpoint(workspaceId, info, d) {
|
|
|
16071
16166
|
import { createServer as createHttpServer } from "http";
|
|
16072
16167
|
import { spawn as spawn6 } from "child_process";
|
|
16073
16168
|
import { randomBytes } from "crypto";
|
|
16074
|
-
import { existsSync as existsSync31, mkdirSync as mkdirSync20, readFileSync as
|
|
16169
|
+
import { existsSync as existsSync31, mkdirSync as mkdirSync20, readFileSync as readFileSync28, writeFileSync as writeFileSync21, unlinkSync as unlinkSync4, readdirSync as readdirSync11 } from "fs";
|
|
16075
16170
|
import { join as join34 } from "path";
|
|
16076
16171
|
|
|
16077
16172
|
// src/secret-compare.ts
|
|
@@ -16167,7 +16262,7 @@ function readPersistedSyncDaemon(stateDir, workspaceId) {
|
|
|
16167
16262
|
const path = syncDaemonStatePath(stateDir, workspaceId);
|
|
16168
16263
|
if (!existsSync31(path)) return void 0;
|
|
16169
16264
|
try {
|
|
16170
|
-
return JSON.parse(
|
|
16265
|
+
return JSON.parse(readFileSync28(path, "utf8"));
|
|
16171
16266
|
} catch {
|
|
16172
16267
|
return void 0;
|
|
16173
16268
|
}
|
|
@@ -16284,11 +16379,11 @@ function describeCause(err) {
|
|
|
16284
16379
|
}
|
|
16285
16380
|
|
|
16286
16381
|
// src/daemon.ts
|
|
16287
|
-
import { existsSync as existsSync33, mkdirSync as mkdirSync21, readFileSync as
|
|
16382
|
+
import { existsSync as existsSync33, mkdirSync as mkdirSync21, readFileSync as readFileSync30, readdirSync as readdirSync13, rmSync as rmSync14, statSync as statSync9, writeFileSync as writeFileSync22 } from "fs";
|
|
16288
16383
|
import { join as join36 } from "path";
|
|
16289
16384
|
|
|
16290
16385
|
// src/interrupted.ts
|
|
16291
|
-
import { existsSync as existsSync32, readdirSync as readdirSync12, readFileSync as
|
|
16386
|
+
import { existsSync as existsSync32, readdirSync as readdirSync12, readFileSync as readFileSync29 } from "fs";
|
|
16292
16387
|
import { join as join35 } from "path";
|
|
16293
16388
|
var INTERRUPTED_MARKER = calloutHeader("NOTE", "Interrupted");
|
|
16294
16389
|
function interruptedComment(b) {
|
|
@@ -16310,7 +16405,7 @@ function runFinished(streamPath) {
|
|
|
16310
16405
|
const path = `${streamPath}.meta.json`;
|
|
16311
16406
|
if (!existsSync32(path)) return false;
|
|
16312
16407
|
try {
|
|
16313
|
-
return JSON.parse(
|
|
16408
|
+
return JSON.parse(readFileSync29(path, "utf8")).running !== true;
|
|
16314
16409
|
} catch {
|
|
16315
16410
|
return true;
|
|
16316
16411
|
}
|
|
@@ -16467,7 +16562,7 @@ function noteDaemonStarted(stateDir, now = Date.now()) {
|
|
|
16467
16562
|
}
|
|
16468
16563
|
function readDaemonStarted(stateDir) {
|
|
16469
16564
|
try {
|
|
16470
|
-
const v = JSON.parse(
|
|
16565
|
+
const v = JSON.parse(readFileSync30(daemonStampFor(stateDir), "utf8")).startedAt;
|
|
16471
16566
|
return typeof v === "number" ? v : null;
|
|
16472
16567
|
} catch {
|
|
16473
16568
|
return null;
|
|
@@ -17018,6 +17113,7 @@ function usage(problem, requested = false) {
|
|
|
17018
17113
|
crew skills sync [route] push only crew's skill files (e.g. grill-me) into the workspace Agent Skills table
|
|
17019
17114
|
crew ship rename NAME [--json] rename this ship in crew.yaml and on every workspace's Ships row
|
|
17020
17115
|
crew repos add [ROUTE] [NAME] [PATH] attach a local checkout to a route in crew.yaml (--dry-run previews)
|
|
17116
|
+
crew repos check [PATH] validate a checkout's .crew.yaml without attaching it (--route ROUTE, --strict, --json)
|
|
17021
17117
|
crew repos clone [ROUTE] NAME clone a Repos row's remote into the route directory and attach it (--dir PATH, --dry-run, --json)
|
|
17022
17118
|
crew repos hooks [ROUTE] NAME suggest test/build hooks for a checkout (--agent, --write route|repo, --recipes, --json)
|
|
17023
17119
|
crew repos list ROUTE the checkouts a route has, with the tracker's Repos row each matches (--json: every Repos row too)
|
|
@@ -17116,6 +17212,22 @@ var KNOWN_COMMANDS = /* @__PURE__ */ new Set([
|
|
|
17116
17212
|
"worktree"
|
|
17117
17213
|
]);
|
|
17118
17214
|
if (!KNOWN_COMMANDS.has(command)) usage(`unknown command "${command}"`);
|
|
17215
|
+
function runReposCheck(dir, ship) {
|
|
17216
|
+
const report = checkRepoSpec(dir, { ship, strict: flag("strict") });
|
|
17217
|
+
if (flag("json")) {
|
|
17218
|
+
const { exitCode: _exit, ...rest } = report;
|
|
17219
|
+
process.stdout.write(`${JSON.stringify(rest)}
|
|
17220
|
+
`);
|
|
17221
|
+
} else {
|
|
17222
|
+
process.stdout.write(renderRepoCheck(report));
|
|
17223
|
+
}
|
|
17224
|
+
process.exitCode = report.exitCode;
|
|
17225
|
+
}
|
|
17226
|
+
var reposCheckBare = command === "repos" && positional[1] === "check" && !value("route") && positional.length < 4;
|
|
17227
|
+
if (reposCheckBare) {
|
|
17228
|
+
runReposCheck(resolvePath(positional[2] ?? "."));
|
|
17229
|
+
process.exit(process.exitCode ?? 0);
|
|
17230
|
+
}
|
|
17119
17231
|
function findSyncRoute(c, name) {
|
|
17120
17232
|
if (!name && c.routes.length > 1) {
|
|
17121
17233
|
throw new ConfigError(`this ship has ${c.routes.length} routes \u2014 name one: ${c.routes.map((r) => r.route).join(", ")}`);
|
|
@@ -17502,7 +17614,7 @@ if (command === "passenger-sync-daemon") {
|
|
|
17502
17614
|
`
|
|
17503
17615
|
);
|
|
17504
17616
|
if (!dryRun) {
|
|
17505
|
-
|
|
17617
|
+
spawnSync7("systemctl", ["--user", "disable", "--now", `${label}.timer`], { cwd: CREW_HOME });
|
|
17506
17618
|
try {
|
|
17507
17619
|
unlinkSync5(staleTimer);
|
|
17508
17620
|
} catch {
|
|
@@ -17542,7 +17654,7 @@ if (command === "passenger-sync-daemon") {
|
|
|
17542
17654
|
async function doctorFix(o) {
|
|
17543
17655
|
const routes = await Promise.all(routesInScope(cfg.routes, named).map((c) => gatherHealth({ route: c.route, enabled: c.enabled, hasRepos: reposOf(c).length > 0 }, () => routeProblems(c))));
|
|
17544
17656
|
const unitExists = (p) => existsSync34(p);
|
|
17545
|
-
const crontab = o.host === "linux" ?
|
|
17657
|
+
const crontab = o.host === "linux" ? spawnSync7("crontab", ["-l"], { encoding: "utf8" }).stdout ?? "" : "";
|
|
17546
17658
|
const schedulerAbsent = o.host !== "windows" ? !schedulerInstalled(planInstall(cfg.ship, CREW_HOME, o.host, detectSystemd(), "run"), unitExists, crontab) : true;
|
|
17547
17659
|
const plan = planFix({ routes, schedulerAbsent, host: o.host });
|
|
17548
17660
|
for (const n of plan.notes) process.stdout.write(`fix: ${n}
|
|
@@ -17558,7 +17670,7 @@ if (command === "passenger-sync-daemon") {
|
|
|
17558
17670
|
for (const a of plan.actions) {
|
|
17559
17671
|
if (a.kind === "enable") {
|
|
17560
17672
|
if (prompter && !await prompter.confirm(`Route ${a.route} passes every check \u2014 set enabled: true in ${cfg.configFile}?`, false)) continue;
|
|
17561
|
-
writeFileSync23(cfg.configFile, planEnable(
|
|
17673
|
+
writeFileSync23(cfg.configFile, planEnable(readFileSync31(cfg.configFile, "utf8"), a.route));
|
|
17562
17674
|
enabledSound.add(a.route);
|
|
17563
17675
|
changed2.push(`enabled route ${a.route} in ${cfg.configFile}`);
|
|
17564
17676
|
} else {
|
|
@@ -18559,7 +18671,7 @@ ${changed2.map((c) => ` - ${c}
|
|
|
18559
18671
|
}
|
|
18560
18672
|
let failed = false;
|
|
18561
18673
|
for (const cmd2 of commands) {
|
|
18562
|
-
const res2 =
|
|
18674
|
+
const res2 = spawnSync7(cmd2[0], cmd2.slice(1), { cwd: CREW_HOME, encoding: "utf8" });
|
|
18563
18675
|
if (res2.stdout) process.stdout.write(res2.stdout);
|
|
18564
18676
|
if (res2.stderr) process.stderr.write(res2.stderr);
|
|
18565
18677
|
if (cmd2 === control.startCommand && (res2.error || res2.status !== 0)) {
|
|
@@ -18577,7 +18689,7 @@ ${changed2.map((c) => ` - ${c}
|
|
|
18577
18689
|
`);
|
|
18578
18690
|
break;
|
|
18579
18691
|
}
|
|
18580
|
-
const res =
|
|
18692
|
+
const res = spawnSync7(cmd[0], cmd.slice(1), { cwd: CREW_HOME, encoding: "utf8" });
|
|
18581
18693
|
if (res.stdout) process.stdout.write(res.stdout);
|
|
18582
18694
|
if (res.stderr) process.stderr.write(res.stderr);
|
|
18583
18695
|
if (daemonSub !== "status" && (res.error || res.status !== 0)) {
|
|
@@ -18828,7 +18940,7 @@ ${changed2.map((c) => ` - ${c}
|
|
|
18828
18940
|
`);
|
|
18829
18941
|
if (!dryRun) {
|
|
18830
18942
|
const resolvedPath = resolvedPathFor(cfg.ship.stateDir, target.route);
|
|
18831
|
-
const raw = existsSync34(resolvedPath) ? JSON.parse(
|
|
18943
|
+
const raw = existsSync34(resolvedPath) ? JSON.parse(readFileSync31(resolvedPath, "utf8")) : {};
|
|
18832
18944
|
raw.agentSkills = result.agentSkills;
|
|
18833
18945
|
mkdirSync22(dirOf(resolvedPath), { recursive: true });
|
|
18834
18946
|
writeFileSync23(resolvedPath, `${JSON.stringify(raw, null, 2)}
|
|
@@ -18854,7 +18966,7 @@ ${changed2.map((c) => ` - ${c}
|
|
|
18854
18966
|
throw e;
|
|
18855
18967
|
}
|
|
18856
18968
|
const oldName = cfg.ship.name;
|
|
18857
|
-
writeFileSync23(cfg.configFile, setShipNameInYaml(
|
|
18969
|
+
writeFileSync23(cfg.configFile, setShipNameInYaml(readFileSync31(cfg.configFile, "utf8"), newName));
|
|
18858
18970
|
cfg.ship.name = newName;
|
|
18859
18971
|
let renameMachineId;
|
|
18860
18972
|
try {
|
|
@@ -18897,11 +19009,37 @@ ${changed2.map((c) => ` - ${c}
|
|
|
18897
19009
|
case "repos": {
|
|
18898
19010
|
const sub = positional[1];
|
|
18899
19011
|
const usage2 = () => {
|
|
18900
|
-
process.stderr.write("crew repos add [workspace/project] [name] [path] [--name N] [--project P] [--dry-run] [--no-create]\ncrew repos clone [workspace/project] <name> [--dir PATH] [--dry-run] [--json]\ncrew repos list <workspace/project> [--json]\ncrew repos hooks [workspace/project] <name> [--suggest] [--agent] [--write route|repo [--replace]] [--clear route] [--recipes ID,ID] [--hook KEY=CMD ...] [--json]\n");
|
|
19012
|
+
process.stderr.write("crew repos add [workspace/project] [name] [path] [--name N] [--project P] [--dry-run] [--no-create]\ncrew repos clone [workspace/project] <name> [--dir PATH] [--dry-run] [--json]\ncrew repos list <workspace/project> [--json]\ncrew repos check [PATH | --route ROUTE [PATH] | ROUTE NAME] [--strict] [--json]\ncrew repos hooks [workspace/project] <name> [--suggest] [--agent] [--write route|repo [--replace]] [--clear route] [--recipes ID,ID] [--hook KEY=CMD ...] [--json]\n");
|
|
18901
19013
|
process.exit(2);
|
|
18902
19014
|
};
|
|
18903
|
-
if (sub !== "add" && sub !== "list" && sub !== "clone" && sub !== "hooks") usage2();
|
|
18904
|
-
const configText =
|
|
19015
|
+
if (sub !== "add" && sub !== "list" && sub !== "clone" && sub !== "hooks" && sub !== "check") usage2();
|
|
19016
|
+
const configText = readFileSync31(cfg.configFile, "utf8");
|
|
19017
|
+
if (sub === "check") {
|
|
19018
|
+
try {
|
|
19019
|
+
const routeArg = value("route") ?? positional[2];
|
|
19020
|
+
const found = cfg.routes.find((r) => r.route === routeArg);
|
|
19021
|
+
if (!found) throw new ReposError(`no route "${routeArg}" in the config (have: ${cfg.routes.map((r) => r.route).join(", ") || "none"})`);
|
|
19022
|
+
const named2 = value("route") ? void 0 : positional[3];
|
|
19023
|
+
const dir = named2 ? repoDirOf(configText, dirname20(cfg.configFile), found.route, named2) : resolvePath(positional[value("route") ? 2 : 3] ?? ".");
|
|
19024
|
+
const target = reposOf(found).find((t) => resolvePath(t.dir) === dir || t.name === named2);
|
|
19025
|
+
const o = target ? found.repoOverrides[target.name] : void 0;
|
|
19026
|
+
runReposCheck(dir, {
|
|
19027
|
+
hooks: { ...found.hooks, ...o?.hooks },
|
|
19028
|
+
labels: { ...found.labels, ...o?.labels },
|
|
19029
|
+
release: mergeRouteRelease(found.release, o?.release),
|
|
19030
|
+
branch: { ...found.branch, ...o?.branch },
|
|
19031
|
+
worktrees: { prefix: shipWorktreePrefixFor(found), nameOverride: o?.worktreeName }
|
|
19032
|
+
});
|
|
19033
|
+
} catch (e) {
|
|
19034
|
+
if (e instanceof ReposError) {
|
|
19035
|
+
process.stderr.write(`crew repos: ${e.message}
|
|
19036
|
+
`);
|
|
19037
|
+
process.exit(2);
|
|
19038
|
+
}
|
|
19039
|
+
throw e;
|
|
19040
|
+
}
|
|
19041
|
+
break;
|
|
19042
|
+
}
|
|
18905
19043
|
if (sub === "hooks") {
|
|
18906
19044
|
try {
|
|
18907
19045
|
const a = parseHooksArgs(positional.slice(2), routeNamesOf(configText));
|
|
@@ -18915,7 +19053,7 @@ ${changed2.map((c) => ` - ${c}
|
|
|
18915
19053
|
}
|
|
18916
19054
|
}, read: (r) => {
|
|
18917
19055
|
try {
|
|
18918
|
-
return
|
|
19056
|
+
return readFileSync31(resolvePath(dir, r), "utf8");
|
|
18919
19057
|
} catch {
|
|
18920
19058
|
return void 0;
|
|
18921
19059
|
}
|
|
@@ -19056,7 +19194,7 @@ ${changed2.map((c) => ` - ${c}
|
|
|
19056
19194
|
let resolvedRemotes;
|
|
19057
19195
|
if (existsSync34(resolvedFile)) {
|
|
19058
19196
|
try {
|
|
19059
|
-
resolvedRemotes = JSON.parse(
|
|
19197
|
+
resolvedRemotes = JSON.parse(readFileSync31(resolvedFile, "utf8"));
|
|
19060
19198
|
} catch {
|
|
19061
19199
|
}
|
|
19062
19200
|
}
|
|
@@ -19077,7 +19215,7 @@ ${changed2.map((c) => ` - ${c}
|
|
|
19077
19215
|
isCheckout,
|
|
19078
19216
|
repoHasHooks: (d) => REPO_CONFIG_FILENAMES.some((f) => {
|
|
19079
19217
|
try {
|
|
19080
|
-
return Object.keys(parseRepoConfig(
|
|
19218
|
+
return Object.keys(parseRepoConfig(readFileSync31(resolvePath(d, f), "utf8"), f).hooks).length > 0;
|
|
19081
19219
|
} catch {
|
|
19082
19220
|
return false;
|
|
19083
19221
|
}
|
|
@@ -19186,7 +19324,7 @@ ${changed2.map((c) => ` - ${c}
|
|
|
19186
19324
|
process.stdout.write(`created Repos row ${String(rowPayload.name)} (${String(rowPayload.remote)}) ${created.id}
|
|
19187
19325
|
`);
|
|
19188
19326
|
if (existsSync34(resolvedFile)) {
|
|
19189
|
-
const raw = JSON.parse(
|
|
19327
|
+
const raw = JSON.parse(readFileSync31(resolvedFile, "utf8"));
|
|
19190
19328
|
raw.repoNames = { ...raw.repoNames, [created.id]: String(rowPayload.name) };
|
|
19191
19329
|
raw.repoRemotes = { ...raw.repoRemotes, [created.id]: String(rowPayload.remote) };
|
|
19192
19330
|
writeFileSync23(resolvedFile, `${JSON.stringify(raw, null, 2)}
|
|
@@ -19544,7 +19682,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
19544
19682
|
process.exit(2);
|
|
19545
19683
|
}
|
|
19546
19684
|
if (found.shipNameAdopted && !dryRun) {
|
|
19547
|
-
writeFileSync23(cfg.configFile, setShipNameInYaml(
|
|
19685
|
+
writeFileSync23(cfg.configFile, setShipNameInYaml(readFileSync31(cfg.configFile, "utf8"), found.shipNameAdopted));
|
|
19548
19686
|
cfg.ship.name = found.shipNameAdopted;
|
|
19549
19687
|
}
|
|
19550
19688
|
if (found.shipRow) {
|
|
@@ -19590,7 +19728,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
19590
19728
|
const hadPreviousFile = existsSync34(resolvedPath);
|
|
19591
19729
|
if (hadPreviousFile) {
|
|
19592
19730
|
try {
|
|
19593
|
-
previous = JSON.parse(
|
|
19731
|
+
previous = JSON.parse(readFileSync31(resolvedPath, "utf8"));
|
|
19594
19732
|
} catch {
|
|
19595
19733
|
}
|
|
19596
19734
|
}
|
|
@@ -20475,7 +20613,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
20475
20613
|
};
|
|
20476
20614
|
const droppedRoutes = [];
|
|
20477
20615
|
try {
|
|
20478
|
-
const rawCfg = parseYaml2(
|
|
20616
|
+
const rawCfg = parseYaml2(readFileSync31(cfg.configFile, "utf8"));
|
|
20479
20617
|
for (const r of rawCfg?.routes ?? []) {
|
|
20480
20618
|
if (typeof r?.route !== "string" || cfg.routes.some((x) => x.route === r.route)) continue;
|
|
20481
20619
|
try {
|
|
@@ -20511,7 +20649,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
20511
20649
|
}
|
|
20512
20650
|
const host = hostPlatform();
|
|
20513
20651
|
const control = host === "windows" ? null : planDaemonControl(CREW_HOME, host, detectSystemd());
|
|
20514
|
-
const unitStatus = control?.statusCommand ?
|
|
20652
|
+
const unitStatus = control?.statusCommand ? spawnSync7(control.statusCommand[0], control.statusCommand.slice(1), { cwd: CREW_HOME, encoding: "utf8" }) : null;
|
|
20515
20653
|
const unitRunning = unitStatus !== null && !unitStatus.error && unitStatus.status === 0;
|
|
20516
20654
|
if (!unitRunning) {
|
|
20517
20655
|
process.stdout.write("crew reload: no daemon unit is running \u2014 nothing to reload (the next run reads crew.yaml fresh)\n");
|
|
@@ -20523,7 +20661,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
20523
20661
|
process.stdout.write("crew reload: requested \u2014 the daemon finishes its in-flight pass, then relaunches with the new config\n");
|
|
20524
20662
|
if (flag("now") && control?.stopCommand && control.startCommand) {
|
|
20525
20663
|
for (const cmd of [control.stopCommand, control.startCommand]) {
|
|
20526
|
-
const res =
|
|
20664
|
+
const res = spawnSync7(cmd[0], cmd.slice(1), { cwd: CREW_HOME, encoding: "utf8" });
|
|
20527
20665
|
if (cmd === control.startCommand && (res.error || res.status !== 0)) {
|
|
20528
20666
|
process.stderr.write(`crew reload: ${cmd.join(" ")} exited ${res.status ?? "error"}
|
|
20529
20667
|
`);
|
|
@@ -20569,7 +20707,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
20569
20707
|
}
|
|
20570
20708
|
case "log": {
|
|
20571
20709
|
try {
|
|
20572
|
-
const text =
|
|
20710
|
+
const text = readFileSync31(cfg.ship.logFile, "utf8").split("\n");
|
|
20573
20711
|
process.stdout.write(`${text.slice(-(Number(value("tail")) || 40)).join("\n")}
|
|
20574
20712
|
`);
|
|
20575
20713
|
} catch {
|
|
@@ -20592,7 +20730,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
20592
20730
|
};
|
|
20593
20731
|
if (!route.resolved) fail(`route ${route.route} is not connected \u2014 run \`crew connect\` first`);
|
|
20594
20732
|
const tracker = new Tracker(route, cfg.ship);
|
|
20595
|
-
const configText =
|
|
20733
|
+
const configText = readFileSync31(cfg.configFile, "utf8");
|
|
20596
20734
|
const cfgBase = dirname20(cfg.configFile);
|
|
20597
20735
|
const configured = configuredRepos(configText, route.route, cfgBase);
|
|
20598
20736
|
const dir = configured.find((r) => r.name === SAMPLE_REPO)?.dir ?? sampleDir(configured.map((r) => r.dir), route.reposBasePath, route.route.split("/")[0]);
|
|
@@ -20630,14 +20768,14 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
20630
20768
|
if (row) {
|
|
20631
20769
|
await tracker.deleteRepoRow(row.id);
|
|
20632
20770
|
if (existsSync34(resolvedFile)) {
|
|
20633
|
-
const raw = JSON.parse(
|
|
20771
|
+
const raw = JSON.parse(readFileSync31(resolvedFile, "utf8"));
|
|
20634
20772
|
if (raw.repoNames) delete raw.repoNames[row.id];
|
|
20635
20773
|
if (raw.repoRemotes) delete raw.repoRemotes[row.id];
|
|
20636
20774
|
writeFileSync23(resolvedFile, `${JSON.stringify(raw, null, 2)}
|
|
20637
20775
|
`);
|
|
20638
20776
|
}
|
|
20639
20777
|
}
|
|
20640
|
-
if (existsSync34(join37(dir, ".git")) &&
|
|
20778
|
+
if (existsSync34(join37(dir, ".git")) && basename12(dir) === SAMPLE_REPO) rmSync15(dir, { recursive: true, force: true });
|
|
20641
20779
|
say("removed", { dir, closed }, `removed the sample repo${closed.length ? `, closed ${closed.join(", ")}` : ""}`);
|
|
20642
20780
|
break;
|
|
20643
20781
|
}
|
|
@@ -20653,7 +20791,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
20653
20791
|
writeFileSync23(join37(dir, "README.md"), SAMPLE_README);
|
|
20654
20792
|
writeFileSync23(join37(dir, ".crew.yaml"), SAMPLE_CREW_YAML);
|
|
20655
20793
|
const g = (...a) => {
|
|
20656
|
-
const r =
|
|
20794
|
+
const r = spawnSync7("git", a, { cwd: dir, encoding: "utf8" });
|
|
20657
20795
|
if (r.status !== 0) fail(`git ${a[0]} failed in ${dir}: ${(r.stderr || r.stdout).trim()}`);
|
|
20658
20796
|
};
|
|
20659
20797
|
g("init", "-b", "main");
|
|
@@ -20670,7 +20808,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
20670
20808
|
});
|
|
20671
20809
|
repoId = (await tracker.createRepoRow(payload)).id;
|
|
20672
20810
|
if (existsSync34(resolvedFile)) {
|
|
20673
|
-
const raw = JSON.parse(
|
|
20811
|
+
const raw = JSON.parse(readFileSync31(resolvedFile, "utf8"));
|
|
20674
20812
|
raw.repoNames = { ...raw.repoNames, [repoId]: SAMPLE_REPO };
|
|
20675
20813
|
raw.repoRemotes = { ...raw.repoRemotes, [repoId]: SAMPLE_REMOTE };
|
|
20676
20814
|
writeFileSync23(resolvedFile, `${JSON.stringify(raw, null, 2)}
|
|
@@ -20901,7 +21039,7 @@ Signed in as ${result.identity.email} \u2014 minted an API key in workspace "${r
|
|
|
20901
21039
|
// `synthesis-tabl-123`. `worktreePrefix` above is kept for
|
|
20902
21040
|
// config-provenance display; this is the field that actually
|
|
20903
21041
|
// describes where a worktree lands by default.
|
|
20904
|
-
worktreeDirPattern: r.config.worktrees.name ?? (r.config.provenance["worktrees.prefix"] !== "default" ? `${r.config.worktrees.prefix}<number>` : `${
|
|
21042
|
+
worktreeDirPattern: r.config.worktrees.name ?? (r.config.provenance["worktrees.prefix"] !== "default" ? `${r.config.worktrees.prefix}<number>` : `${basename12(r.dir.replace(/[/\\]+$/, ""))}-<branch>`),
|
|
20905
21043
|
worktreeParent: resolvePath(r.dir, "..")
|
|
20906
21044
|
})),
|
|
20907
21045
|
waiting: waiting ? { ticket: waiting.ticket, since: waiting.since, streak: fairness.streak() } : null
|
|
@@ -21068,7 +21206,7 @@ routes: ${cfg.routes.map((c) => c.route).join(", ")}
|
|
|
21068
21206
|
const unitPlan = planInstall(cfg.ship, CREW_HOME, host, detectSystemd(), "run");
|
|
21069
21207
|
const abandons = unitAbandonsAgents(unitPlan, (p) => {
|
|
21070
21208
|
try {
|
|
21071
|
-
return
|
|
21209
|
+
return readFileSync31(p, "utf8");
|
|
21072
21210
|
} catch {
|
|
21073
21211
|
return null;
|
|
21074
21212
|
}
|
|
@@ -21111,7 +21249,7 @@ routes: ${cfg.routes.map((c) => c.route).join(", ")}
|
|
|
21111
21249
|
);
|
|
21112
21250
|
for (const r of await resolvedRepos(route)) {
|
|
21113
21251
|
const { config } = r;
|
|
21114
|
-
const worktreePattern = config.worktrees.name ?? (config.provenance["worktrees.prefix"] !== "default" ? `${config.worktrees.prefix}<number>` : `${
|
|
21252
|
+
const worktreePattern = config.worktrees.name ?? (config.provenance["worktrees.prefix"] !== "default" ? `${config.worktrees.prefix}<number>` : `${basename12(r.dir.replace(/[/\\]+$/, ""))}-${effectiveBranchTemplate(config, void 0)}`);
|
|
21115
21253
|
process.stdout.write(
|
|
21116
21254
|
`${`repo ${r.name}:`.padEnd(19)}${r.dir}
|
|
21117
21255
|
worktrees at ../${worktreePattern} (${config.provenance["worktrees.name"] ?? config.provenance["worktrees.prefix"] ?? "default"}), branch ${config.branch.name}
|
package/package.json
CHANGED