@mnci/cli 4.50.0 → 4.51.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/dist/cli.js CHANGED
@@ -8239,14 +8239,130 @@ function runKinds(options) {
8239
8239
  }
8240
8240
  }
8241
8241
 
8242
- // src/workspace-diagnostics/check-invariants.use-case.ts
8242
+ // src/dev-servers/run-dev.use-case.ts
8243
+ var import_prompts4 = require("@inquirer/prompts");
8244
+
8245
+ // src/dev-servers/spawn-project.client.ts
8243
8246
  var import_node_child_process = require("child_process");
8247
+ var import_node_readline = require("readline");
8248
+ var import_cross_spawn3 = __toESM(require("cross-spawn"));
8249
+ function spawnProject(workspaceRoot, project, target, onLine) {
8250
+ const windows = process.platform === "win32";
8251
+ const child = (0, import_cross_spawn3.default)("npx", ["nx", "run", `${project}:${target}`], { cwd: workspaceRoot, stdio: ["ignore", "pipe", "pipe"], detached: !windows });
8252
+ for (const stream of [child.stdout, child.stderr]) {
8253
+ if (stream !== null) {
8254
+ (0, import_node_readline.createInterface)({ input: stream }).on("line", onLine);
8255
+ }
8256
+ }
8257
+ const exited = new Promise((resolve2) => {
8258
+ child.on("error", () => {
8259
+ resolve2(1);
8260
+ });
8261
+ child.on("close", (code) => {
8262
+ resolve2(code ?? 1);
8263
+ });
8264
+ });
8265
+ const stop = () => {
8266
+ if (child.pid === void 0 || child.exitCode !== null) {
8267
+ return;
8268
+ }
8269
+ if (windows) {
8270
+ (0, import_node_child_process.spawnSync)("taskkill", ["/pid", String(child.pid), "/T", "/F"], { stdio: "ignore" });
8271
+ } else {
8272
+ try {
8273
+ process.kill(-child.pid, "SIGTERM");
8274
+ } catch {
8275
+ child.kill("SIGTERM");
8276
+ }
8277
+ }
8278
+ };
8279
+ return { stop, exited };
8280
+ }
8281
+
8282
+ // src/dev-servers/startable-projects.use-case.ts
8283
+ var import_node_path36 = require("path");
8284
+ var START_SUFFIX = ":start";
8285
+ var NX_RUN = /\bnx run [^\s:]+:(\S+)/;
8286
+ function listStartableProjects(workspaceRoot) {
8287
+ const manifestPath = (0, import_node_path36.join)(workspaceRoot, "package.json");
8288
+ if (!fileExists(manifestPath)) {
8289
+ return [];
8290
+ }
8291
+ const scripts = readJson(manifestPath).scripts ?? {};
8292
+ return Object.entries(scripts).filter(([script]) => script.endsWith(START_SUFFIX)).map(([script, command]) => ({ project: script.slice(0, -START_SUFFIX.length), target: NX_RUN.exec(command)?.[1] ?? "start" })).toSorted((a, b) => a.project.localeCompare(b.project));
8293
+ }
8294
+
8295
+ // src/dev-servers/run-dev.use-case.ts
8296
+ async function chooseProjects(available) {
8297
+ return await (0, import_prompts4.checkbox)({
8298
+ message: "Which projects should start together?",
8299
+ choices: available.map((name) => ({ name, value: name })),
8300
+ required: true
8301
+ });
8302
+ }
8303
+ async function selectProjects(available, named, options, dependencies) {
8304
+ const names = available.map((entry) => entry.project);
8305
+ const unknown = named.filter((name) => !names.includes(name));
8306
+ if (unknown.length > 0) {
8307
+ throw new Error(`Cannot start ${unknown.join(", ")}: not a project with a start command. Startable: ${names.join(", ")}.`);
8308
+ }
8309
+ let chosen = named;
8310
+ if (chosen.length === 0) {
8311
+ if (options.all === true) {
8312
+ chosen = names;
8313
+ } else if (dependencies.interactive ?? process.stdin.isTTY) {
8314
+ chosen = await (dependencies.choose ?? chooseProjects)(names);
8315
+ } else {
8316
+ throw new Error(`Name the projects to start, or pass --all. Startable: ${names.join(", ")}.`);
8317
+ }
8318
+ }
8319
+ return available.filter((entry) => chosen.includes(entry.project));
8320
+ }
8321
+ async function runDev(workspaceRoot, named, options = {}, dependencies = {}) {
8322
+ const available = listStartableProjects(workspaceRoot);
8323
+ if (available.length === 0) {
8324
+ throw new Error("No project here has a start command. Add an app (`mnci add`) first; libraries are not started.");
8325
+ }
8326
+ const selected = await selectProjects(available, named, options, dependencies);
8327
+ for (const { project, target } of selected) {
8328
+ logger.info(`${project}: nx run ${project}:${target}`);
8329
+ }
8330
+ if (options.dryRun === true) {
8331
+ return;
8332
+ }
8333
+ const start = dependencies.start ?? ((project, target, onLine) => spawnProject(workspaceRoot, project, target, onLine));
8334
+ const print = dependencies.print ?? ((line) => {
8335
+ console.log(line);
8336
+ });
8337
+ const running = selected.map(({ project, target }) => start(project, target, (line) => {
8338
+ print(`[${project}] ${line}`);
8339
+ }));
8340
+ const stopAll = () => {
8341
+ for (const each of running) {
8342
+ each.stop();
8343
+ }
8344
+ };
8345
+ process.once("SIGINT", stopAll);
8346
+ process.once("SIGTERM", stopAll);
8347
+ const first = await Promise.race(running.map(async (each, index) => ({ index, status: await each.exited })));
8348
+ stopAll();
8349
+ await Promise.all(running.map(async (each) => await each.exited));
8350
+ process.removeListener("SIGINT", stopAll);
8351
+ process.removeListener("SIGTERM", stopAll);
8352
+ logger.info(`${selected[first.index].project} stopped (${first.status}), so the rest were stopped too.`);
8353
+ if (first.status !== 0) {
8354
+ process.exitCode = first.status;
8355
+ }
8356
+ }
8357
+
8358
+ // src/workspace-diagnostics/check-invariants.use-case.ts
8359
+ var import_node_child_process2 = require("child_process");
8244
8360
  var import_node_fs32 = require("fs");
8245
- var import_node_path37 = require("path");
8361
+ var import_node_path38 = require("path");
8246
8362
 
8247
8363
  // src/workspace-diagnostics/locate-stranded-release-tags.use-case.ts
8248
8364
  var import_node_fs31 = require("fs");
8249
- var import_node_path36 = require("path");
8365
+ var import_node_path37 = require("path");
8250
8366
 
8251
8367
  // src/workspace-diagnostics/stranded-release-tags.algorithm.ts
8252
8368
  function compareVersions2(left, right) {
@@ -8283,7 +8399,7 @@ function locateStrandedReleaseTags(workspaceRoot, tagPattern) {
8283
8399
  if (listed.status !== 0) {
8284
8400
  return [];
8285
8401
  }
8286
- const projects = (0, import_node_fs31.globSync)("{apps,libs,packages}/*/package.json", { cwd: workspaceRoot }).map((path) => readJson((0, import_node_path36.join)(workspaceRoot, path))).filter((manifest) => manifest.private !== true && manifest.name !== void 0).map((manifest) => manifest.name);
8402
+ const projects = (0, import_node_fs31.globSync)("{apps,libs,packages}/*/package.json", { cwd: workspaceRoot }).map((path) => readJson((0, import_node_path37.join)(workspaceRoot, path))).filter((manifest) => manifest.private !== true && manifest.name !== void 0).map((manifest) => manifest.name);
8287
8403
  return findStrandedReleaseTags(projects, listed.stdout.split(/\r?\n/).filter(Boolean));
8288
8404
  }
8289
8405
 
@@ -8314,9 +8430,9 @@ function checkEslintConfigs(workspaceRoot) {
8314
8430
  ];
8315
8431
  }
8316
8432
  function checkEslintEntryPointReachesTheRules(workspaceRoot) {
8317
- const rules = (0, import_node_path37.join)(workspaceRoot, ESLINT_MNCI_FILENAME);
8433
+ const rules = (0, import_node_path38.join)(workspaceRoot, ESLINT_MNCI_FILENAME);
8318
8434
  if (!(0, import_node_fs32.existsSync)(rules)) return [];
8319
- const entryPoint = (0, import_node_path37.join)(workspaceRoot, ESLINT_USER_FILENAME);
8435
+ const entryPoint = (0, import_node_path38.join)(workspaceRoot, ESLINT_USER_FILENAME);
8320
8436
  const source = (0, import_node_fs32.existsSync)(entryPoint) ? (0, import_node_fs32.readFileSync)(entryPoint, "utf8") : "";
8321
8437
  return [
8322
8438
  {
@@ -8330,7 +8446,7 @@ function checkEslintEntryPointReachesTheRules(workspaceRoot) {
8330
8446
  function declaredDevDependencies(workspaceRoot) {
8331
8447
  try {
8332
8448
  const manifest = readJson(
8333
- (0, import_node_path37.join)(workspaceRoot, "package.json")
8449
+ (0, import_node_path38.join)(workspaceRoot, "package.json")
8334
8450
  );
8335
8451
  return manifest.devDependencies ?? {};
8336
8452
  } catch {
@@ -8338,7 +8454,7 @@ function declaredDevDependencies(workspaceRoot) {
8338
8454
  }
8339
8455
  }
8340
8456
  function checkNoRetiredFormatter(workspaceRoot) {
8341
- const files = RETIRED_FORMATTER_FILES.filter((file) => fileExists((0, import_node_path37.join)(workspaceRoot, file)));
8457
+ const files = RETIRED_FORMATTER_FILES.filter((file) => fileExists((0, import_node_path38.join)(workspaceRoot, file)));
8342
8458
  const declared = ["prettier", "eslint-config-prettier", "oxlint", "oxfmt", "@mnci/oxlint-config"].filter((name) => declaredDevDependencies(workspaceRoot)[name] !== void 0);
8343
8459
  const stale = [...files, ...declared];
8344
8460
  if (stale.length === 0) {
@@ -8364,7 +8480,7 @@ function checkEslintPlugin(nxJson) {
8364
8480
  };
8365
8481
  }
8366
8482
  function checkResolvedEslint(workspaceRoot) {
8367
- const manifestPath = (0, import_node_path37.join)(workspaceRoot, "node_modules/eslint/package.json");
8483
+ const manifestPath = (0, import_node_path38.join)(workspaceRoot, "node_modules/eslint/package.json");
8368
8484
  if (!fileExists(manifestPath)) {
8369
8485
  return void 0;
8370
8486
  }
@@ -8382,7 +8498,7 @@ function checkResolvedEslint(workspaceRoot) {
8382
8498
  }
8383
8499
  }
8384
8500
  function checkNpmrc(workspaceRoot, registry, scope) {
8385
- const npmrcPath = (0, import_node_path37.join)(workspaceRoot, ".npmrc");
8501
+ const npmrcPath = (0, import_node_path38.join)(workspaceRoot, ".npmrc");
8386
8502
  if (!registry || !fileExists(npmrcPath)) {
8387
8503
  return void 0;
8388
8504
  }
@@ -8403,7 +8519,7 @@ function checkNpmrc(workspaceRoot, registry, scope) {
8403
8519
  };
8404
8520
  }
8405
8521
  function checkNpmrcCredentialResolves(workspaceRoot, environment = process.env) {
8406
- const npmrcPath = (0, import_node_path37.join)(workspaceRoot, ".npmrc");
8522
+ const npmrcPath = (0, import_node_path38.join)(workspaceRoot, ".npmrc");
8407
8523
  if (!fileExists(npmrcPath)) {
8408
8524
  return void 0;
8409
8525
  }
@@ -8431,8 +8547,8 @@ function checkNpmrcCredentialResolves(workspaceRoot, environment = process.env)
8431
8547
  };
8432
8548
  }
8433
8549
  function checkNpmAuthMatchesPipeline(workspaceRoot, registry) {
8434
- const npmrcPath = (0, import_node_path37.join)(workspaceRoot, ".npmrc");
8435
- const pipelinePath = (0, import_node_path37.join)(workspaceRoot, "azure-pipelines.yml");
8550
+ const npmrcPath = (0, import_node_path38.join)(workspaceRoot, ".npmrc");
8551
+ const pipelinePath = (0, import_node_path38.join)(workspaceRoot, "azure-pipelines.yml");
8436
8552
  if (registry?.kind !== "azure-artifacts" || !fileExists(npmrcPath) || !fileExists(pipelinePath)) {
8437
8553
  return void 0;
8438
8554
  }
@@ -8488,7 +8604,7 @@ function checkVersionActions(workspaceRoot) {
8488
8604
  ].map((manifest) => toPosix2(manifest));
8489
8605
  return candidates.flatMap((manifest) => {
8490
8606
  const projectRoot = manifest.slice(0, manifest.lastIndexOf("/"));
8491
- const projectJsonPath = (0, import_node_path37.join)(workspaceRoot, projectRoot, "project.json");
8607
+ const projectJsonPath = (0, import_node_path38.join)(workspaceRoot, projectRoot, "project.json");
8492
8608
  let hasOverride = false;
8493
8609
  try {
8494
8610
  const projectJson = readJson(projectJsonPath);
@@ -8517,7 +8633,7 @@ function declaredTargets(workspaceRoot, projectRoot) {
8517
8633
  ]) {
8518
8634
  try {
8519
8635
  const targets = extract(
8520
- readJson((0, import_node_path37.join)(workspaceRoot, projectRoot, file))
8636
+ readJson((0, import_node_path38.join)(workspaceRoot, projectRoot, file))
8521
8637
  );
8522
8638
  if (targets && typeof targets === "object") {
8523
8639
  sources.push({
@@ -8549,7 +8665,7 @@ function checkOneTargetsFiles(workspaceRoot, source, targetName, target) {
8549
8665
  return TARGET_FILE_OPTIONS.flatMap((option) => {
8550
8666
  const value = target?.options?.[option];
8551
8667
  const checkable = typeof value === "string" && !value.includes("{");
8552
- if (!checkable || fileExists((0, import_node_path37.join)(workspaceRoot, value))) {
8668
+ if (!checkable || fileExists((0, import_node_path38.join)(workspaceRoot, value))) {
8553
8669
  return [];
8554
8670
  }
8555
8671
  return [
@@ -8574,7 +8690,7 @@ ${result.stdout.trim()}` : ""}`,
8574
8690
  }
8575
8691
  function hasCCompiler() {
8576
8692
  const candidates = [process.env.CC?.split(" ", 1)[0], "gcc", "clang", "cc"];
8577
- return candidates.some((command) => command !== void 0 && command !== "" && (0, import_node_child_process.spawnSync)(command, ["--version"], { stdio: "ignore" }).status === 0);
8693
+ return candidates.some((command) => command !== void 0 && command !== "" && (0, import_node_child_process2.spawnSync)(command, ["--version"], { stdio: "ignore" }).status === 0);
8578
8694
  }
8579
8695
  function checkGoPluginRegistered(workspaceRoot) {
8580
8696
  if (!hasGoProject(workspaceRoot)) {
@@ -8588,7 +8704,7 @@ function checkGoPluginRegistered(workspaceRoot) {
8588
8704
  };
8589
8705
  }
8590
8706
  function checkGoWorkInSync(workspaceRoot) {
8591
- const goWorkPath = (0, import_node_path37.join)(workspaceRoot, "go.work");
8707
+ const goWorkPath = (0, import_node_path38.join)(workspaceRoot, "go.work");
8592
8708
  if (!fileExists(goWorkPath)) {
8593
8709
  return void 0;
8594
8710
  }
@@ -8596,7 +8712,7 @@ function checkGoWorkInSync(workspaceRoot) {
8596
8712
  const blockEntries = Array.from(contents.matchAll(/(?:^|\n)\s*use\s+\(([^)]*)\)/g), (match) => match[1].split("\n")).flat();
8597
8713
  const singleEntries = Array.from(contents.matchAll(/(?:^|\n)\s*use\s+(\.\S+)/g), (match) => match[1]);
8598
8714
  const uses = [...blockEntries, ...singleEntries].map((entry) => entry.trim()).filter(Boolean);
8599
- const stale = uses.filter((entry) => !(0, import_node_fs32.existsSync)((0, import_node_path37.join)(workspaceRoot, entry, "go.mod")));
8715
+ const stale = uses.filter((entry) => !(0, import_node_fs32.existsSync)((0, import_node_path38.join)(workspaceRoot, entry, "go.mod")));
8600
8716
  return {
8601
8717
  check: "every go.work `use` entry points to a module that still exists",
8602
8718
  ok: stale.length === 0,
@@ -8612,9 +8728,9 @@ function checkNativeApps(workspaceRoot) {
8612
8728
  { file: ".github/workflows/ci.yml", marker: "native (${{ matrix.os }})" },
8613
8729
  { file: "azure-pipelines.yml", marker: "- job: native" }
8614
8730
  ];
8615
- const findings = pipelines.filter(({ file }) => fileExists((0, import_node_path37.join)(workspaceRoot, file))).map(({ file, marker }) => ({
8731
+ const findings = pipelines.filter(({ file }) => fileExists((0, import_node_path38.join)(workspaceRoot, file))).map(({ file, marker }) => ({
8616
8732
  check: `${file} builds the native apps on every OS`,
8617
- ok: (0, import_node_fs32.readFileSync)((0, import_node_path37.join)(workspaceRoot, file), "utf8").includes(marker),
8733
+ ok: (0, import_node_fs32.readFileSync)((0, import_node_path38.join)(workspaceRoot, file), "utf8").includes(marker),
8618
8734
  detail: `${file} has no native job: CI would verify a cgo app on the one agent that cannot build it`,
8619
8735
  remedy: "run `mnci upgrade`"
8620
8736
  }));
@@ -8627,7 +8743,7 @@ function checkNativeApps(workspaceRoot) {
8627
8743
  return findings;
8628
8744
  }
8629
8745
  function checkNoRootRuntimeDependencies(workspaceRoot) {
8630
- const manifestPath = (0, import_node_path37.join)(workspaceRoot, "package.json");
8746
+ const manifestPath = (0, import_node_path38.join)(workspaceRoot, "package.json");
8631
8747
  if (!fileExists(manifestPath)) {
8632
8748
  return void 0;
8633
8749
  }
@@ -8651,7 +8767,7 @@ function checkRollupSourceMaps(workspaceRoot) {
8651
8767
  cwd: workspaceRoot
8652
8768
  }).map((config) => toPosix2(config));
8653
8769
  return configs.flatMap((relativePath) => {
8654
- const configPath = (0, import_node_path37.join)(workspaceRoot, relativePath);
8770
+ const configPath = (0, import_node_path38.join)(workspaceRoot, relativePath);
8655
8771
  let config;
8656
8772
  try {
8657
8773
  config = (0, import_node_fs32.readFileSync)(configPath, "utf8");
@@ -8673,7 +8789,7 @@ function checkDeclarationSpecifiers(workspaceRoot) {
8673
8789
  cwd: workspaceRoot
8674
8790
  }).map((config) => toPosix2(config));
8675
8791
  return configs.flatMap((relativePath) => {
8676
- const configPath = (0, import_node_path37.join)(workspaceRoot, relativePath);
8792
+ const configPath = (0, import_node_path38.join)(workspaceRoot, relativePath);
8677
8793
  if (!fileExists(configPath)) {
8678
8794
  return [];
8679
8795
  }
@@ -8704,8 +8820,8 @@ function checkDeclarationSpecifiers(workspaceRoot) {
8704
8820
  }
8705
8821
  function checkPipelinePhases(workspaceRoot) {
8706
8822
  const files = ["azure-pipelines.yml", ".github/workflows/ci.yml"];
8707
- return files.filter((file) => fileExists((0, import_node_path37.join)(workspaceRoot, file))).flatMap((file) => {
8708
- const { verifyActive, switchedOff } = inspectPipeline((0, import_node_fs32.readFileSync)((0, import_node_path37.join)(workspaceRoot, file), "utf8"));
8823
+ return files.filter((file) => fileExists((0, import_node_path38.join)(workspaceRoot, file))).flatMap((file) => {
8824
+ const { verifyActive, switchedOff } = inspectPipeline((0, import_node_fs32.readFileSync)((0, import_node_path38.join)(workspaceRoot, file), "utf8"));
8709
8825
  const verify = {
8710
8826
  check: `${file} runs the enforced verify phase`,
8711
8827
  ok: verifyActive,
@@ -8746,7 +8862,7 @@ function checkDependencyRangesNotPreserved(nxJson) {
8746
8862
  };
8747
8863
  }
8748
8864
  function checkLockfileHasNoLocalRegistry(workspaceRoot) {
8749
- if (!fileExists((0, import_node_path37.join)(workspaceRoot, "package-lock.json"))) {
8865
+ if (!fileExists((0, import_node_path38.join)(workspaceRoot, "package-lock.json"))) {
8750
8866
  return void 0;
8751
8867
  }
8752
8868
  return {
@@ -8766,7 +8882,7 @@ function checkReleaseTagsResolve(workspaceRoot, nxJson) {
8766
8882
  };
8767
8883
  }
8768
8884
  function collectFindings(workspaceRoot) {
8769
- const nxJsonPath = (0, import_node_path37.join)(workspaceRoot, "nx.json");
8885
+ const nxJsonPath = (0, import_node_path38.join)(workspaceRoot, "nx.json");
8770
8886
  if (!fileExists(nxJsonPath)) {
8771
8887
  throw new Error(
8772
8888
  `No nx.json found in ${workspaceRoot} \u2014 run 'mnci doctor' from the workspace root.`
@@ -8832,7 +8948,7 @@ function runDoctor(workspaceRoot, options = {}) {
8832
8948
 
8833
8949
  // src/repository-adoption/adopt-dependencies.use-case.ts
8834
8950
  var import_node_fs33 = require("fs");
8835
- var import_node_path38 = require("path");
8951
+ var import_node_path39 = require("path");
8836
8952
 
8837
8953
  // src/repository-adoption/clean-working-tree.validator.ts
8838
8954
  function requireCleanWorkingTree(repositoryRoot, capture = runCapture) {
@@ -8903,7 +9019,7 @@ var SOURCE_FILE = /\.(?:[cm]?[jt]s|[jt]sx)$/;
8903
9019
  var TEST_FILE = /\.(?:spec|test)\.[^.]+$|[\\/](?:__tests__|tests?|e2e)[\\/]/;
8904
9020
  var SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set(["node_modules", "dist", "build", "coverage", "out", ".git", ".nx", "tmp"]);
8905
9021
  function sourceFiles(directory, relative = "") {
8906
- const entries = (0, import_node_fs33.readdirSync)((0, import_node_path38.join)(directory, relative), { withFileTypes: true });
9022
+ const entries = (0, import_node_fs33.readdirSync)((0, import_node_path39.join)(directory, relative), { withFileTypes: true });
8907
9023
  const found = [];
8908
9024
  for (const entry of entries) {
8909
9025
  const path = relative === "" ? entry.name : `${relative}/${entry.name}`;
@@ -8930,7 +9046,7 @@ function importsOfProject(projectDirectory, wanted) {
8930
9046
  const tested = /* @__PURE__ */ new Set();
8931
9047
  const files = sourceFiles(projectDirectory);
8932
9048
  for (const file of files) {
8933
- const names = wantedImportsOf((0, import_node_path38.join)(projectDirectory, file), wanted);
9049
+ const names = wantedImportsOf((0, import_node_path39.join)(projectDirectory, file), wanted);
8934
9050
  const target = TEST_FILE.test(file) ? tested : shipped;
8935
9051
  for (const name of names) {
8936
9052
  target.add(name);
@@ -8942,7 +9058,7 @@ function importsOfProject(projectDirectory, wanted) {
8942
9058
  function findUsage(repositoryRoot, projectDirectories, wanted) {
8943
9059
  const usage = {};
8944
9060
  for (const dir of projectDirectories) {
8945
- const { shipped, testsOnly } = importsOfProject((0, import_node_path38.join)(repositoryRoot, dir), wanted);
9061
+ const { shipped, testsOnly } = importsOfProject((0, import_node_path39.join)(repositoryRoot, dir), wanted);
8946
9062
  for (const name of shipped) {
8947
9063
  usage[name] ??= { runtime: [], testsOnly: [] };
8948
9064
  usage[name].runtime.push(dir);
@@ -8957,7 +9073,7 @@ function findUsage(repositoryRoot, projectDirectories, wanted) {
8957
9073
  function adoptDependencies(repositoryRoot, dependencies = {}) {
8958
9074
  const run = dependencies.run ?? runShell;
8959
9075
  requireCleanWorkingTree(repositoryRoot, dependencies.capture ?? runCapture);
8960
- const rootPath = (0, import_node_path38.join)(repositoryRoot, "package.json");
9076
+ const rootPath = (0, import_node_path39.join)(repositoryRoot, "package.json");
8961
9077
  if (!fileExists(rootPath)) {
8962
9078
  throw new Error("No package.json at the repository root.");
8963
9079
  }
@@ -8993,7 +9109,7 @@ function adoptDependencies(repositoryRoot, dependencies = {}) {
8993
9109
 
8994
9110
  // src/repository-adoption/adopt-kinds.use-case.ts
8995
9111
  var import_node_fs34 = require("fs");
8996
- var import_node_path39 = require("path");
9112
+ var import_node_path40 = require("path");
8997
9113
 
8998
9114
  // src/repository-adoption/infer-project-kind.algorithm.ts
8999
9115
  var SERVER_FRAMEWORKS = ["express", "koa", "fastify", "@nestjs/core"];
@@ -9094,32 +9210,32 @@ function inferProjectKind(evidence) {
9094
9210
  // src/repository-adoption/adopt-kinds.use-case.ts
9095
9211
  var TYPE_TAG = "type:";
9096
9212
  function readEvidence(repositoryRoot, location) {
9097
- const directory = (0, import_node_path39.join)(repositoryRoot, location.dir);
9213
+ const directory = (0, import_node_path40.join)(repositoryRoot, location.dir);
9098
9214
  const files = (0, import_node_fs34.readdirSync)(directory);
9099
9215
  const manifest = location.ecosystem === "npm" ? readJson(location.manifestPath) : {};
9100
9216
  const text = location.ecosystem === "npm" ? "" : (0, import_node_fs34.readFileSync)(location.manifestPath, "utf8");
9101
9217
  let hasProgram = false;
9102
9218
  if (location.ecosystem === "go") {
9103
- hasProgram = files.filter((file) => file.endsWith(".go")).some((file) => /^package main\b/m.test((0, import_node_fs34.readFileSync)((0, import_node_path39.join)(directory, file), "utf8")));
9219
+ hasProgram = files.filter((file) => file.endsWith(".go")).some((file) => /^package main\b/m.test((0, import_node_fs34.readFileSync)((0, import_node_path40.join)(directory, file), "utf8")));
9104
9220
  } else if (location.ecosystem === "pub") {
9105
- hasProgram = (0, import_node_fs34.existsSync)((0, import_node_path39.join)(directory, "lib", "main.dart"));
9221
+ hasProgram = (0, import_node_fs34.existsSync)((0, import_node_path40.join)(directory, "lib", "main.dart"));
9106
9222
  }
9107
9223
  return { ecosystem: location.ecosystem, manifest, text, files, hasProgram };
9108
9224
  }
9109
9225
  function existingKind(directory) {
9110
9226
  const sources = [];
9111
- if (fileExists((0, import_node_path39.join)(directory, "project.json"))) {
9112
- sources.push(readJson((0, import_node_path39.join)(directory, "project.json")).tags ?? []);
9227
+ if (fileExists((0, import_node_path40.join)(directory, "project.json"))) {
9228
+ sources.push(readJson((0, import_node_path40.join)(directory, "project.json")).tags ?? []);
9113
9229
  }
9114
- if (fileExists((0, import_node_path39.join)(directory, "package.json"))) {
9115
- sources.push(readJson((0, import_node_path39.join)(directory, "package.json")).nx?.tags ?? []);
9230
+ if (fileExists((0, import_node_path40.join)(directory, "package.json"))) {
9231
+ sources.push(readJson((0, import_node_path40.join)(directory, "package.json")).nx?.tags ?? []);
9116
9232
  }
9117
9233
  return sources.flat().find((tag) => tag.startsWith(TYPE_TAG))?.slice(TYPE_TAG.length);
9118
9234
  }
9119
9235
  function writeKindTag(directory, name, kind) {
9120
9236
  const tag = `${TYPE_TAG}${kind}`;
9121
- const projectJson = (0, import_node_path39.join)(directory, "project.json");
9122
- const packageJson = (0, import_node_path39.join)(directory, "package.json");
9237
+ const projectJson = (0, import_node_path40.join)(directory, "project.json");
9238
+ const packageJson = (0, import_node_path40.join)(directory, "package.json");
9123
9239
  if (fileExists(projectJson)) {
9124
9240
  const project = readJson(projectJson);
9125
9241
  writeFileEnsured(projectJson, toJson({ ...project, tags: [...project.tags ?? [], tag] }));
@@ -9143,7 +9259,7 @@ function adoptKinds(repositoryRoot, overrides = {}, dependencies = {}) {
9143
9259
  }
9144
9260
  }
9145
9261
  return locations.map((location) => {
9146
- const directory = (0, import_node_path39.join)(repositoryRoot, location.dir);
9262
+ const directory = (0, import_node_path40.join)(repositoryRoot, location.dir);
9147
9263
  const present = existingKind(directory);
9148
9264
  if (present !== void 0) {
9149
9265
  return { dir: location.dir, outcome: "already", kind: present, reason: PROJECT_KINDS.includes(present) ? "it already has a type tag" : "it already has a type tag of its own, which is not an mnci kind name; left as it is" };
@@ -9160,11 +9276,11 @@ function adoptKinds(repositoryRoot, overrides = {}, dependencies = {}) {
9160
9276
 
9161
9277
  // src/repository-adoption/adopt-overlay.use-case.ts
9162
9278
  var import_node_fs36 = require("fs");
9163
- var import_node_path41 = require("path");
9279
+ var import_node_path42 = require("path");
9164
9280
 
9165
9281
  // src/workspace-upgrade/upgrade-workspace.use-case.ts
9166
9282
  var import_node_fs35 = require("fs");
9167
- var import_node_path40 = require("path");
9283
+ var import_node_path41 = require("path");
9168
9284
  function resolveRegistry(options, persisted) {
9169
9285
  if (options.registry === "azure-artifacts" || options.organization && options.artifactsFeed) {
9170
9286
  const persistedAzure = persisted?.kind === "azure-artifacts" ? persisted : void 0;
@@ -9201,11 +9317,11 @@ function resolveWorkspaceName(workspaceRoot, persisted) {
9201
9317
  }
9202
9318
  } catch {
9203
9319
  }
9204
- return (0, import_node_path40.basename)(workspaceRoot);
9320
+ return (0, import_node_path41.basename)(workspaceRoot);
9205
9321
  }
9206
9322
  function detectNpmAuth(workspaceRoot) {
9207
9323
  try {
9208
- return (0, import_node_fs35.readFileSync)((0, import_node_path40.join)(workspaceRoot, "azure-pipelines.yml"), "utf8").includes("npmAuthenticate@0") ? "build-identity" : void 0;
9324
+ return (0, import_node_fs35.readFileSync)((0, import_node_path41.join)(workspaceRoot, "azure-pipelines.yml"), "utf8").includes("npmAuthenticate@0") ? "build-identity" : void 0;
9209
9325
  } catch {
9210
9326
  return void 0;
9211
9327
  }
@@ -9252,7 +9368,7 @@ function resolveOverlayOptions(workspaceRoot, options, persisted) {
9252
9368
  };
9253
9369
  }
9254
9370
  function runUpgrade(workspaceRoot, options) {
9255
- if (!fileExists((0, import_node_path40.join)(workspaceRoot, "nx.json"))) {
9371
+ if (!fileExists((0, import_node_path41.join)(workspaceRoot, "nx.json"))) {
9256
9372
  throw new Error(
9257
9373
  `No nx.json found in ${workspaceRoot} \u2014 this does not look like an Nx workspace. Run 'mnci upgrade' from the workspace root.`
9258
9374
  );
@@ -9330,7 +9446,7 @@ function runUpgrade(workspaceRoot, options) {
9330
9446
  "Formatting the workspace (eslint --fix) \u2014 the slowest step, minutes on a large workspace"
9331
9447
  );
9332
9448
  runFormatter(workspaceRoot);
9333
- const nxJson = readJson((0, import_node_path40.join)(workspaceRoot, "nx.json"));
9449
+ const nxJson = readJson((0, import_node_path41.join)(workspaceRoot, "nx.json"));
9334
9450
  const stranded = locateStrandedReleaseTags(workspaceRoot, nxJson.release?.releaseTag?.pattern);
9335
9451
  for (const entry of stranded) {
9336
9452
  logger.warn(`${entry.project} has release tags only under ${entry.oldTag}: nx release would restart it from its disk version. Run \`mnci adopt --tags\` before the next release to create ${entry.newTag} locally (\`mnci doctor\` lists them all).`);
@@ -9341,8 +9457,8 @@ function runUpgrade(workspaceRoot, options) {
9341
9457
 
9342
9458
  // src/repository-adoption/adopt-overlay.use-case.ts
9343
9459
  function detectCiProvider(repositoryRoot) {
9344
- const azure = (0, import_node_fs36.existsSync)((0, import_node_path41.join)(repositoryRoot, "azure-pipelines.yml"));
9345
- const workflows = (0, import_node_path41.join)(repositoryRoot, ".github", "workflows");
9460
+ const azure = (0, import_node_fs36.existsSync)((0, import_node_path42.join)(repositoryRoot, "azure-pipelines.yml"));
9461
+ const workflows = (0, import_node_path42.join)(repositoryRoot, ".github", "workflows");
9346
9462
  const github = (0, import_node_fs36.existsSync)(workflows) && (0, import_node_fs36.readdirSync)(workflows).some((file) => /\.ya?ml$/.test(file));
9347
9463
  if (azure && github) {
9348
9464
  return "both";
@@ -9353,7 +9469,7 @@ function detectCiProvider(repositoryRoot) {
9353
9469
  return github ? "github" : void 0;
9354
9470
  }
9355
9471
  function detectTestRunner(repositoryRoot) {
9356
- const manifestPath = (0, import_node_path41.join)(repositoryRoot, "package.json");
9472
+ const manifestPath = (0, import_node_path42.join)(repositoryRoot, "package.json");
9357
9473
  if (!fileExists(manifestPath)) {
9358
9474
  return void 0;
9359
9475
  }
@@ -9366,7 +9482,7 @@ function detectTestRunner(repositoryRoot) {
9366
9482
  }
9367
9483
  function adoptOverlay(repositoryRoot, options, dependencies = {}) {
9368
9484
  requireCleanWorkingTree(repositoryRoot, dependencies.capture ?? runCapture);
9369
- if (!fileExists((0, import_node_path41.join)(repositoryRoot, "nx.json"))) {
9485
+ if (!fileExists((0, import_node_path42.join)(repositoryRoot, "nx.json"))) {
9370
9486
  throw new Error("There is no nx.json, so Nx is not set up here. Set it up first (`npx nx@latest init`); adopt does not install Nx yet.");
9371
9487
  }
9372
9488
  const persisted = readMnciConfig(repositoryRoot);
@@ -9379,7 +9495,7 @@ function adoptOverlay(repositoryRoot, options, dependencies = {}) {
9379
9495
  }
9380
9496
 
9381
9497
  // src/repository-adoption/adopt-toolchain.use-case.ts
9382
- var import_node_path43 = require("path");
9498
+ var import_node_path44 = require("path");
9383
9499
 
9384
9500
  // src/repository-adoption/align-nx-family.algorithm.ts
9385
9501
  var VERSION = /^[\^~]?(\d+)\.(\d+)\.(\d+)$/;
@@ -9418,23 +9534,23 @@ function alignNxFamily(dependencies, version) {
9418
9534
 
9419
9535
  // src/repository-adoption/retire-tooling.use-case.ts
9420
9536
  var import_node_fs37 = require("fs");
9421
- var import_node_path42 = require("path");
9537
+ var import_node_path43 = require("path");
9422
9538
  var RETIRED_COMMAND = /\b(?:prettier|oxfmt|oxlint)\b/;
9423
9539
  function retireTooling(repositoryRoot) {
9424
9540
  const removed = [];
9425
9541
  for (const file of RETIRED_FORMATTER_FILES) {
9426
- if (!fileExists((0, import_node_path42.join)(repositoryRoot, file))) {
9542
+ if (!fileExists((0, import_node_path43.join)(repositoryRoot, file))) {
9427
9543
  continue;
9428
9544
  }
9429
- (0, import_node_fs37.rmSync)((0, import_node_path42.join)(repositoryRoot, file), { force: true });
9545
+ (0, import_node_fs37.rmSync)((0, import_node_path43.join)(repositoryRoot, file), { force: true });
9430
9546
  removed.push(file);
9431
9547
  }
9432
- const hadRegistry = fileExists((0, import_node_path42.join)(repositoryRoot, ".verdaccio"));
9548
+ const hadRegistry = fileExists((0, import_node_path43.join)(repositoryRoot, ".verdaccio"));
9433
9549
  removeLocalRegistryScaffolding(repositoryRoot);
9434
9550
  if (hadRegistry) {
9435
9551
  removed.push(".verdaccio, its dependency and the local-registry target");
9436
9552
  }
9437
- const manifestPath = (0, import_node_path42.join)(repositoryRoot, "package.json");
9553
+ const manifestPath = (0, import_node_path43.join)(repositoryRoot, "package.json");
9438
9554
  if (!fileExists(manifestPath)) {
9439
9555
  return removed;
9440
9556
  }
@@ -9481,7 +9597,7 @@ function adoptToolchain(repositoryRoot, options = {}, dependencies = {}) {
9481
9597
  console.log(message);
9482
9598
  });
9483
9599
  requireCleanWorkingTree(repositoryRoot, capture);
9484
- const manifestPath = (0, import_node_path43.join)(repositoryRoot, "package.json");
9600
+ const manifestPath = (0, import_node_path44.join)(repositoryRoot, "package.json");
9485
9601
  if (!fileExists(manifestPath)) {
9486
9602
  throw new Error("No package.json at the repository root.");
9487
9603
  }
@@ -9532,7 +9648,7 @@ function createBaselineTags(repositoryRoot, stranded) {
9532
9648
 
9533
9649
  // src/repository-adoption/inspect-repository.use-case.ts
9534
9650
  var import_node_fs38 = require("fs");
9535
- var import_node_path44 = require("path");
9651
+ var import_node_path45 = require("path");
9536
9652
  var LOCKFILES = [
9537
9653
  ["package-lock.json", "npm"],
9538
9654
  ["pnpm-lock.yaml", "pnpm"],
@@ -9553,22 +9669,22 @@ function inspectRepository(repositoryRoot) {
9553
9669
  const isGitRepo = inside.status === 0 && inside.stdout.trim() === "true";
9554
9670
  const dirty = isGitRepo && runCapture("git", ["status", "--porcelain"], repositoryRoot).stdout.trim() !== "";
9555
9671
  const tags = isGitRepo ? runCapture("git", ["tag", "--list"], repositoryRoot).stdout.split(/\r?\n/).filter(Boolean) : [];
9556
- const manifestPath = (0, import_node_path44.join)(repositoryRoot, "package.json");
9672
+ const manifestPath = (0, import_node_path45.join)(repositoryRoot, "package.json");
9557
9673
  const manifest = fileExists(manifestPath) ? readJson(manifestPath) : {};
9558
- const nxJsonPath = (0, import_node_path44.join)(repositoryRoot, "nx.json");
9674
+ const nxJsonPath = (0, import_node_path45.join)(repositoryRoot, "nx.json");
9559
9675
  const nxJson = fileExists(nxJsonPath) ? readJson(nxJsonPath) : {};
9560
9676
  const ci = [];
9561
9677
  const pipelines = [];
9562
- if ((0, import_node_fs38.existsSync)((0, import_node_path44.join)(repositoryRoot, "azure-pipelines.yml"))) {
9678
+ if ((0, import_node_fs38.existsSync)((0, import_node_path45.join)(repositoryRoot, "azure-pipelines.yml"))) {
9563
9679
  ci.push("azure");
9564
- pipelines.push((0, import_node_path44.join)(repositoryRoot, "azure-pipelines.yml"));
9680
+ pipelines.push((0, import_node_path45.join)(repositoryRoot, "azure-pipelines.yml"));
9565
9681
  }
9566
- const workflows = (0, import_node_path44.join)(repositoryRoot, ".github", "workflows");
9682
+ const workflows = (0, import_node_path45.join)(repositoryRoot, ".github", "workflows");
9567
9683
  if ((0, import_node_fs38.existsSync)(workflows)) {
9568
9684
  const files = (0, import_node_fs38.readdirSync)(workflows).filter((file) => /\.ya?ml$/.test(file));
9569
9685
  if (files.length > 0) {
9570
9686
  ci.push("github");
9571
- pipelines.push(...files.map((file) => (0, import_node_path44.join)(workflows, file)));
9687
+ pipelines.push(...files.map((file) => (0, import_node_path45.join)(workflows, file)));
9572
9688
  }
9573
9689
  }
9574
9690
  const rootFiles = (0, import_node_fs38.readdirSync)(repositoryRoot);
@@ -9815,6 +9931,7 @@ var COMMAND_GROUPS = {
9815
9931
  doctor: "workspace",
9816
9932
  adopt: "inspect",
9817
9933
  add: "projects",
9934
+ dev: "projects",
9818
9935
  install: "dependencies",
9819
9936
  sync: "dependencies",
9820
9937
  up: "dependencies",
@@ -9879,13 +9996,13 @@ function runCommands(program, options) {
9879
9996
  }
9880
9997
 
9881
9998
  // src/workspace-info/list-projects.use-case.ts
9882
- var import_node_path45 = require("path");
9999
+ var import_node_path46 = require("path");
9883
10000
  var TYPE_TAG_PREFIX = "type:";
9884
10001
  function listProjects(workspaceRoot) {
9885
10002
  return locateProjects(workspaceRoot).map((location) => {
9886
- const projectJsonPath = (0, import_node_path45.join)(workspaceRoot, location.dir, "project.json");
10003
+ const projectJsonPath = (0, import_node_path46.join)(workspaceRoot, location.dir, "project.json");
9887
10004
  const project = fileExists(projectJsonPath) ? readJson(projectJsonPath) : {};
9888
- const manifestPath = (0, import_node_path45.join)(workspaceRoot, location.dir, "package.json");
10005
+ const manifestPath = (0, import_node_path46.join)(workspaceRoot, location.dir, "package.json");
9889
10006
  const manifestTags = fileExists(manifestPath) ? readJson(manifestPath).nx?.tags ?? [] : [];
9890
10007
  const typeTag = [...project.tags ?? [], ...manifestTags].find((tag) => tag.startsWith(TYPE_TAG_PREFIX));
9891
10008
  return {
@@ -9898,7 +10015,7 @@ function listProjects(workspaceRoot) {
9898
10015
  }).toSorted((a, b) => a.dir.localeCompare(b.dir));
9899
10016
  }
9900
10017
  function runProjects(workspaceRoot, options) {
9901
- if (!fileExists((0, import_node_path45.join)(workspaceRoot, "nx.json"))) {
10018
+ if (!fileExists((0, import_node_path46.join)(workspaceRoot, "nx.json"))) {
9902
10019
  throw new Error("No nx.json found here. Run `projects` from the workspace root.");
9903
10020
  }
9904
10021
  const projects = listProjects(workspaceRoot);
@@ -9912,12 +10029,12 @@ function runProjects(workspaceRoot, options) {
9912
10029
  }
9913
10030
 
9914
10031
  // src/workspace-info/read-workspace-info.use-case.ts
9915
- var import_node_path47 = require("path");
10032
+ var import_node_path48 = require("path");
9916
10033
 
9917
10034
  // src/cli-version/check-for-update.use-case.ts
9918
10035
  var import_node_fs39 = require("fs");
9919
- var import_node_path46 = require("path");
9920
- var import_cross_spawn3 = __toESM(require("cross-spawn"));
10036
+ var import_node_path47 = require("path");
10037
+ var import_cross_spawn4 = __toESM(require("cross-spawn"));
9921
10038
  var PACKAGE_NAME2 = "@mnci/cli";
9922
10039
  var REGISTRY_TIMEOUT_MS = 2e3;
9923
10040
  function checkForUpdate(currentVersion2) {
@@ -9935,7 +10052,7 @@ function checkForUpdate(currentVersion2) {
9935
10052
  }
9936
10053
  }
9937
10054
  function fetchLatestVersion() {
9938
- const result = import_cross_spawn3.default.sync("npm", ["view", PACKAGE_NAME2, "version"], {
10055
+ const result = import_cross_spawn4.default.sync("npm", ["view", PACKAGE_NAME2, "version"], {
9939
10056
  encoding: "utf8",
9940
10057
  timeout: REGISTRY_TIMEOUT_MS,
9941
10058
  stdio: ["ignore", "pipe", "ignore"]
@@ -9975,7 +10092,7 @@ function releaseTriple(version) {
9975
10092
  }
9976
10093
  function readCliVersion(packageDirectory) {
9977
10094
  try {
9978
- const manifestPath = (0, import_node_path46.join)(packageDirectory, "..", "package.json");
10095
+ const manifestPath = (0, import_node_path47.join)(packageDirectory, "..", "package.json");
9979
10096
  const manifest = JSON.parse((0, import_node_fs39.readFileSync)(manifestPath, "utf8"));
9980
10097
  return manifest.version ?? "0.0.0";
9981
10098
  } catch {
@@ -9986,7 +10103,7 @@ function readCliVersion(packageDirectory) {
9986
10103
  // src/workspace-info/read-workspace-info.use-case.ts
9987
10104
  function readWorkspaceInfo(workspaceRoot, cliVersion) {
9988
10105
  const latest = fetchLatestVersion() ?? null;
9989
- const isWorkspace = fileExists((0, import_node_path47.join)(workspaceRoot, "nx.json"));
10106
+ const isWorkspace = fileExists((0, import_node_path48.join)(workspaceRoot, "nx.json"));
9990
10107
  return {
9991
10108
  cli: {
9992
10109
  version: cliVersion,
@@ -10007,7 +10124,7 @@ function runInfo(workspaceRoot, cliVersion, options) {
10007
10124
  }
10008
10125
 
10009
10126
  // src/interactive-wizard/interactive-wizard.use-case.ts
10010
- var import_node_path48 = require("path");
10127
+ var import_node_path49 = require("path");
10011
10128
 
10012
10129
  // src/interactive-wizard/adopt-steps.config.ts
10013
10130
  var ADOPT_GLOBAL_OPTIONS = ["json"];
@@ -10143,14 +10260,14 @@ function formatCommandLine(argv) {
10143
10260
  }
10144
10261
 
10145
10262
  // src/interactive-wizard/inquirer-prompter.client.ts
10146
- var import_prompts4 = require("@inquirer/prompts");
10263
+ var import_prompts5 = require("@inquirer/prompts");
10147
10264
  function toRows(choices) {
10148
10265
  const rows = [];
10149
10266
  let current;
10150
10267
  for (const choice of choices) {
10151
10268
  if (choice.group !== void 0 && choice.group !== current) {
10152
10269
  current = choice.group;
10153
- rows.push(new import_prompts4.Separator(`-- ${choice.group} --`));
10270
+ rows.push(new import_prompts5.Separator(`-- ${choice.group} --`));
10154
10271
  }
10155
10272
  rows.push({ name: choice.name, value: choice.value, description: choice.description });
10156
10273
  }
@@ -10158,10 +10275,10 @@ function toRows(choices) {
10158
10275
  }
10159
10276
  function createInquirerPrompter() {
10160
10277
  return {
10161
- select: async (message, choices) => await (0, import_prompts4.select)({ message, choices: toRows(choices), pageSize: 18 }),
10162
- checkbox: async (message, choices) => await (0, import_prompts4.checkbox)({ message, choices: toRows(choices), pageSize: 18 }),
10163
- input: async (message, required) => await (0, import_prompts4.input)({ message, validate: (value) => !required || value.trim().length > 0 || "A value is required" }),
10164
- confirm: async (message, fallback) => await (0, import_prompts4.confirm)({ message, default: fallback })
10278
+ select: async (message, choices) => await (0, import_prompts5.select)({ message, choices: toRows(choices), pageSize: 18 }),
10279
+ checkbox: async (message, choices) => await (0, import_prompts5.checkbox)({ message, choices: toRows(choices), pageSize: 18 }),
10280
+ input: async (message, required) => await (0, import_prompts5.input)({ message, validate: (value) => !required || value.trim().length > 0 || "A value is required" }),
10281
+ confirm: async (message, fallback) => await (0, import_prompts5.confirm)({ message, default: fallback })
10165
10282
  };
10166
10283
  }
10167
10284
 
@@ -10188,7 +10305,7 @@ function wizardMenu(commands, inWorkspace) {
10188
10305
  }
10189
10306
  async function runInteractive(program, prompter = createInquirerPrompter(), workingDirectory = process.cwd()) {
10190
10307
  const commands = describeCommands(program);
10191
- const inWorkspace = fileExists((0, import_node_path48.join)(workingDirectory, "nx.json"));
10308
+ const inWorkspace = fileExists((0, import_node_path49.join)(workingDirectory, "nx.json"));
10192
10309
  const name = await prompter.select("What would you like to do?", wizardMenu(commands, inWorkspace));
10193
10310
  const command = commands.find((candidate) => candidate.name === name);
10194
10311
  const argv = buildArgv(command, await askCommand(command, prompter));
@@ -10202,7 +10319,7 @@ async function runInteractive(program, prompter = createInquirerPrompter(), work
10202
10319
 
10203
10320
  // src/workspace-creation/adopt-directory.use-case.ts
10204
10321
  var import_node_fs40 = require("fs");
10205
- var import_node_path49 = require("path");
10322
+ var import_node_path50 = require("path");
10206
10323
  var NEVER_COPIED = /* @__PURE__ */ new Set([".git", "node_modules"]);
10207
10324
  var AGENT_AND_CI_ENTRIES = [
10208
10325
  "AGENTS.md",
@@ -10239,7 +10356,7 @@ function assertAdoptableDirectory(target) {
10239
10356
  if (!(0, import_node_fs40.statSync)(target).isDirectory()) {
10240
10357
  throw new Error(`--into '${target}' is not a directory.`);
10241
10358
  }
10242
- const blockers = PREFLIGHT_BLOCKERS.filter((entry) => (0, import_node_fs40.existsSync)((0, import_node_path49.join)(target, entry)));
10359
+ const blockers = PREFLIGHT_BLOCKERS.filter((entry) => (0, import_node_fs40.existsSync)((0, import_node_path50.join)(target, entry)));
10243
10360
  if (blockers.length > 0) {
10244
10361
  throw new Error(
10245
10362
  `--into '${target}' already holds ${blockers.join(", ")}, so it is a project already. Use \`mnci upgrade\` to re-apply the overlay to an existing workspace, or point --into at an empty checkout.`
@@ -10267,7 +10384,7 @@ ${additions.join("\n")}
10267
10384
  function adoptGeneratedWorkspace(generatedRoot, target) {
10268
10385
  const entries = (0, import_node_fs40.readdirSync)(generatedRoot).filter((entry) => !NEVER_COPIED.has(entry));
10269
10386
  const collisions = entries.filter(
10270
- (entry) => (0, import_node_fs40.existsSync)((0, import_node_path49.join)(target, entry)) && !EXISTING_WINS.has(entry) && entry !== ".gitignore"
10387
+ (entry) => (0, import_node_fs40.existsSync)((0, import_node_path50.join)(target, entry)) && !EXISTING_WINS.has(entry) && entry !== ".gitignore"
10271
10388
  );
10272
10389
  if (collisions.length > 0) {
10273
10390
  throw new Error(
@@ -10276,8 +10393,8 @@ function adoptGeneratedWorkspace(generatedRoot, target) {
10276
10393
  }
10277
10394
  const result = { copied: [], kept: [], mergedGitignore: false };
10278
10395
  for (const entry of entries) {
10279
- const from = (0, import_node_path49.join)(generatedRoot, entry);
10280
- const to = (0, import_node_path49.join)(target, entry);
10396
+ const from = (0, import_node_path50.join)(generatedRoot, entry);
10397
+ const to = (0, import_node_path50.join)(target, entry);
10281
10398
  if (entry === ".gitignore" && (0, import_node_fs40.existsSync)(to)) {
10282
10399
  result.mergedGitignore = mergeGitignore(from, to);
10283
10400
  result.kept.push(entry);
@@ -10297,7 +10414,7 @@ function adoptGeneratedWorkspace(generatedRoot, target) {
10297
10414
  // src/workspace-creation/create-workspace.use-case.ts
10298
10415
  var import_node_fs41 = require("fs");
10299
10416
  var import_node_os2 = require("os");
10300
- var import_node_path50 = require("path");
10417
+ var import_node_path51 = require("path");
10301
10418
  function nxCloudProviderValue(ci) {
10302
10419
  return ci === "azure" ? "azure" : "github";
10303
10420
  }
@@ -10340,9 +10457,9 @@ async function resolveRegistry2(options) {
10340
10457
  return await promptRegistry();
10341
10458
  }
10342
10459
  async function runNew(name, options) {
10343
- const adoptTarget = options.into === void 0 ? void 0 : (0, import_node_path50.resolve)(process.cwd(), options.into);
10460
+ const adoptTarget = options.into === void 0 ? void 0 : (0, import_node_path51.resolve)(process.cwd(), options.into);
10344
10461
  if (adoptTarget !== void 0) assertAdoptableDirectory(adoptTarget);
10345
- const workspaceName = name ?? (adoptTarget === void 0 ? await promptText("Workspace name") : (0, import_node_path50.basename)(adoptTarget));
10462
+ const workspaceName = name ?? (adoptTarget === void 0 ? await promptText("Workspace name") : (0, import_node_path51.basename)(adoptTarget));
10346
10463
  assertValidProjectName(workspaceName, "Workspace name");
10347
10464
  const scope = options.scope ?? (options.yes ? `@${workspaceName}` : await promptText("npm scope for publishable packages", `@${workspaceName}`));
10348
10465
  const registry = await resolveRegistry2(options);
@@ -10355,8 +10472,8 @@ async function runNew(name, options) {
10355
10472
  const npmAuth = resolveNpmAuth(options.npmAuth, registry, ci);
10356
10473
  const stack = await resolveStack(options);
10357
10474
  const nxCloud = options.nxCloud ?? (options.yes ? false : await promptNxCloud());
10358
- const stagingParent = adoptTarget === void 0 ? process.cwd() : (0, import_node_fs41.mkdtempSync)((0, import_node_path50.join)((0, import_node_os2.tmpdir)(), "mnci-new-"));
10359
- let workspaceRoot = (0, import_node_path50.join)(stagingParent, workspaceName);
10475
+ const stagingParent = adoptTarget === void 0 ? process.cwd() : (0, import_node_fs41.mkdtempSync)((0, import_node_path51.join)((0, import_node_os2.tmpdir)(), "mnci-new-"));
10476
+ let workspaceRoot = (0, import_node_path51.join)(stagingParent, workspaceName);
10360
10477
  try {
10361
10478
  logger.step(`Creating Nx workspace '${workspaceName}' (preset: ts)`);
10362
10479
  runNpx(
@@ -10389,8 +10506,8 @@ async function runNew(name, options) {
10389
10506
  { workspaceName, scope, registry, agent, variableGroup, ci, stack, ...npmAuth && { npmAuth } },
10390
10507
  logger.detail
10391
10508
  );
10392
- (0, import_node_fs41.rmSync)((0, import_node_path50.join)(workspaceRoot, "node_modules"), { recursive: true, force: true });
10393
- (0, import_node_fs41.rmSync)((0, import_node_path50.join)(workspaceRoot, "package-lock.json"), { force: true });
10509
+ (0, import_node_fs41.rmSync)((0, import_node_path51.join)(workspaceRoot, "node_modules"), { recursive: true, force: true });
10510
+ (0, import_node_fs41.rmSync)((0, import_node_path51.join)(workspaceRoot, "package-lock.json"), { force: true });
10394
10511
  logger.step("Installing the toolchain (commitlint + husky + linting/formatting)");
10395
10512
  const installStatus = runShell(
10396
10513
  "npm",
@@ -10454,6 +10571,9 @@ function buildProgram(cliVersion) {
10454
10571
  ).action((options) => {
10455
10572
  runUpgrade(process.cwd(), options);
10456
10573
  });
10574
+ program.command("dev").argument("[projects...]", "the projects to start (those with a start command)").description("Start several projects together, such as a frontend and the API it calls: runs their start targets as one Nx command, so Ctrl+C stops them all. Name the projects, pass --all, or pick from a list").option("--all", "start every project that has a start command").option("--dry-run", "print the Nx command and start nothing").action(async (projects, options) => {
10575
+ await runDev(process.cwd(), projects, options);
10576
+ });
10457
10577
  program.command("doctor").description(
10458
10578
  "Check this workspace against the invariants mnci maintains (one ESLint config, no stray .prettierrc, the eslint plugin registered, the resolved eslint major, .npmrc vs the recorded registry, versionActions overrides, nx sync) \u2014 read-only; exits non-zero if anything failed"
10459
10579
  ).option("--json", "print the findings as one JSON document, each with its remedy").action((options) => {