@mnci/cli 4.46.0 → 4.48.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
@@ -8485,7 +8485,7 @@ function runDoctor(workspaceRoot, options = {}) {
8485
8485
  process.exitCode = 1;
8486
8486
  }
8487
8487
 
8488
- // src/repository-adoption/adopt-kinds.use-case.ts
8488
+ // src/repository-adoption/adopt-dependencies.use-case.ts
8489
8489
  var import_node_fs31 = require("fs");
8490
8490
  var import_node_path36 = require("path");
8491
8491
 
@@ -8499,6 +8499,157 @@ function requireCleanWorkingTree(repositoryRoot, capture = runCapture) {
8499
8499
  }
8500
8500
  }
8501
8501
 
8502
+ // src/repository-adoption/extract-imports.algorithm.ts
8503
+ var import_node_module = require("module");
8504
+ var SPECIFIER2 = /(?:\bfrom\s*|\bimport\s*(?:\(\s*)?|\brequire\s*\(\s*)(['"`])([^'"`\n]+)\1/g;
8505
+ var BUILTINS = new Set(import_node_module.builtinModules);
8506
+ function packageOfSpecifier(specifier) {
8507
+ if (/^(?:[./#~]|[a-z]+:)/i.test(specifier)) {
8508
+ return void 0;
8509
+ }
8510
+ const parts = specifier.split("/");
8511
+ const name = specifier.startsWith("@") ? parts.slice(0, 2).join("/") : parts[0];
8512
+ if (name === "" || BUILTINS.has(name) || specifier.startsWith("@") && parts.length < 2) {
8513
+ return void 0;
8514
+ }
8515
+ return name;
8516
+ }
8517
+ function extractImportedPackages(source) {
8518
+ const found = /* @__PURE__ */ new Set();
8519
+ for (const match of source.matchAll(SPECIFIER2)) {
8520
+ const name = packageOfSpecifier(match[2]);
8521
+ if (name !== void 0) {
8522
+ found.add(name);
8523
+ }
8524
+ }
8525
+ return found;
8526
+ }
8527
+
8528
+ // src/repository-adoption/plan-dependency-moves.algorithm.ts
8529
+ function planDependencyMoves(rootDependencies, usage, declared) {
8530
+ const plan = { moves: [], removed: [], keptAtRoot: [], conflicts: [] };
8531
+ for (const [name, range] of Object.entries(rootDependencies)) {
8532
+ const where = usage[name];
8533
+ const runtime = where?.runtime ?? [];
8534
+ const testsOnly = where?.testsOnly ?? [];
8535
+ if (runtime.length === 0 && testsOnly.length === 0) {
8536
+ plan.keptAtRoot.push({ name, reason: "no project imports it: it may be tooling the root uses, or unused" });
8537
+ continue;
8538
+ }
8539
+ const targets = [
8540
+ ...runtime.map((dir) => [dir, "dependencies"]),
8541
+ ...testsOnly.map((dir) => [dir, "devDependencies"])
8542
+ ];
8543
+ for (const [dir, field] of targets) {
8544
+ const existing = declared[dir]?.[name];
8545
+ if (existing === void 0) {
8546
+ plan.moves.push({ name, range, dir, field });
8547
+ } else if (existing !== range) {
8548
+ plan.conflicts.push({ name, dir, projectRange: existing, rootRange: range });
8549
+ }
8550
+ }
8551
+ plan.removed.push(name);
8552
+ }
8553
+ return plan;
8554
+ }
8555
+
8556
+ // src/repository-adoption/adopt-dependencies.use-case.ts
8557
+ var SOURCE_FILE = /\.(?:[cm]?[jt]s|[jt]sx)$/;
8558
+ var TEST_FILE = /\.(?:spec|test)\.[^.]+$|[\\/](?:__tests__|tests?|e2e)[\\/]/;
8559
+ var SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set(["node_modules", "dist", "build", "coverage", "out", ".git", ".nx", "tmp"]);
8560
+ function sourceFiles(directory, relative = "") {
8561
+ const entries = (0, import_node_fs31.readdirSync)((0, import_node_path36.join)(directory, relative), { withFileTypes: true });
8562
+ const found = [];
8563
+ for (const entry of entries) {
8564
+ const path = relative === "" ? entry.name : `${relative}/${entry.name}`;
8565
+ if (entry.isDirectory()) {
8566
+ if (!SKIPPED_DIRECTORIES.has(entry.name)) {
8567
+ found.push(...sourceFiles(directory, path));
8568
+ }
8569
+ } else if (SOURCE_FILE.test(entry.name)) {
8570
+ found.push(path);
8571
+ }
8572
+ }
8573
+ return found;
8574
+ }
8575
+ function wantedImportsOf(path, wanted) {
8576
+ try {
8577
+ const imported = [...extractImportedPackages((0, import_node_fs31.readFileSync)(path, "utf8"))];
8578
+ return imported.filter((name) => wanted.has(name));
8579
+ } catch {
8580
+ return [];
8581
+ }
8582
+ }
8583
+ function importsOfProject(projectDirectory, wanted) {
8584
+ const shipped = /* @__PURE__ */ new Set();
8585
+ const tested = /* @__PURE__ */ new Set();
8586
+ const files = sourceFiles(projectDirectory);
8587
+ for (const file of files) {
8588
+ const names = wantedImportsOf((0, import_node_path36.join)(projectDirectory, file), wanted);
8589
+ const target = TEST_FILE.test(file) ? tested : shipped;
8590
+ for (const name of names) {
8591
+ target.add(name);
8592
+ }
8593
+ }
8594
+ const shippedNames = [...shipped];
8595
+ return { shipped: shippedNames, testsOnly: [...tested].filter((name) => !shippedNames.includes(name)) };
8596
+ }
8597
+ function findUsage(repositoryRoot, projectDirectories, wanted) {
8598
+ const usage = {};
8599
+ for (const dir of projectDirectories) {
8600
+ const { shipped, testsOnly } = importsOfProject((0, import_node_path36.join)(repositoryRoot, dir), wanted);
8601
+ for (const name of shipped) {
8602
+ usage[name] ??= { runtime: [], testsOnly: [] };
8603
+ usage[name].runtime.push(dir);
8604
+ }
8605
+ for (const name of testsOnly) {
8606
+ usage[name] ??= { runtime: [], testsOnly: [] };
8607
+ usage[name].testsOnly.push(dir);
8608
+ }
8609
+ }
8610
+ return usage;
8611
+ }
8612
+ function adoptDependencies(repositoryRoot, dependencies = {}) {
8613
+ const run = dependencies.run ?? runShell;
8614
+ requireCleanWorkingTree(repositoryRoot, dependencies.capture ?? runCapture);
8615
+ const rootPath = (0, import_node_path36.join)(repositoryRoot, "package.json");
8616
+ if (!fileExists(rootPath)) {
8617
+ throw new Error("No package.json at the repository root.");
8618
+ }
8619
+ const root = readJson(rootPath);
8620
+ const rootDependencies = root.dependencies ?? {};
8621
+ const projects = locateProjects(repositoryRoot).filter((location) => location.ecosystem === "npm");
8622
+ const manifests = new Map(projects.map((location) => [location.dir, readJson(location.manifestPath)]));
8623
+ const usage = findUsage(repositoryRoot, projects.map((location) => location.dir), new Set(Object.keys(rootDependencies)));
8624
+ const declared = Object.fromEntries([...manifests].map(([dir, manifest]) => [dir, { ...manifest.devDependencies, ...manifest.dependencies }]));
8625
+ const plan = planDependencyMoves(rootDependencies, usage, declared);
8626
+ if (plan.moves.length === 0 && plan.removed.length === 0) {
8627
+ return { plan, installed: true };
8628
+ }
8629
+ for (const move of plan.moves) {
8630
+ const manifest = manifests.get(move.dir);
8631
+ manifest[move.field] = Object.fromEntries(Object.entries({ ...manifest[move.field], [move.name]: move.range }).toSorted(([a], [b]) => a.localeCompare(b)));
8632
+ }
8633
+ for (const location of projects) {
8634
+ if (plan.moves.some((move) => move.dir === location.dir)) {
8635
+ writeFileEnsured(location.manifestPath, toJson(manifests.get(location.dir)));
8636
+ }
8637
+ }
8638
+ const remaining = Object.fromEntries(Object.entries(rootDependencies).filter(([name]) => !plan.removed.includes(name)));
8639
+ if (Object.keys(remaining).length === 0) {
8640
+ Reflect.deleteProperty(root, "dependencies");
8641
+ } else {
8642
+ root.dependencies = remaining;
8643
+ }
8644
+ writeFileEnsured(rootPath, toJson(root));
8645
+ const installed = run("npm", ["install", "--ignore-scripts", "--no-audit", "--no-fund"], repositoryRoot) === 0;
8646
+ return { plan, installed };
8647
+ }
8648
+
8649
+ // src/repository-adoption/adopt-kinds.use-case.ts
8650
+ var import_node_fs32 = require("fs");
8651
+ var import_node_path37 = require("path");
8652
+
8502
8653
  // src/repository-adoption/infer-project-kind.algorithm.ts
8503
8654
  var SERVER_FRAMEWORKS = ["express", "koa", "fastify", "@nestjs/core"];
8504
8655
  function declares(manifest, name) {
@@ -8598,32 +8749,32 @@ function inferProjectKind(evidence) {
8598
8749
  // src/repository-adoption/adopt-kinds.use-case.ts
8599
8750
  var TYPE_TAG = "type:";
8600
8751
  function readEvidence(repositoryRoot, location) {
8601
- const directory = (0, import_node_path36.join)(repositoryRoot, location.dir);
8602
- const files = (0, import_node_fs31.readdirSync)(directory);
8752
+ const directory = (0, import_node_path37.join)(repositoryRoot, location.dir);
8753
+ const files = (0, import_node_fs32.readdirSync)(directory);
8603
8754
  const manifest = location.ecosystem === "npm" ? readJson(location.manifestPath) : {};
8604
- const text = location.ecosystem === "npm" ? "" : (0, import_node_fs31.readFileSync)(location.manifestPath, "utf8");
8755
+ const text = location.ecosystem === "npm" ? "" : (0, import_node_fs32.readFileSync)(location.manifestPath, "utf8");
8605
8756
  let hasProgram = false;
8606
8757
  if (location.ecosystem === "go") {
8607
- hasProgram = files.filter((file) => file.endsWith(".go")).some((file) => /^package main\b/m.test((0, import_node_fs31.readFileSync)((0, import_node_path36.join)(directory, file), "utf8")));
8758
+ hasProgram = files.filter((file) => file.endsWith(".go")).some((file) => /^package main\b/m.test((0, import_node_fs32.readFileSync)((0, import_node_path37.join)(directory, file), "utf8")));
8608
8759
  } else if (location.ecosystem === "pub") {
8609
- hasProgram = (0, import_node_fs31.existsSync)((0, import_node_path36.join)(directory, "lib", "main.dart"));
8760
+ hasProgram = (0, import_node_fs32.existsSync)((0, import_node_path37.join)(directory, "lib", "main.dart"));
8610
8761
  }
8611
8762
  return { ecosystem: location.ecosystem, manifest, text, files, hasProgram };
8612
8763
  }
8613
8764
  function existingKind(directory) {
8614
8765
  const sources = [];
8615
- if (fileExists((0, import_node_path36.join)(directory, "project.json"))) {
8616
- sources.push(readJson((0, import_node_path36.join)(directory, "project.json")).tags ?? []);
8766
+ if (fileExists((0, import_node_path37.join)(directory, "project.json"))) {
8767
+ sources.push(readJson((0, import_node_path37.join)(directory, "project.json")).tags ?? []);
8617
8768
  }
8618
- if (fileExists((0, import_node_path36.join)(directory, "package.json"))) {
8619
- sources.push(readJson((0, import_node_path36.join)(directory, "package.json")).nx?.tags ?? []);
8769
+ if (fileExists((0, import_node_path37.join)(directory, "package.json"))) {
8770
+ sources.push(readJson((0, import_node_path37.join)(directory, "package.json")).nx?.tags ?? []);
8620
8771
  }
8621
8772
  return sources.flat().find((tag) => tag.startsWith(TYPE_TAG))?.slice(TYPE_TAG.length);
8622
8773
  }
8623
8774
  function writeKindTag(directory, name, kind) {
8624
8775
  const tag = `${TYPE_TAG}${kind}`;
8625
- const projectJson = (0, import_node_path36.join)(directory, "project.json");
8626
- const packageJson = (0, import_node_path36.join)(directory, "package.json");
8776
+ const projectJson = (0, import_node_path37.join)(directory, "project.json");
8777
+ const packageJson = (0, import_node_path37.join)(directory, "package.json");
8627
8778
  if (fileExists(projectJson)) {
8628
8779
  const project = readJson(projectJson);
8629
8780
  writeFileEnsured(projectJson, toJson({ ...project, tags: [...project.tags ?? [], tag] }));
@@ -8647,7 +8798,7 @@ function adoptKinds(repositoryRoot, overrides = {}, dependencies = {}) {
8647
8798
  }
8648
8799
  }
8649
8800
  return locations.map((location) => {
8650
- const directory = (0, import_node_path36.join)(repositoryRoot, location.dir);
8801
+ const directory = (0, import_node_path37.join)(repositoryRoot, location.dir);
8651
8802
  const present = existingKind(directory);
8652
8803
  if (present !== void 0) {
8653
8804
  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" };
@@ -8663,12 +8814,12 @@ function adoptKinds(repositoryRoot, overrides = {}, dependencies = {}) {
8663
8814
  }
8664
8815
 
8665
8816
  // src/repository-adoption/adopt-overlay.use-case.ts
8666
- var import_node_fs33 = require("fs");
8667
- var import_node_path38 = require("path");
8817
+ var import_node_fs34 = require("fs");
8818
+ var import_node_path39 = require("path");
8668
8819
 
8669
8820
  // src/workspace-upgrade/upgrade-workspace.use-case.ts
8670
- var import_node_fs32 = require("fs");
8671
- var import_node_path37 = require("path");
8821
+ var import_node_fs33 = require("fs");
8822
+ var import_node_path38 = require("path");
8672
8823
  function resolveRegistry(options, persisted) {
8673
8824
  if (options.registry === "azure-artifacts" || options.organization && options.artifactsFeed) {
8674
8825
  const persistedAzure = persisted?.kind === "azure-artifacts" ? persisted : void 0;
@@ -8697,7 +8848,7 @@ function resolveWorkspaceName(workspaceRoot, persisted) {
8697
8848
  return persisted.workspaceName;
8698
8849
  }
8699
8850
  try {
8700
- const existing = (0, import_node_fs32.readdirSync)(workspaceRoot).find(
8851
+ const existing = (0, import_node_fs33.readdirSync)(workspaceRoot).find(
8701
8852
  (file) => file.endsWith(".code-workspace") && file !== "undefined.code-workspace"
8702
8853
  );
8703
8854
  if (existing) {
@@ -8705,11 +8856,11 @@ function resolveWorkspaceName(workspaceRoot, persisted) {
8705
8856
  }
8706
8857
  } catch {
8707
8858
  }
8708
- return (0, import_node_path37.basename)(workspaceRoot);
8859
+ return (0, import_node_path38.basename)(workspaceRoot);
8709
8860
  }
8710
8861
  function detectNpmAuth(workspaceRoot) {
8711
8862
  try {
8712
- return (0, import_node_fs32.readFileSync)((0, import_node_path37.join)(workspaceRoot, "azure-pipelines.yml"), "utf8").includes("npmAuthenticate@0") ? "build-identity" : void 0;
8863
+ return (0, import_node_fs33.readFileSync)((0, import_node_path38.join)(workspaceRoot, "azure-pipelines.yml"), "utf8").includes("npmAuthenticate@0") ? "build-identity" : void 0;
8713
8864
  } catch {
8714
8865
  return void 0;
8715
8866
  }
@@ -8756,7 +8907,7 @@ function resolveOverlayOptions(workspaceRoot, options, persisted) {
8756
8907
  };
8757
8908
  }
8758
8909
  function runUpgrade(workspaceRoot, options) {
8759
- if (!fileExists((0, import_node_path37.join)(workspaceRoot, "nx.json"))) {
8910
+ if (!fileExists((0, import_node_path38.join)(workspaceRoot, "nx.json"))) {
8760
8911
  throw new Error(
8761
8912
  `No nx.json found in ${workspaceRoot} \u2014 this does not look like an Nx workspace. Run 'mnci upgrade' from the workspace root.`
8762
8913
  );
@@ -8830,7 +8981,7 @@ function runUpgrade(workspaceRoot, options) {
8830
8981
  "Formatting the workspace (eslint --fix) \u2014 the slowest step, minutes on a large workspace"
8831
8982
  );
8832
8983
  runFormatter(workspaceRoot);
8833
- const nxJson = readJson((0, import_node_path37.join)(workspaceRoot, "nx.json"));
8984
+ const nxJson = readJson((0, import_node_path38.join)(workspaceRoot, "nx.json"));
8834
8985
  const stranded = locateStrandedReleaseTags(workspaceRoot, nxJson.release?.releaseTag?.pattern);
8835
8986
  for (const entry of stranded) {
8836
8987
  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).`);
@@ -8841,9 +8992,9 @@ function runUpgrade(workspaceRoot, options) {
8841
8992
 
8842
8993
  // src/repository-adoption/adopt-overlay.use-case.ts
8843
8994
  function detectCiProvider(repositoryRoot) {
8844
- const azure = (0, import_node_fs33.existsSync)((0, import_node_path38.join)(repositoryRoot, "azure-pipelines.yml"));
8845
- const workflows = (0, import_node_path38.join)(repositoryRoot, ".github", "workflows");
8846
- const github = (0, import_node_fs33.existsSync)(workflows) && (0, import_node_fs33.readdirSync)(workflows).some((file) => /\.ya?ml$/.test(file));
8995
+ const azure = (0, import_node_fs34.existsSync)((0, import_node_path39.join)(repositoryRoot, "azure-pipelines.yml"));
8996
+ const workflows = (0, import_node_path39.join)(repositoryRoot, ".github", "workflows");
8997
+ const github = (0, import_node_fs34.existsSync)(workflows) && (0, import_node_fs34.readdirSync)(workflows).some((file) => /\.ya?ml$/.test(file));
8847
8998
  if (azure && github) {
8848
8999
  return "both";
8849
9000
  }
@@ -8853,7 +9004,7 @@ function detectCiProvider(repositoryRoot) {
8853
9004
  return github ? "github" : void 0;
8854
9005
  }
8855
9006
  function detectTestRunner(repositoryRoot) {
8856
- const manifestPath = (0, import_node_path38.join)(repositoryRoot, "package.json");
9007
+ const manifestPath = (0, import_node_path39.join)(repositoryRoot, "package.json");
8857
9008
  if (!fileExists(manifestPath)) {
8858
9009
  return void 0;
8859
9010
  }
@@ -8866,7 +9017,7 @@ function detectTestRunner(repositoryRoot) {
8866
9017
  }
8867
9018
  function adoptOverlay(repositoryRoot, options, dependencies = {}) {
8868
9019
  requireCleanWorkingTree(repositoryRoot, dependencies.capture ?? runCapture);
8869
- if (!fileExists((0, import_node_path38.join)(repositoryRoot, "nx.json"))) {
9020
+ if (!fileExists((0, import_node_path39.join)(repositoryRoot, "nx.json"))) {
8870
9021
  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.");
8871
9022
  }
8872
9023
  const persisted = readMnciConfig(repositoryRoot);
@@ -8879,7 +9030,7 @@ function adoptOverlay(repositoryRoot, options, dependencies = {}) {
8879
9030
  }
8880
9031
 
8881
9032
  // src/repository-adoption/adopt-toolchain.use-case.ts
8882
- var import_node_path40 = require("path");
9033
+ var import_node_path41 = require("path");
8883
9034
 
8884
9035
  // src/repository-adoption/align-nx-family.algorithm.ts
8885
9036
  var VERSION = /^[\^~]?(\d+)\.(\d+)\.(\d+)$/;
@@ -8917,24 +9068,24 @@ function alignNxFamily(dependencies, version) {
8917
9068
  }
8918
9069
 
8919
9070
  // src/repository-adoption/retire-tooling.use-case.ts
8920
- var import_node_fs34 = require("fs");
8921
- var import_node_path39 = require("path");
9071
+ var import_node_fs35 = require("fs");
9072
+ var import_node_path40 = require("path");
8922
9073
  var RETIRED_COMMAND = /\b(?:prettier|oxfmt|oxlint)\b/;
8923
9074
  function retireTooling(repositoryRoot) {
8924
9075
  const removed = [];
8925
9076
  for (const file of RETIRED_FORMATTER_FILES) {
8926
- if (!fileExists((0, import_node_path39.join)(repositoryRoot, file))) {
9077
+ if (!fileExists((0, import_node_path40.join)(repositoryRoot, file))) {
8927
9078
  continue;
8928
9079
  }
8929
- (0, import_node_fs34.rmSync)((0, import_node_path39.join)(repositoryRoot, file), { force: true });
9080
+ (0, import_node_fs35.rmSync)((0, import_node_path40.join)(repositoryRoot, file), { force: true });
8930
9081
  removed.push(file);
8931
9082
  }
8932
- const hadRegistry = fileExists((0, import_node_path39.join)(repositoryRoot, ".verdaccio"));
9083
+ const hadRegistry = fileExists((0, import_node_path40.join)(repositoryRoot, ".verdaccio"));
8933
9084
  removeLocalRegistryScaffolding(repositoryRoot);
8934
9085
  if (hadRegistry) {
8935
9086
  removed.push(".verdaccio, its dependency and the local-registry target");
8936
9087
  }
8937
- const manifestPath = (0, import_node_path39.join)(repositoryRoot, "package.json");
9088
+ const manifestPath = (0, import_node_path40.join)(repositoryRoot, "package.json");
8938
9089
  if (!fileExists(manifestPath)) {
8939
9090
  return removed;
8940
9091
  }
@@ -8981,7 +9132,7 @@ function adoptToolchain(repositoryRoot, options = {}, dependencies = {}) {
8981
9132
  console.log(message);
8982
9133
  });
8983
9134
  requireCleanWorkingTree(repositoryRoot, capture);
8984
- const manifestPath = (0, import_node_path40.join)(repositoryRoot, "package.json");
9135
+ const manifestPath = (0, import_node_path41.join)(repositoryRoot, "package.json");
8985
9136
  if (!fileExists(manifestPath)) {
8986
9137
  throw new Error("No package.json at the repository root.");
8987
9138
  }
@@ -9031,8 +9182,8 @@ function createBaselineTags(repositoryRoot, stranded) {
9031
9182
  }
9032
9183
 
9033
9184
  // src/repository-adoption/inspect-repository.use-case.ts
9034
- var import_node_fs35 = require("fs");
9035
- var import_node_path41 = require("path");
9185
+ var import_node_fs36 = require("fs");
9186
+ var import_node_path42 = require("path");
9036
9187
  var LOCKFILES = [
9037
9188
  ["package-lock.json", "npm"],
9038
9189
  ["pnpm-lock.yaml", "pnpm"],
@@ -9053,25 +9204,25 @@ function inspectRepository(repositoryRoot) {
9053
9204
  const isGitRepo = inside.status === 0 && inside.stdout.trim() === "true";
9054
9205
  const dirty = isGitRepo && runCapture("git", ["status", "--porcelain"], repositoryRoot).stdout.trim() !== "";
9055
9206
  const tags = isGitRepo ? runCapture("git", ["tag", "--list"], repositoryRoot).stdout.split(/\r?\n/).filter(Boolean) : [];
9056
- const manifestPath = (0, import_node_path41.join)(repositoryRoot, "package.json");
9207
+ const manifestPath = (0, import_node_path42.join)(repositoryRoot, "package.json");
9057
9208
  const manifest = fileExists(manifestPath) ? readJson(manifestPath) : {};
9058
- const nxJsonPath = (0, import_node_path41.join)(repositoryRoot, "nx.json");
9209
+ const nxJsonPath = (0, import_node_path42.join)(repositoryRoot, "nx.json");
9059
9210
  const nxJson = fileExists(nxJsonPath) ? readJson(nxJsonPath) : {};
9060
9211
  const ci = [];
9061
9212
  const pipelines = [];
9062
- if ((0, import_node_fs35.existsSync)((0, import_node_path41.join)(repositoryRoot, "azure-pipelines.yml"))) {
9213
+ if ((0, import_node_fs36.existsSync)((0, import_node_path42.join)(repositoryRoot, "azure-pipelines.yml"))) {
9063
9214
  ci.push("azure");
9064
- pipelines.push((0, import_node_path41.join)(repositoryRoot, "azure-pipelines.yml"));
9215
+ pipelines.push((0, import_node_path42.join)(repositoryRoot, "azure-pipelines.yml"));
9065
9216
  }
9066
- const workflows = (0, import_node_path41.join)(repositoryRoot, ".github", "workflows");
9067
- if ((0, import_node_fs35.existsSync)(workflows)) {
9068
- const files = (0, import_node_fs35.readdirSync)(workflows).filter((file) => /\.ya?ml$/.test(file));
9217
+ const workflows = (0, import_node_path42.join)(repositoryRoot, ".github", "workflows");
9218
+ if ((0, import_node_fs36.existsSync)(workflows)) {
9219
+ const files = (0, import_node_fs36.readdirSync)(workflows).filter((file) => /\.ya?ml$/.test(file));
9069
9220
  if (files.length > 0) {
9070
9221
  ci.push("github");
9071
- pipelines.push(...files.map((file) => (0, import_node_path41.join)(workflows, file)));
9222
+ pipelines.push(...files.map((file) => (0, import_node_path42.join)(workflows, file)));
9072
9223
  }
9073
9224
  }
9074
- const rootFiles = (0, import_node_fs35.readdirSync)(repositoryRoot);
9225
+ const rootFiles = (0, import_node_fs36.readdirSync)(repositoryRoot);
9075
9226
  return {
9076
9227
  isGitRepo,
9077
9228
  dirty,
@@ -9082,7 +9233,7 @@ function inspectRepository(repositoryRoot) {
9082
9233
  tagCount: tags.length,
9083
9234
  strandedTags: isGitRepo ? locateStrandedReleaseTags(repositoryRoot, nxJson.release?.releaseTag?.pattern) : [],
9084
9235
  ci,
9085
- pipelineUsesMnci: pipelines.some((file) => /\bmnci ci\b/.test((0, import_node_fs35.readFileSync)(file, "utf8"))),
9236
+ pipelineUsesMnci: pipelines.some((file) => /\bmnci ci\b/.test((0, import_node_fs36.readFileSync)(file, "utf8"))),
9086
9237
  retiredTooling: [.../* @__PURE__ */ new Set([...rootFiles.filter((file) => RETIRED_FILE_PATTERN.test(file) || file === ".verdaccio"), ...retiredNamesIn(manifest)])],
9087
9238
  personalFiles: PERSONAL_FILES.filter((file) => rootFiles.includes(file))
9088
9239
  };
@@ -9139,6 +9290,10 @@ function reportAdoption(repositoryRoot) {
9139
9290
  }
9140
9291
  function runAdopt(repositoryRoot, options = {}) {
9141
9292
  const report = reportAdoption(repositoryRoot);
9293
+ if (options.dependencies === true) {
9294
+ runDependencies(repositoryRoot);
9295
+ return;
9296
+ }
9142
9297
  if (options.kinds === true) {
9143
9298
  runKinds2(repositoryRoot, options);
9144
9299
  return;
@@ -9278,6 +9433,31 @@ function runKinds2(repositoryRoot, options) {
9278
9433
  process.exitCode = 1;
9279
9434
  }
9280
9435
  }
9436
+ function runDependencies(repositoryRoot) {
9437
+ try {
9438
+ const { plan, installed } = adoptDependencies(repositoryRoot);
9439
+ for (const move of plan.moves) {
9440
+ logger.success(`${move.name} ${move.range} -> ${move.dir} (${move.field})`);
9441
+ }
9442
+ for (const conflict of plan.conflicts) {
9443
+ logger.warn(`${conflict.name}: ${conflict.dir} already declares ${conflict.projectRange}; the root had ${conflict.rootRange}. The project's own range is kept.`);
9444
+ }
9445
+ for (const kept of plan.keptAtRoot) {
9446
+ logger.warn(`${kept.name} stays at the root: ${kept.reason}`);
9447
+ }
9448
+ if (plan.removed.length === 0) {
9449
+ logger.success("The root declares no runtime dependency that a project imports. Nothing to move.");
9450
+ } else if (installed) {
9451
+ logger.success(`Moved ${plan.removed.length} package(s) out of the root and reinstalled. Review with \`git diff\` and commit package.json files and package-lock.json.`);
9452
+ } else {
9453
+ logger.error("The moves were written, but npm install failed. Run `npm install` and read its error before committing.");
9454
+ process.exitCode = 1;
9455
+ }
9456
+ } catch (error) {
9457
+ logger.error(error instanceof Error ? error.message : String(error));
9458
+ process.exitCode = 1;
9459
+ }
9460
+ }
9281
9461
 
9282
9462
  // src/command-catalog/command-groups.config.ts
9283
9463
  var COMMAND_GROUPS = {
@@ -9350,13 +9530,13 @@ function runCommands(program, options) {
9350
9530
  }
9351
9531
 
9352
9532
  // src/workspace-info/list-projects.use-case.ts
9353
- var import_node_path42 = require("path");
9533
+ var import_node_path43 = require("path");
9354
9534
  var TYPE_TAG_PREFIX = "type:";
9355
9535
  function listProjects(workspaceRoot) {
9356
9536
  return locateProjects(workspaceRoot).map((location) => {
9357
- const projectJsonPath = (0, import_node_path42.join)(workspaceRoot, location.dir, "project.json");
9537
+ const projectJsonPath = (0, import_node_path43.join)(workspaceRoot, location.dir, "project.json");
9358
9538
  const project = fileExists(projectJsonPath) ? readJson(projectJsonPath) : {};
9359
- const manifestPath = (0, import_node_path42.join)(workspaceRoot, location.dir, "package.json");
9539
+ const manifestPath = (0, import_node_path43.join)(workspaceRoot, location.dir, "package.json");
9360
9540
  const manifestTags = fileExists(manifestPath) ? readJson(manifestPath).nx?.tags ?? [] : [];
9361
9541
  const typeTag = [...project.tags ?? [], ...manifestTags].find((tag) => tag.startsWith(TYPE_TAG_PREFIX));
9362
9542
  return {
@@ -9369,7 +9549,7 @@ function listProjects(workspaceRoot) {
9369
9549
  }).toSorted((a, b) => a.dir.localeCompare(b.dir));
9370
9550
  }
9371
9551
  function runProjects(workspaceRoot, options) {
9372
- if (!fileExists((0, import_node_path42.join)(workspaceRoot, "nx.json"))) {
9552
+ if (!fileExists((0, import_node_path43.join)(workspaceRoot, "nx.json"))) {
9373
9553
  throw new Error("No nx.json found here. Run `projects` from the workspace root.");
9374
9554
  }
9375
9555
  const projects = listProjects(workspaceRoot);
@@ -9383,11 +9563,11 @@ function runProjects(workspaceRoot, options) {
9383
9563
  }
9384
9564
 
9385
9565
  // src/workspace-info/read-workspace-info.use-case.ts
9386
- var import_node_path44 = require("path");
9566
+ var import_node_path45 = require("path");
9387
9567
 
9388
9568
  // src/cli-version/check-for-update.use-case.ts
9389
- var import_node_fs36 = require("fs");
9390
- var import_node_path43 = require("path");
9569
+ var import_node_fs37 = require("fs");
9570
+ var import_node_path44 = require("path");
9391
9571
  var import_cross_spawn3 = __toESM(require("cross-spawn"));
9392
9572
  var PACKAGE_NAME2 = "@mnci/cli";
9393
9573
  var REGISTRY_TIMEOUT_MS = 2e3;
@@ -9446,8 +9626,8 @@ function releaseTriple(version) {
9446
9626
  }
9447
9627
  function readCliVersion(packageDirectory) {
9448
9628
  try {
9449
- const manifestPath = (0, import_node_path43.join)(packageDirectory, "..", "package.json");
9450
- const manifest = JSON.parse((0, import_node_fs36.readFileSync)(manifestPath, "utf8"));
9629
+ const manifestPath = (0, import_node_path44.join)(packageDirectory, "..", "package.json");
9630
+ const manifest = JSON.parse((0, import_node_fs37.readFileSync)(manifestPath, "utf8"));
9451
9631
  return manifest.version ?? "0.0.0";
9452
9632
  } catch {
9453
9633
  return "0.0.0";
@@ -9457,7 +9637,7 @@ function readCliVersion(packageDirectory) {
9457
9637
  // src/workspace-info/read-workspace-info.use-case.ts
9458
9638
  function readWorkspaceInfo(workspaceRoot, cliVersion) {
9459
9639
  const latest = fetchLatestVersion() ?? null;
9460
- const isWorkspace = fileExists((0, import_node_path44.join)(workspaceRoot, "nx.json"));
9640
+ const isWorkspace = fileExists((0, import_node_path45.join)(workspaceRoot, "nx.json"));
9461
9641
  return {
9462
9642
  cli: {
9463
9643
  version: cliVersion,
@@ -9477,9 +9657,203 @@ function runInfo(workspaceRoot, cliVersion, options) {
9477
9657
  logger.info(info.workspace ? `workspace: ${info.workspace.root}` : "not inside a workspace");
9478
9658
  }
9479
9659
 
9660
+ // src/interactive-wizard/interactive-wizard.use-case.ts
9661
+ var import_node_path46 = require("path");
9662
+
9663
+ // src/interactive-wizard/adopt-steps.config.ts
9664
+ var ADOPT_GLOBAL_OPTIONS = ["json"];
9665
+ var ADOPT_STEPS = [
9666
+ { id: "report", label: "Report", summary: "read the repository and say what adopting it involves (changes nothing)", options: [] },
9667
+ { id: "tags", label: "Baseline release tags", summary: "create the tag each renamed project needs so a release continues from its last version", switch: "tags", options: [] },
9668
+ { id: "toolchain", label: "Toolchain", summary: "retire old tooling, align Nx to one version, make the audit pass", switch: "toolchain", options: ["nx"] },
9669
+ { id: "kinds", label: "Project kinds", summary: "record each project's kind as a type tag", switch: "kinds", options: ["kind"] },
9670
+ { id: "dependencies", label: "Dependencies", summary: "move root runtime dependencies into the projects that import them", switch: "dependencies", options: [] },
9671
+ {
9672
+ id: "overlay",
9673
+ label: "Overlay and pipeline",
9674
+ summary: "apply the release config, .npmrc, commitlint and the CI pipeline",
9675
+ switch: "overlay",
9676
+ options: ["scope", "registry", "organization", "project", "artifacts-feed", "agent", "variable-group", "npm-auth", "ci", "test-runner"]
9677
+ }
9678
+ ];
9679
+
9680
+ // src/interactive-wizard/applicable-options.policy.ts
9681
+ function optionsOfAdd(command, kind) {
9682
+ const claimed = new Set(PROJECT_KIND_CATALOG.flatMap((entry) => entry.flags));
9683
+ const own = new Set(PROJECT_KIND_CATALOG.find((entry) => entry.kind === kind)?.flags);
9684
+ return command.options.filter((option) => !claimed.has(option.name) || own.has(option.name));
9685
+ }
9686
+ function applicableOptions(command, context = {}) {
9687
+ if (command.name === "add") {
9688
+ return optionsOfAdd(command, context.kind);
9689
+ }
9690
+ if (command.name === "adopt") {
9691
+ const step = context.adoptStep;
9692
+ const wanted = /* @__PURE__ */ new Set([...step?.options ?? [], ...ADOPT_GLOBAL_OPTIONS]);
9693
+ return command.options.filter((option) => wanted.has(option.name));
9694
+ }
9695
+ return [...command.options];
9696
+ }
9697
+
9698
+ // src/interactive-wizard/ask-command.use-case.ts
9699
+ var NOT_ASKED = /* @__PURE__ */ new Set(["help", "version"]);
9700
+ var SKIP = "";
9701
+ async function askArgument(argument, prompter) {
9702
+ const message = `${argument.name}${argument.required ? "" : " (optional)"}: ${argument.description}`;
9703
+ if (argument.choices !== void 0) {
9704
+ const choices = argument.choices.map((choice) => ({ name: choice, value: choice }));
9705
+ const value = await prompter.select(message, argument.required ? choices : [{ name: "(skip)", value: SKIP }, ...choices]);
9706
+ return value === SKIP ? [] : [value];
9707
+ }
9708
+ const typed = await prompter.input(message, argument.required);
9709
+ const answer = typed.trim();
9710
+ if (answer === "") {
9711
+ return [];
9712
+ }
9713
+ return argument.variadic ? answer.split(/\s+/) : [answer];
9714
+ }
9715
+ async function askOptionValue(option, prompter) {
9716
+ if (option.takesValue === "none") {
9717
+ return true;
9718
+ }
9719
+ const message = `${option.flags}: ${option.description}`;
9720
+ if (option.choices !== void 0) {
9721
+ return await prompter.select(message, option.choices.map((choice) => ({ name: choice, value: choice })));
9722
+ }
9723
+ if (option.repeatable) {
9724
+ const values = [];
9725
+ let typed2 = await prompter.input(`${message} (an empty answer ends the list)`, false);
9726
+ while (typed2.trim() !== "") {
9727
+ values.push(typed2.trim());
9728
+ typed2 = await prompter.input(`${option.flags} (another, or empty to finish)`, false);
9729
+ }
9730
+ return values;
9731
+ }
9732
+ const typed = await prompter.input(message, option.takesValue === "required");
9733
+ const answer = typed.trim();
9734
+ return answer === "" ? true : answer;
9735
+ }
9736
+ async function askAdoptStep(prompter) {
9737
+ const id = await prompter.select("Which adoption step?", ADOPT_STEPS.map((step) => ({ name: step.label, value: step.id, description: step.summary })));
9738
+ return ADOPT_STEPS.find((step) => step.id === id);
9739
+ }
9740
+ async function askCommand(command, prompter) {
9741
+ const answers = { arguments: [], options: {} };
9742
+ for (const argument of command.arguments) {
9743
+ answers.arguments.push(...await askArgument(argument, prompter));
9744
+ }
9745
+ let adoptStep;
9746
+ if (command.name === "adopt") {
9747
+ adoptStep = await askAdoptStep(prompter);
9748
+ if (adoptStep.switch !== void 0) {
9749
+ answers.options[adoptStep.switch] = true;
9750
+ }
9751
+ }
9752
+ const offered = applicableOptions(command, { kind: command.name === "add" ? answers.arguments[0] : void 0, adoptStep }).filter((option) => !NOT_ASKED.has(option.name) && answers.options[option.name] === void 0);
9753
+ if (offered.length === 0) {
9754
+ return answers;
9755
+ }
9756
+ const chosen = await prompter.checkbox("Options (space to select, enter to continue)", offered.map((option) => ({ name: option.flags, value: option.name, description: option.description })));
9757
+ for (const name of chosen) {
9758
+ const option = offered.find((candidate) => candidate.name === name);
9759
+ const value = await askOptionValue(option, prompter);
9760
+ if (!(Array.isArray(value) && value.length === 0)) {
9761
+ answers.options[name] = value;
9762
+ }
9763
+ }
9764
+ return answers;
9765
+ }
9766
+
9767
+ // src/interactive-wizard/build-argv.algorithm.ts
9768
+ function longFlag(option) {
9769
+ return `--${option.name}`;
9770
+ }
9771
+ function buildArgv(command, answers) {
9772
+ const argv = [command.name, ...answers.arguments];
9773
+ for (const [name, answer] of Object.entries(answers.options)) {
9774
+ const option = command.options.find((candidate) => candidate.name === name);
9775
+ if (option === void 0) {
9776
+ throw new Error(`mnci ${command.name} has no option --${name}`);
9777
+ }
9778
+ if (answer === false) {
9779
+ continue;
9780
+ }
9781
+ if (typeof answer === "boolean") {
9782
+ argv.push(longFlag(option));
9783
+ } else {
9784
+ const values = Array.isArray(answer) ? answer : [answer];
9785
+ for (const value of values) {
9786
+ argv.push(longFlag(option), value);
9787
+ }
9788
+ }
9789
+ }
9790
+ return argv;
9791
+ }
9792
+ function formatCommandLine(argv) {
9793
+ return ["mnci", ...argv.map((argument) => /\s/.test(argument) ? JSON.stringify(argument) : argument)].join(" ");
9794
+ }
9795
+
9796
+ // src/interactive-wizard/inquirer-prompter.client.ts
9797
+ var import_prompts4 = require("@inquirer/prompts");
9798
+ function toRows(choices) {
9799
+ const rows = [];
9800
+ let current;
9801
+ for (const choice of choices) {
9802
+ if (choice.group !== void 0 && choice.group !== current) {
9803
+ current = choice.group;
9804
+ rows.push(new import_prompts4.Separator(`-- ${choice.group} --`));
9805
+ }
9806
+ rows.push({ name: choice.name, value: choice.value, description: choice.description });
9807
+ }
9808
+ return rows;
9809
+ }
9810
+ function createInquirerPrompter() {
9811
+ return {
9812
+ select: async (message, choices) => await (0, import_prompts4.select)({ message, choices: toRows(choices), pageSize: 18 }),
9813
+ checkbox: async (message, choices) => await (0, import_prompts4.checkbox)({ message, choices: toRows(choices), pageSize: 18 }),
9814
+ input: async (message, required) => await (0, import_prompts4.input)({ message, validate: (value) => !required || value.trim().length > 0 || "A value is required" }),
9815
+ confirm: async (message, fallback) => await (0, import_prompts4.confirm)({ message, default: fallback })
9816
+ };
9817
+ }
9818
+
9819
+ // src/interactive-wizard/interactive-wizard.use-case.ts
9820
+ var GROUP_LABELS = {
9821
+ workspace: "Workspace",
9822
+ projects: "Projects",
9823
+ dependencies: "Dependencies",
9824
+ pipeline: "Pipeline",
9825
+ inspect: "Inspect"
9826
+ };
9827
+ var WIZARD_GROUP_OVERRIDES = { adopt: "workspace" };
9828
+ var ORDER_IN_WORKSPACE = ["projects", "dependencies", "workspace", "pipeline", "inspect"];
9829
+ var ORDER_OUTSIDE = ["workspace", "projects", "dependencies", "pipeline", "inspect"];
9830
+ var SUMMARY_LENGTH = 110;
9831
+ function summarise(description) {
9832
+ const first = description.split(/\.\s| — /, 1)[0];
9833
+ return first.length > SUMMARY_LENGTH ? `${first.slice(0, SUMMARY_LENGTH - 1)}\u2026` : first;
9834
+ }
9835
+ function wizardMenu(commands, inWorkspace) {
9836
+ const groupOf = (command) => WIZARD_GROUP_OVERRIDES[command.name] ?? command.group;
9837
+ const order = inWorkspace ? ORDER_IN_WORKSPACE : ORDER_OUTSIDE;
9838
+ return order.flatMap((group) => commands.filter((command) => groupOf(command) === group).map((command) => ({ name: `${command.name.padEnd(9)} ${summarise(command.description)}`, value: command.name, group: GROUP_LABELS[group] })));
9839
+ }
9840
+ async function runInteractive(program, prompter = createInquirerPrompter(), workingDirectory = process.cwd()) {
9841
+ const commands = describeCommands(program);
9842
+ const inWorkspace = fileExists((0, import_node_path46.join)(workingDirectory, "nx.json"));
9843
+ const name = await prompter.select("What would you like to do?", wizardMenu(commands, inWorkspace));
9844
+ const command = commands.find((candidate) => candidate.name === name);
9845
+ const argv = buildArgv(command, await askCommand(command, prompter));
9846
+ logger.info(`Running: ${formatCommandLine(argv)}`);
9847
+ if (await prompter.confirm("Run it?", true)) {
9848
+ await program.parseAsync(argv, { from: "user" });
9849
+ } else {
9850
+ logger.info("Nothing was run.");
9851
+ }
9852
+ }
9853
+
9480
9854
  // src/workspace-creation/adopt-directory.use-case.ts
9481
- var import_node_fs37 = require("fs");
9482
- var import_node_path45 = require("path");
9855
+ var import_node_fs38 = require("fs");
9856
+ var import_node_path47 = require("path");
9483
9857
  var NEVER_COPIED = /* @__PURE__ */ new Set([".git", "node_modules"]);
9484
9858
  var AGENT_AND_CI_ENTRIES = [
9485
9859
  "AGENTS.md",
@@ -9510,13 +9884,13 @@ var PREFLIGHT_BLOCKERS = [
9510
9884
  "packages"
9511
9885
  ];
9512
9886
  function assertAdoptableDirectory(target) {
9513
- if (!(0, import_node_fs37.existsSync)(target)) {
9887
+ if (!(0, import_node_fs38.existsSync)(target)) {
9514
9888
  throw new Error(`--into '${target}' does not exist. Create or clone it first.`);
9515
9889
  }
9516
- if (!(0, import_node_fs37.statSync)(target).isDirectory()) {
9890
+ if (!(0, import_node_fs38.statSync)(target).isDirectory()) {
9517
9891
  throw new Error(`--into '${target}' is not a directory.`);
9518
9892
  }
9519
- const blockers = PREFLIGHT_BLOCKERS.filter((entry) => (0, import_node_fs37.existsSync)((0, import_node_path45.join)(target, entry)));
9893
+ const blockers = PREFLIGHT_BLOCKERS.filter((entry) => (0, import_node_fs38.existsSync)((0, import_node_path47.join)(target, entry)));
9520
9894
  if (blockers.length > 0) {
9521
9895
  throw new Error(
9522
9896
  `--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.`
@@ -9524,15 +9898,15 @@ function assertAdoptableDirectory(target) {
9524
9898
  }
9525
9899
  }
9526
9900
  function mergeGitignore(generatedFile, targetFile) {
9527
- const existing = (0, import_node_fs37.readFileSync)(targetFile, "utf8");
9901
+ const existing = (0, import_node_fs38.readFileSync)(targetFile, "utf8");
9528
9902
  const present = new Set(existing.split("\n").map((line) => line.trim()));
9529
- const additions = (0, import_node_fs37.readFileSync)(generatedFile, "utf8").split("\n").map((line) => line.trimEnd()).filter((line) => {
9903
+ const additions = (0, import_node_fs38.readFileSync)(generatedFile, "utf8").split("\n").map((line) => line.trimEnd()).filter((line) => {
9530
9904
  const trimmed = line.trim();
9531
9905
  return trimmed !== "" && !trimmed.startsWith("#") && !present.has(trimmed);
9532
9906
  });
9533
9907
  if (additions.length === 0) return false;
9534
9908
  const separator = existing.endsWith("\n") ? "" : "\n";
9535
- (0, import_node_fs37.writeFileSync)(
9909
+ (0, import_node_fs38.writeFileSync)(
9536
9910
  targetFile,
9537
9911
  `${existing}${separator}
9538
9912
  # Added by mnci (Nx workspace)
@@ -9542,9 +9916,9 @@ ${additions.join("\n")}
9542
9916
  return true;
9543
9917
  }
9544
9918
  function adoptGeneratedWorkspace(generatedRoot, target) {
9545
- const entries = (0, import_node_fs37.readdirSync)(generatedRoot).filter((entry) => !NEVER_COPIED.has(entry));
9919
+ const entries = (0, import_node_fs38.readdirSync)(generatedRoot).filter((entry) => !NEVER_COPIED.has(entry));
9546
9920
  const collisions = entries.filter(
9547
- (entry) => (0, import_node_fs37.existsSync)((0, import_node_path45.join)(target, entry)) && !EXISTING_WINS.has(entry) && entry !== ".gitignore"
9921
+ (entry) => (0, import_node_fs38.existsSync)((0, import_node_path47.join)(target, entry)) && !EXISTING_WINS.has(entry) && entry !== ".gitignore"
9548
9922
  );
9549
9923
  if (collisions.length > 0) {
9550
9924
  throw new Error(
@@ -9553,28 +9927,28 @@ function adoptGeneratedWorkspace(generatedRoot, target) {
9553
9927
  }
9554
9928
  const result = { copied: [], kept: [], mergedGitignore: false };
9555
9929
  for (const entry of entries) {
9556
- const from = (0, import_node_path45.join)(generatedRoot, entry);
9557
- const to = (0, import_node_path45.join)(target, entry);
9558
- if (entry === ".gitignore" && (0, import_node_fs37.existsSync)(to)) {
9930
+ const from = (0, import_node_path47.join)(generatedRoot, entry);
9931
+ const to = (0, import_node_path47.join)(target, entry);
9932
+ if (entry === ".gitignore" && (0, import_node_fs38.existsSync)(to)) {
9559
9933
  result.mergedGitignore = mergeGitignore(from, to);
9560
9934
  result.kept.push(entry);
9561
9935
  continue;
9562
9936
  }
9563
- if ((0, import_node_fs37.existsSync)(to)) {
9937
+ if ((0, import_node_fs38.existsSync)(to)) {
9564
9938
  result.kept.push(entry);
9565
9939
  continue;
9566
9940
  }
9567
- (0, import_node_fs37.cpSync)(from, to, { recursive: true });
9941
+ (0, import_node_fs38.cpSync)(from, to, { recursive: true });
9568
9942
  result.copied.push(entry);
9569
9943
  }
9570
- (0, import_node_fs37.rmSync)(generatedRoot, { recursive: true, force: true });
9944
+ (0, import_node_fs38.rmSync)(generatedRoot, { recursive: true, force: true });
9571
9945
  return result;
9572
9946
  }
9573
9947
 
9574
9948
  // src/workspace-creation/create-workspace.use-case.ts
9575
- var import_node_fs38 = require("fs");
9949
+ var import_node_fs39 = require("fs");
9576
9950
  var import_node_os2 = require("os");
9577
- var import_node_path46 = require("path");
9951
+ var import_node_path48 = require("path");
9578
9952
  function nxCloudProviderValue(ci) {
9579
9953
  return ci === "azure" ? "azure" : "github";
9580
9954
  }
@@ -9617,9 +9991,9 @@ async function resolveRegistry2(options) {
9617
9991
  return await promptRegistry();
9618
9992
  }
9619
9993
  async function runNew(name, options) {
9620
- const adoptTarget = options.into === void 0 ? void 0 : (0, import_node_path46.resolve)(process.cwd(), options.into);
9994
+ const adoptTarget = options.into === void 0 ? void 0 : (0, import_node_path48.resolve)(process.cwd(), options.into);
9621
9995
  if (adoptTarget !== void 0) assertAdoptableDirectory(adoptTarget);
9622
- const workspaceName = name ?? (adoptTarget === void 0 ? await promptText("Workspace name") : (0, import_node_path46.basename)(adoptTarget));
9996
+ const workspaceName = name ?? (adoptTarget === void 0 ? await promptText("Workspace name") : (0, import_node_path48.basename)(adoptTarget));
9623
9997
  assertValidProjectName(workspaceName, "Workspace name");
9624
9998
  const scope = options.scope ?? (options.yes ? `@${workspaceName}` : await promptText("npm scope for publishable packages", `@${workspaceName}`));
9625
9999
  const registry = await resolveRegistry2(options);
@@ -9632,8 +10006,8 @@ async function runNew(name, options) {
9632
10006
  const npmAuth = resolveNpmAuth(options.npmAuth, registry, ci);
9633
10007
  const stack = await resolveStack(options);
9634
10008
  const nxCloud = options.nxCloud ?? (options.yes ? false : await promptNxCloud());
9635
- const stagingParent = adoptTarget === void 0 ? process.cwd() : (0, import_node_fs38.mkdtempSync)((0, import_node_path46.join)((0, import_node_os2.tmpdir)(), "mnci-new-"));
9636
- let workspaceRoot = (0, import_node_path46.join)(stagingParent, workspaceName);
10009
+ const stagingParent = adoptTarget === void 0 ? process.cwd() : (0, import_node_fs39.mkdtempSync)((0, import_node_path48.join)((0, import_node_os2.tmpdir)(), "mnci-new-"));
10010
+ let workspaceRoot = (0, import_node_path48.join)(stagingParent, workspaceName);
9637
10011
  try {
9638
10012
  logger.step(`Creating Nx workspace '${workspaceName}' (preset: ts)`);
9639
10013
  runNpx(
@@ -9656,7 +10030,7 @@ async function runNew(name, options) {
9656
10030
  workspaceRoot = adoptTarget;
9657
10031
  }
9658
10032
  } finally {
9659
- if (adoptTarget !== void 0) (0, import_node_fs38.rmSync)(stagingParent, { recursive: true, force: true });
10033
+ if (adoptTarget !== void 0) (0, import_node_fs39.rmSync)(stagingParent, { recursive: true, force: true });
9660
10034
  }
9661
10035
  logger.step(
9662
10036
  "Applying MoNecromanCI overlay (VS Code workspace, release config, .npmrc, commitlint, pipeline, stack)"
@@ -9666,8 +10040,8 @@ async function runNew(name, options) {
9666
10040
  { workspaceName, scope, registry, agent, variableGroup, ci, stack, ...npmAuth && { npmAuth } },
9667
10041
  logger.detail
9668
10042
  );
9669
- (0, import_node_fs38.rmSync)((0, import_node_path46.join)(workspaceRoot, "node_modules"), { recursive: true, force: true });
9670
- (0, import_node_fs38.rmSync)((0, import_node_path46.join)(workspaceRoot, "package-lock.json"), { force: true });
10043
+ (0, import_node_fs39.rmSync)((0, import_node_path48.join)(workspaceRoot, "node_modules"), { recursive: true, force: true });
10044
+ (0, import_node_fs39.rmSync)((0, import_node_path48.join)(workspaceRoot, "package-lock.json"), { force: true });
9671
10045
  logger.step("Installing the toolchain (commitlint + husky + linting/formatting)");
9672
10046
  const installStatus = runShell(
9673
10047
  "npm",
@@ -9704,30 +10078,6 @@ function projectKindHintLines(kinds = PROJECT_KINDS) {
9704
10078
  return lines;
9705
10079
  }
9706
10080
 
9707
- // src/workspace-creation/interactive-wizard.use-case.ts
9708
- var import_node_path47 = require("path");
9709
- var import_prompts4 = require("@inquirer/prompts");
9710
- async function runInteractive() {
9711
- const inWorkspace = fileExists((0, import_node_path47.join)(process.cwd(), "nx.json"));
9712
- const choiceNew = { name: "Create a new monorepo", value: "new" };
9713
- const choiceAdd = { name: "Add a project to this workspace", value: "add" };
9714
- const choiceUpgrade = {
9715
- name: "Upgrade this workspace (re-apply the latest overlay)",
9716
- value: "upgrade"
9717
- };
9718
- const action = await (0, import_prompts4.select)({
9719
- message: "What would you like to do?",
9720
- choices: inWorkspace ? [choiceAdd, choiceUpgrade, choiceNew] : [choiceNew, choiceAdd, choiceUpgrade]
9721
- });
9722
- if (action === "new") {
9723
- await runNew(void 0, {});
9724
- } else if (action === "upgrade") {
9725
- runUpgrade(process.cwd(), {});
9726
- } else {
9727
- await runAdd(void 0, void 0, {});
9728
- }
9729
- }
9730
-
9731
10081
  // src/main.ts
9732
10082
  function buildProgram(cliVersion) {
9733
10083
  const program = new import_commander.Command();
@@ -9760,7 +10110,7 @@ function buildProgram(cliVersion) {
9760
10110
  ).option("--json", "print the findings as one JSON document, each with its remedy").action((options) => {
9761
10111
  runDoctor(process.cwd(), options);
9762
10112
  });
9763
- program.command("adopt").description("Read an existing repository and report what bringing it under mnci would involve: blockers, warnings and the step that clears each. Read-only unless a step flag is given; exits non-zero when something blocks").option("--json", "print the report as one JSON document").option("--tags", "create, locally, the baseline tag each project whose release tags are stranded under an old name needs; nothing is pushed").option("--toolchain", "retire old formatter and registry tooling, align the Nx family to one version and run npm audit fix until the audit gate passes; needs a clean git tree and leaves the changes uncommitted").option("--nx <version>", "the Nx version --toolchain aligns to (default: the newest published in the major already in use)").option("--kinds", "record each project's mnci kind as a type:<kind> tag (npm, Python, Go, Flutter and C# projects); a project where two kinds fit is listed with a guess and not tagged; needs a clean git tree").option("--kind <dir=kind>", "the kind you choose for one project, for --kinds (repeatable)", (value, previous = []) => [...previous, value]).option("--overlay", "apply the mnci overlay (release config, pipeline, npmrc, commitlint, scripts) with the flags mnci upgrade takes; the pipeline keeps the steps mnci does not recognise in its slots; needs a clean git tree and an nx.json").option("--scope <scope>", "npm scope for publishable packages (--overlay)").option("--registry <kind>", "azure-artifacts | npm (--overlay)").option("--organization <name>", "Azure DevOps organization (--overlay)").option("--project <name>", "Azure DevOps project (--overlay)").option("--artifacts-feed <name>", "Azure Artifacts feed (--overlay)").option("--agent <pool>", "CI build agent (--overlay)").option("--variable-group <name>", "Azure DevOps variable group holding the npm PAT (--overlay)").option("--npm-auth <mode>", "pat (default) | build-identity (--overlay, --ci azure only)").option("--ci <provider>", "azure | github | both (--overlay; default: what the repository already has)").option("--test-runner <runner>", "jest | vitest (--overlay; default: what the repository already declares)").action((options) => {
10113
+ program.command("adopt").description("Read an existing repository and report what bringing it under mnci would involve: blockers, warnings and the step that clears each. Read-only unless a step flag is given; exits non-zero when something blocks").option("--json", "print the report as one JSON document").option("--tags", "create, locally, the baseline tag each project whose release tags are stranded under an old name needs; nothing is pushed").option("--toolchain", "retire old formatter and registry tooling, align the Nx family to one version and run npm audit fix until the audit gate passes; needs a clean git tree and leaves the changes uncommitted").option("--nx <version>", "the Nx version --toolchain aligns to (default: the newest published in the major already in use)").option("--dependencies", "move the root package.json runtime dependencies into the projects that import them (same range), then reinstall; a package no project imports stays and is listed; needs a clean git tree").option("--kinds", "record each project's mnci kind as a type:<kind> tag (npm, Python, Go, Flutter and C# projects); a project where two kinds fit is listed with a guess and not tagged; needs a clean git tree").option("--kind <dir=kind>", "the kind you choose for one project, for --kinds (repeatable)", (value, previous = []) => [...previous, value]).option("--overlay", "apply the mnci overlay (release config, pipeline, npmrc, commitlint, scripts) with the flags mnci upgrade takes; the pipeline keeps the steps mnci does not recognise in its slots; needs a clean git tree and an nx.json").option("--scope <scope>", "npm scope for publishable packages (--overlay)").option("--registry <kind>", "azure-artifacts | npm (--overlay)").option("--organization <name>", "Azure DevOps organization (--overlay)").option("--project <name>", "Azure DevOps project (--overlay)").option("--artifacts-feed <name>", "Azure Artifacts feed (--overlay)").option("--agent <pool>", "CI build agent (--overlay)").option("--variable-group <name>", "Azure DevOps variable group holding the npm PAT (--overlay)").option("--npm-auth <mode>", "pat (default) | build-identity (--overlay, --ci azure only)").option("--ci <provider>", "azure | github | both (--overlay; default: what the repository already has)").option("--test-runner <runner>", "jest | vitest (--overlay; default: what the repository already declares)").action((options) => {
9764
10114
  runAdopt(process.cwd(), options);
9765
10115
  });
9766
10116
  program.command("ci").addArgument(new import_commander.Argument("<phase>", "which phase of the pipeline to run").choices(CI_PHASES)).description(
@@ -9829,7 +10179,7 @@ function buildProgram(cliVersion) {
9829
10179
  runInfo(process.cwd(), cliVersion, options);
9830
10180
  });
9831
10181
  program.action(async () => {
9832
- await runInteractive();
10182
+ await runInteractive(program);
9833
10183
  });
9834
10184
  return program;
9835
10185
  }