@launchsecure/launch-kit 0.0.34 → 0.0.35

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.
@@ -36689,6 +36689,7 @@ function parseArgs() {
36689
36689
  const args = process.argv.slice(2);
36690
36690
  let port = 0;
36691
36691
  let token = null;
36692
+ let course = null;
36692
36693
  const subcommand = args[0] && !args[0].startsWith("--") ? args[0] : null;
36693
36694
  for (let i = 0; i < args.length; i++) {
36694
36695
  if (args[i].startsWith("--token=")) {
@@ -36699,6 +36700,10 @@ function parseArgs() {
36699
36700
  port = parseInt(args[i].slice("--port=".length), 10);
36700
36701
  } else if (args[i] === "--port" && args[i + 1]) {
36701
36702
  port = parseInt(args[++i], 10);
36703
+ } else if (args[i].startsWith("--course=")) {
36704
+ course = args[i].slice("--course=".length);
36705
+ } else if (args[i] === "--course" && args[i + 1]) {
36706
+ course = args[++i];
36702
36707
  }
36703
36708
  }
36704
36709
  if (!port) {
@@ -36715,7 +36720,7 @@ function parseArgs() {
36715
36720
  }
36716
36721
  }
36717
36722
  if (!port) port = 52718;
36718
- return { port, token, serverUrl: LAUNCHSECURE_URL, subcommand };
36723
+ return { port, token, serverUrl: LAUNCHSECURE_URL, subcommand, course };
36719
36724
  }
36720
36725
  function tryListen(server, port, maxRetries = 10) {
36721
36726
  return new Promise((resolve6, reject) => {
@@ -37286,7 +37291,7 @@ if (parsedArgs.subcommand === "mcp:graph") {
37286
37291
  var __isMcpMode = parsedArgs.subcommand === "mcp:graph";
37287
37292
  var __isRadarMode = parsedArgs.subcommand === "radar";
37288
37293
  var radar = null;
37289
- function readLaunchSecureMcpConfig() {
37294
+ function readLaunchSecureMcpConfig(courseOverride) {
37290
37295
  const fix = `Run \`npx @launchsecure/launch-kit@latest refresh\` to re-sync this project (or \`init\` if you haven't bootstrapped yet).`;
37291
37296
  const cred = readCredFile(REPO_ROOT);
37292
37297
  if (!cred) {
@@ -37296,9 +37301,12 @@ function readLaunchSecureMcpConfig() {
37296
37301
  if (!nested) {
37297
37302
  throw new Error(`${CONFIG_FILENAME} is malformed or missing required fields (pat/orgSlug/projectSlug/serverUrl). ${fix}`);
37298
37303
  }
37299
- const profile = nested.profiles[nested.active];
37304
+ const courseName = courseOverride || nested.active;
37305
+ const profile = nested.profiles[courseName];
37300
37306
  if (!profile) {
37301
- throw new Error(`${CONFIG_FILENAME} active course "${nested.active}" not found in profiles. ${fix}`);
37307
+ const available = Object.keys(nested.profiles).join(", ") || "(none)";
37308
+ const why = courseOverride ? `course "${courseName}" (from --course) not found in profiles. Available: ${available}.` : `active course "${courseName}" not found in profiles. ${fix}`;
37309
+ throw new Error(`${CONFIG_FILENAME} ${why}`);
37302
37310
  }
37303
37311
  const { pat, orgSlug, projectSlug, serverUrl } = profile;
37304
37312
  if (!pat || !pat.startsWith("ls_pat_")) {
@@ -37307,7 +37315,7 @@ function readLaunchSecureMcpConfig() {
37307
37315
  if (!orgSlug || !projectSlug || !serverUrl) {
37308
37316
  throw new Error(`${CONFIG_FILENAME} is missing required fields (orgSlug, projectSlug, serverUrl). ${fix}`);
37309
37317
  }
37310
- return { pat, orgSlug, projectSlug, serverUrl, source: `${CONFIG_FILENAME}[${nested.active}]` };
37318
+ return { pat, orgSlug, projectSlug, serverUrl, source: `${CONFIG_FILENAME}[${courseName}]`, course: courseName };
37311
37319
  }
37312
37320
  if (!__isMcpMode) {
37313
37321
  let gracefulShutdown = function() {
@@ -37728,7 +37736,7 @@ if (!__isMcpMode) {
37728
37736
  if (__isRadarMode) {
37729
37737
  let cfg;
37730
37738
  try {
37731
- cfg = readLaunchSecureMcpConfig();
37739
+ cfg = readLaunchSecureMcpConfig(parsedArgs.course);
37732
37740
  } catch (err2) {
37733
37741
  console.error(`[radar] ${err2 instanceof Error ? err2.message : String(err2)}`);
37734
37742
  process.exit(1);
@@ -37736,9 +37744,29 @@ if (!__isMcpMode) {
37736
37744
  const actualPort2 = await tryListen(server, PORT);
37737
37745
  PORT = actualPort2;
37738
37746
  const localUrl = `http://127.0.0.1:${PORT}`;
37739
- console.log(`LaunchPod radar \xB7 ${localUrl}`);
37740
- console.log(` cloud: ${cfg.serverUrl} \xB7 source: ${cfg.source}`);
37741
- console.log(` org: ${cfg.orgSlug} \xB7 project: ${cfg.projectSlug}`);
37747
+ const dim = (s) => `\x1B[2m${s}\x1B[0m`;
37748
+ const bold = (s) => `\x1B[1m${s}\x1B[0m`;
37749
+ const cyan = (s) => `\x1B[36m${s}\x1B[0m`;
37750
+ const COURSE_LABELS = {
37751
+ prod: "Production",
37752
+ production: "Production",
37753
+ staging: "Staging",
37754
+ stage: "Staging",
37755
+ local: "Local",
37756
+ dev: "Development",
37757
+ development: "Development"
37758
+ };
37759
+ const courseLabel = COURSE_LABELS[cfg.course.toLowerCase()] ?? cfg.course.charAt(0).toUpperCase() + cfg.course.slice(1);
37760
+ const row = (label, value) => ` ${dim(label.padEnd(11))}${value}`;
37761
+ console.log("");
37762
+ console.log(` ${cyan("\u25C9")} ${bold("LaunchPod Radar")}`);
37763
+ console.log("");
37764
+ const courseTail = parsedArgs.course ? dim(`\xB7 ${cfg.course} \xB7 --course override`) : dim(`\xB7 ${cfg.course}`);
37765
+ console.log(row("Course", `${bold(courseLabel)} ${courseTail}`));
37766
+ console.log(row("Cloud", cfg.serverUrl));
37767
+ console.log(row("Project", `${cfg.orgSlug} ${dim("/")} ${cfg.projectSlug}`));
37768
+ console.log(row("Active on", localUrl));
37769
+ console.log("");
37742
37770
  try {
37743
37771
  radar = new Radar({
37744
37772
  projectRoot: PROJECT_DIR,
File without changes
File without changes
@@ -473,10 +473,15 @@ function defaultServices() {
473
473
  return [expandShorthand("radar")];
474
474
  }
475
475
  function expandShorthand(name) {
476
+ if (name === "preview") {
477
+ const raw = process.env.PREVIEW_PORT;
478
+ const port = raw && Number.isFinite(Number.parseInt(raw, 10)) ? Number.parseInt(raw, 10) : 3e3;
479
+ return { name: "preview", port, bin: "", args: [], skipSpawn: true };
480
+ }
476
481
  const def = SHORTHANDS[name];
477
482
  if (!def) {
478
483
  throw new Error(
479
- `[launch-kit-services] unknown shorthand "${name}" \u2014 known: ${Object.keys(SHORTHANDS).join(", ")}. Use an object entry { name, port, bin, args } for custom services.`
484
+ `[launch-kit-services] unknown shorthand "${name}" \u2014 known: ${[...Object.keys(SHORTHANDS), "preview"].join(", ")}. Use an object entry { name, port, bin, args } for custom services.`
480
485
  );
481
486
  }
482
487
  return { name, port: def.port, bin: def.bin, args: [...def.args] };
@@ -568,10 +573,14 @@ var init_launch_kit_services = __esm({
568
573
  sequencer: { port: 3517, bin: "launch-sequencer", args: [] },
569
574
  chart: { port: 52819, bin: "launch-chart", args: ["serve"] },
570
575
  deck: { port: 52829, bin: "launch-deck", args: ["serve"] },
571
- council: { port: 52839, bin: "launch-council", args: ["serve"] }
576
+ council: { port: 52839, bin: "launch-council", args: ["serve"] },
577
+ // Claude web terminal — exposes a viewable/drivable `claude` session at
578
+ // `bot.<baseDomain>`. NOTE: no auth gate yet (tracked as a separate
579
+ // high-priority rover-security work item); ships behind a plain link first.
580
+ bot: { port: 52849, bin: "launch-bot", args: ["serve"] }
572
581
  };
573
582
  DNS_NAME_RE = /^[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?$/;
574
- SHORTHAND_NAMES = Object.keys(SHORTHANDS);
583
+ SHORTHAND_NAMES = [...Object.keys(SHORTHANDS), "preview"];
575
584
  }
576
585
  });
577
586
 
@@ -790,6 +799,23 @@ function setupClaudeCredentials() {
790
799
  cfg.lastOnboardingVersion = cfg.lastOnboardingVersion ?? "2.1.159";
791
800
  cfg.numStartups = (cfg.numStartups ?? 0) + 1;
792
801
  cfg.installMethod = cfg.installMethod ?? "global";
802
+ const PREAPPROVED_MCPS = [
803
+ "launch-secure",
804
+ "launch-chart",
805
+ "launch-deck",
806
+ "launch-orbit",
807
+ "launch-recall",
808
+ "launch-beacon",
809
+ "launch-sequencer"
810
+ ];
811
+ const projects = cfg.projects ?? {};
812
+ const wsKey = "/workspace";
813
+ const wsProject = projects[wsKey] ?? {};
814
+ const existingEnabled = Array.isArray(wsProject.enabledMcpjsonServers) ? wsProject.enabledMcpjsonServers : [];
815
+ const mergedEnabled = Array.from(/* @__PURE__ */ new Set([...existingEnabled, ...PREAPPROVED_MCPS]));
816
+ wsProject.enabledMcpjsonServers = mergedEnabled;
817
+ projects[wsKey] = wsProject;
818
+ cfg.projects = projects;
793
819
  (0, import_node_fs3.writeFileSync)(configPath, JSON.stringify(cfg, null, 2));
794
820
  (0, import_node_fs3.chmodSync)(configPath, 384);
795
821
  }
@@ -799,6 +825,27 @@ function setupGitAndGh() {
799
825
  const status = run2("launch-kit", ["setup-git", `--identity=${name} <${email}>`]);
800
826
  if (status !== 0) fail(`[entrypoint] launch-kit setup-git failed (status ${status})`);
801
827
  }
828
+ function detectAndSetPreviewPort() {
829
+ if (process.env.PREVIEW_PORT) return;
830
+ try {
831
+ const pkgPath = "/workspace/package.json";
832
+ if (!(0, import_node_fs3.existsSync)(pkgPath)) return;
833
+ const pkg = JSON.parse((0, import_node_fs3.readFileSync)(pkgPath, "utf-8"));
834
+ const scripts = pkg.scripts ?? {};
835
+ const portRe = /(?:--port[= ]|-p\s+|\bPORT=)(\d{2,5})\b/;
836
+ for (const name of ["dev", "start", "serve"]) {
837
+ const script = scripts[name];
838
+ if (typeof script !== "string") continue;
839
+ const m = script.match(portRe);
840
+ if (m) {
841
+ process.env.PREVIEW_PORT = m[1];
842
+ console.log(`[entrypoint] preview port detected from package.json scripts.${name}: ${m[1]}`);
843
+ return;
844
+ }
845
+ }
846
+ } catch {
847
+ }
848
+ }
802
849
  function initWorkspaceIfEmpty() {
803
850
  process.chdir("/workspace");
804
851
  if ((0, import_node_fs3.existsSync)(".git")) {
@@ -898,6 +945,10 @@ function spawnServiceGroup(services) {
898
945
  let exitedCount = 0;
899
946
  let firstFailure = null;
900
947
  for (const spec of services) {
948
+ if (spec.skipSpawn) {
949
+ console.log(`[entrypoint] ${spec.name} \u2192 ingress-only on port ${spec.port} (no spawn; user starts dev server here)`);
950
+ continue;
951
+ }
901
952
  const args = [...spec.args, "--port", String(spec.port)];
902
953
  console.log(`[entrypoint] starting ${spec.name}: ${spec.bin} ${args.join(" ")}`);
903
954
  const proc = (0, import_node_child_process2.spawn)(spec.bin, args, { stdio: ["ignore", "pipe", "pipe"] });
@@ -934,6 +985,7 @@ async function main() {
934
985
  setupClaudeCredentials();
935
986
  setupGitAndGh();
936
987
  initWorkspaceIfEmpty();
988
+ detectAndSetPreviewPort();
937
989
  let services;
938
990
  try {
939
991
  services = resolveServices();
@@ -1007,10 +1059,28 @@ var import_node_child_process = require("node:child_process");
1007
1059
  function run(cmd, args, stdio = "inherit") {
1008
1060
  return (0, import_node_child_process.spawnSync)(cmd, args, { stdio }).status ?? 1;
1009
1061
  }
1010
- function configureGitForBot(identity) {
1011
- if (process.env.GH_TOKEN) {
1062
+ function hasGh() {
1063
+ return (0, import_node_child_process.spawnSync)("gh", ["--version"], { stdio: "ignore" }).status === 0;
1064
+ }
1065
+ function wireGitHubAuth() {
1066
+ if (!process.env.GH_TOKEN) return false;
1067
+ if (hasGh()) {
1012
1068
  run("gh", ["auth", "setup-git"]);
1069
+ return true;
1013
1070
  }
1071
+ const key = "credential.https://github.com.helper";
1072
+ run("git", ["config", "--global", "--replace-all", key, ""]);
1073
+ run("git", [
1074
+ "config",
1075
+ "--global",
1076
+ "--add",
1077
+ key,
1078
+ `!f() { echo username=x-access-token; echo "password=$GH_TOKEN"; }; f`
1079
+ ]);
1080
+ return true;
1081
+ }
1082
+ function configureGitForBot(identity) {
1083
+ wireGitHubAuth();
1014
1084
  run("git", ["config", "--global", "user.name", identity.name]);
1015
1085
  run("git", ["config", "--global", "user.email", identity.email]);
1016
1086
  run("git", ["config", "--global", "init.defaultBranch", "main"]);
@@ -1389,9 +1459,23 @@ What it does:
1389
1459
  Does NOT clone, re-install deps, re-prompt for PAT, or re-run the onboard
1390
1460
  script. Use \`init\` for those.
1391
1461
 
1462
+ No cred yet? If the repo already exists here but was never init'd (e.g. the
1463
+ repo was created by hand and connected in LS afterward), pass
1464
+ --token/--org/--project and refresh will SEED the cred file, then proceed \u2014
1465
+ no clone, no init required.
1466
+
1392
1467
  Options:
1393
1468
  --dir=<path> Target directory (default: cwd). Must contain a
1394
- valid .launch-secure.cred.config.
1469
+ valid .launch-secure.cred.config, OR pass the
1470
+ --token/--org/--project flags below to seed one.
1471
+ --token=<ls_pat_\u2026> LaunchSecure PAT. Only needed to seed a cred when
1472
+ none exists yet (otherwise read from the cred file).
1473
+ --org=<orgSlug> Org slug for the seeded cred (with --token).
1474
+ --project=<projectSlug> Project slug for the seeded cred (with --token).
1475
+ --url=<serverUrl> LaunchSecure base URL for the seeded cred
1476
+ (default: ${DEFAULT_SERVER_URL}).
1477
+ --course=<name> Course/profile name for the seeded cred
1478
+ (default: inferred from --url).
1395
1479
  --no-migrate-safety Skip refreshing the migrate-safety scaffold.
1396
1480
  --no-ls-marketplace Skip refreshing the launch-secure marketplace.
1397
1481
  --no-recall-hook Skip refreshing the recall-hook scaffold.
@@ -1453,9 +1537,13 @@ Options:
1453
1537
  --git-identity="N <e>" Non-interactive git identity for service-account /
1454
1538
  CI / Docker runs. Configures git user.name, user.email,
1455
1539
  init.defaultBranch=main, pull.rebase=false; also
1456
- wires GH_TOKEN into git's credential helper via
1457
- \`gh auth setup-git\` when GH_TOKEN is set. Example:
1540
+ wires GH_TOKEN into git's credential helper (via
1541
+ \`gh auth setup-git\`, or a gh-less helper when the
1542
+ GitHub CLI is absent) when GH_TOKEN is set. Example:
1458
1543
  --git-identity="Radar Bot <radar@launchpod.local>".
1544
+ Note: GH_TOKEN is wired for the clone even without
1545
+ --git-identity, so private-repo clones work on hosts
1546
+ with no \`gh\` as long as GH_TOKEN is set.
1459
1547
  --no-install Skip dependency install step (also skips the onboard
1460
1548
  script \u2014 install is its prerequisite).
1461
1549
  --no-onboard Skip the onboard script even when install runs.
@@ -1557,9 +1645,9 @@ function preflight() {
1557
1645
  const nodeMajor = parseInt(process.versions.node.split(".")[0], 10);
1558
1646
  if (nodeMajor < 18) fail2(`Node.js >= 18 required (current: ${process.versions.node}).`);
1559
1647
  if (!which("git")) fail2("git not found in PATH. Install git: https://git-scm.com/downloads");
1560
- const hasGh = which("gh") !== null;
1561
- ok(`preflight ok \u2014 node ${process.versions.node}, git present${hasGh ? ", gh present" : ", gh not found (will use git for clone)"}`);
1562
- return { hasGh };
1648
+ const hasGh2 = which("gh") !== null;
1649
+ ok(`preflight ok \u2014 node ${process.versions.node}, git present${hasGh2 ? ", gh present" : ", gh not found (will use git for clone)"}`);
1650
+ return { hasGh: hasGh2 };
1563
1651
  }
1564
1652
  var PROJECT_INFO_TIMEOUT_MS = 3e4;
1565
1653
  var PROJECT_INFO_MAX_ATTEMPTS = 3;
@@ -1712,11 +1800,11 @@ function dirIsEmpty(dir) {
1712
1800
  if (!fs5.existsSync(dir)) return true;
1713
1801
  return fs5.readdirSync(dir).length === 0;
1714
1802
  }
1715
- function cloneRepo(repoUrl, targetDir, hasGh) {
1803
+ function cloneRepo(repoUrl, targetDir, hasGh2) {
1716
1804
  const isGithub = /github\.com/i.test(repoUrl);
1717
1805
  let cmd;
1718
1806
  let args;
1719
- if (hasGh && isGithub) {
1807
+ if (hasGh2 && isGithub) {
1720
1808
  cmd = "gh";
1721
1809
  args = ["repo", "clone", repoUrl, targetDir];
1722
1810
  info(`cloning via gh: ${repoUrl} \u2192 ${targetDir}`);
@@ -1732,7 +1820,7 @@ function cloneRepo(repoUrl, targetDir, hasGh) {
1732
1820
  const res = (0, import_node_child_process3.spawnSync)(cmd, args, { stdio: "inherit" });
1733
1821
  if (res.status !== 0) {
1734
1822
  fail2(
1735
- `Clone failed (${cmd} exited ${res.status}). For private repos make sure your GitHub auth is set up: \`gh auth login\` or an SSH key on your GitHub account.`
1823
+ `Clone failed (${cmd} exited ${res.status}). For private repos make sure your GitHub auth is set up: \`gh auth login\`, set GH_TOKEN=<a PAT with repo scope> before re-running (works without the GitHub CLI), or add an SSH key to your GitHub account.`
1736
1824
  );
1737
1825
  }
1738
1826
  if (!fs5.existsSync(path5.join(targetDir, ".git"))) {
@@ -2383,9 +2471,26 @@ async function mainRefresh(args) {
2383
2471
  ok(`wrote ${CONFIG_FILENAME} (course: ${courseName})`);
2384
2472
  }
2385
2473
  }
2474
+ if (!cred && args.token && args.orgSlug && args.projectSlug) {
2475
+ const courseName = args.course ?? inferCourseName(args.serverUrl);
2476
+ const seedCfg = {
2477
+ pat: args.token,
2478
+ orgSlug: args.orgSlug,
2479
+ projectSlug: args.projectSlug,
2480
+ serverUrl: args.serverUrl
2481
+ };
2482
+ info(`no ${CONFIG_FILENAME} found \u2014 seeding it from --token/--org/--project (course: ${courseName})`);
2483
+ writeConfigFile(targetDir, seedCfg, courseName);
2484
+ cred = { active: courseName, profiles: { [courseName]: seedCfg } };
2485
+ }
2386
2486
  if (!cred) {
2387
2487
  fail2(
2388
- `no ${CONFIG_FILENAME} found at ${targetDir}, and could not recover from .mcp.json. Refresh requires an existing cred or a hardcoded launch-secure MCP entry \u2014 run \`npx @launchsecure/launch-kit init --token=<pat> --org=<org> --project=<project> --dir=${path5.relative(cwd, targetDir) || "."}\` first.`
2488
+ `no ${CONFIG_FILENAME} found at ${targetDir}, and could not recover from .mcp.json.
2489
+ Refresh re-applies configs to an already-wired checkout, so it needs a cred. Two ways forward:
2490
+ \u2022 Seed it inline (you already have the repo): re-run refresh with credentials \u2014
2491
+ launch-kit refresh --dir=${path5.relative(cwd, targetDir) || "."} --token=<pat> --org=<org> --project=<project>
2492
+ \u2022 Or run a full init in place \u2014 it detects this existing repo and skips the clone:
2493
+ launch-kit init --token=<pat> --org=<org> --project=<project> --dir=${path5.relative(cwd, targetDir) || "."}`
2389
2494
  );
2390
2495
  }
2391
2496
  const nested = toNested(cred);
@@ -2471,11 +2576,13 @@ async function mainInit(args) {
2471
2576
  ["project", args.projectSlug],
2472
2577
  ["server", args.serverUrl]
2473
2578
  ]);
2474
- const { hasGh } = preflight();
2475
- phase("preflight", { status: "ok", summary: `node ${process.versions.node}${hasGh ? " \xB7 git \xB7 gh" : " \xB7 git (gh not found \u2014 will use git for clone)"}` });
2579
+ const { hasGh: hasGh2 } = preflight();
2580
+ phase("preflight", { status: "ok", summary: `node ${process.versions.node}${hasGh2 ? " \xB7 git \xB7 gh" : " \xB7 git (gh not found \u2014 will use git for clone)"}` });
2476
2581
  if (args.gitIdentity) {
2477
2582
  configureGitForBot(args.gitIdentity);
2478
- phase("git identity", { status: "ok", summary: `${args.gitIdentity.name} <${args.gitIdentity.email}>${process.env.GH_TOKEN ? " \xB7 gh credential helper wired" : ""}` });
2583
+ phase("git identity", { status: "ok", summary: `${args.gitIdentity.name} <${args.gitIdentity.email}>${process.env.GH_TOKEN ? " \xB7 git credential helper wired" : ""}` });
2584
+ } else if (wireGitHubAuth()) {
2585
+ phase("git auth", { status: "ok", summary: hasGh2 ? "GH_TOKEN \u2192 gh credential helper" : "GH_TOKEN \u2192 git credential helper (gh not found)" });
2479
2586
  }
2480
2587
  info(`resolving project ${args.orgSlug}/${args.projectSlug} on ${args.serverUrl} \u2026`);
2481
2588
  let resolved;
@@ -2500,7 +2607,8 @@ async function mainInit(args) {
2500
2607
  if (isGitRepo(targetDir)) {
2501
2608
  const existingRemote = gitRemoteUrl(targetDir);
2502
2609
  if (existingRemote && normalizeRepoUrl(existingRemote) === normalizedRemote) {
2503
- ok(`${targetDir} is already a clone of ${repoUrl} \u2014 skipping clone, refreshing configs only`);
2610
+ ok(`${targetDir} is already a clone of ${repoUrl} \u2014 wiring this existing repo (skipping clone, no GitHub auth needed)`);
2611
+ info(`tip: once wired, use \`launch-kit refresh\` here to re-apply configs without re-running init`);
2504
2612
  skipClone = true;
2505
2613
  } else {
2506
2614
  fail2(`${targetDir} is a git repo but its remote (${existingRemote ?? "unknown"}) does not match ${repoUrl}. Refusing to overwrite. Pass --dir=<other-path>.`);
@@ -2512,7 +2620,7 @@ async function mainInit(args) {
2512
2620
  const relTarget = path5.relative(cwd, targetDir) || ".";
2513
2621
  if (!skipClone) {
2514
2622
  section(`Cloning ${repoUrl}`);
2515
- cloneRepo(repoUrl, targetDir, hasGh);
2623
+ cloneRepo(repoUrl, targetDir, hasGh2);
2516
2624
  phase("clone", { status: "ok", summary: `\u2192 ${relTarget}` });
2517
2625
  } else {
2518
2626
  phase("clone", { status: "in-sync", summary: `${relTarget} (already a clone of this repo)` });