@mnci/cli 4.42.0 → 4.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -505,6 +505,23 @@ from File`), and the curated root scripts.
505
505
  4. Installs the chosen **stack** (see below), `husky` + `@commitlint/*` for
506
506
  real, so versions resolve at generation time.
507
507
 
508
+ ## `mnci adopt`: reading an existing repository before changing it
509
+
510
+ `mnci adopt` is the start of bringing a repository that already has history, tooling and published
511
+ packages under mnci (the plan is #383). So far it is **report mode only and changes nothing**:
512
+
513
+ ```bash
514
+ mnci adopt # what was found, what blocks, what clears each
515
+ mnci adopt --json # the same, as one document
516
+ ```
517
+
518
+ It reads git and the filesystem (no Nx, no install, no network) and reports the package manager, the
519
+ `nx` version, every project manifest, the tags and any release tags stranded under an old project name,
520
+ the CI pipelines and whether they already call `mnci ci`, retired tooling (Prettier, oxfmt, verdaccio)
521
+ and the root files that are yours (`CLAUDE.md`, `AGENTS.md`). A **blocker** (not a git repository,
522
+ uncommitted changes, a package manager other than npm, no projects) makes it exit non-zero; a **warning**
523
+ names the later adoption step that clears it. Each later step is its own issue under #383.
524
+
508
525
  ## `mnci upgrade`: re-applying the overlay to an existing workspace
509
526
 
510
527
  Every fix to `workspace-overlay/overlay.use-case.ts` — a release-config correction, a CI guard rewritten,
package/dist/cli.js CHANGED
@@ -8485,11 +8485,146 @@ function runDoctor(workspaceRoot, options = {}) {
8485
8485
  process.exitCode = 1;
8486
8486
  }
8487
8487
 
8488
+ // src/repository-adoption/inspect-repository.use-case.ts
8489
+ var import_node_fs31 = require("fs");
8490
+ var import_node_path36 = require("path");
8491
+ var LOCKFILES = [
8492
+ ["package-lock.json", "npm"],
8493
+ ["pnpm-lock.yaml", "pnpm"],
8494
+ ["yarn.lock", "yarn"],
8495
+ ["bun.lock", "bun"],
8496
+ ["bun.lockb", "bun"]
8497
+ ];
8498
+ var PERSONAL_FILES = ["CLAUDE.md", "AGENTS.md"];
8499
+ var RETIRED_FILE_PATTERN = /^(?:\.prettierrc.*|\.prettierignore|\.oxfmtrc\.json|oxlint\.config\.ts)$/;
8500
+ var RETIRED_NAMES = ["prettier", "oxfmt", "oxlint", "verdaccio"];
8501
+ function retiredNamesIn(manifest) {
8502
+ const declared = Object.keys({ ...manifest.dependencies, ...manifest.devDependencies });
8503
+ const scripts = Object.values(manifest.scripts ?? {}).join(" ");
8504
+ return RETIRED_NAMES.filter((name) => declared.includes(name) || new RegExp(String.raw`\b${name}\b`).test(scripts));
8505
+ }
8506
+ function inspectRepository(repositoryRoot) {
8507
+ const inside = runCapture("git", ["rev-parse", "--is-inside-work-tree"], repositoryRoot);
8508
+ const isGitRepo = inside.status === 0 && inside.stdout.trim() === "true";
8509
+ const dirty = isGitRepo && runCapture("git", ["status", "--porcelain"], repositoryRoot).stdout.trim() !== "";
8510
+ const tags = isGitRepo ? runCapture("git", ["tag", "--list"], repositoryRoot).stdout.split(/\r?\n/).filter(Boolean) : [];
8511
+ const manifestPath = (0, import_node_path36.join)(repositoryRoot, "package.json");
8512
+ const manifest = fileExists(manifestPath) ? readJson(manifestPath) : {};
8513
+ const nxJsonPath = (0, import_node_path36.join)(repositoryRoot, "nx.json");
8514
+ const nxJson = fileExists(nxJsonPath) ? readJson(nxJsonPath) : {};
8515
+ const ci = [];
8516
+ const pipelines = [];
8517
+ if ((0, import_node_fs31.existsSync)((0, import_node_path36.join)(repositoryRoot, "azure-pipelines.yml"))) {
8518
+ ci.push("azure");
8519
+ pipelines.push((0, import_node_path36.join)(repositoryRoot, "azure-pipelines.yml"));
8520
+ }
8521
+ const workflows = (0, import_node_path36.join)(repositoryRoot, ".github", "workflows");
8522
+ if ((0, import_node_fs31.existsSync)(workflows)) {
8523
+ const files = (0, import_node_fs31.readdirSync)(workflows).filter((file) => /\.ya?ml$/.test(file));
8524
+ if (files.length > 0) {
8525
+ ci.push("github");
8526
+ pipelines.push(...files.map((file) => (0, import_node_path36.join)(workflows, file)));
8527
+ }
8528
+ }
8529
+ const rootFiles = (0, import_node_fs31.readdirSync)(repositoryRoot);
8530
+ return {
8531
+ isGitRepo,
8532
+ dirty,
8533
+ packageManager: LOCKFILES.find(([file]) => rootFiles.includes(file))?.[1],
8534
+ nxVersion: manifest.devDependencies?.nx ?? manifest.dependencies?.nx,
8535
+ alreadyMnci: nxJson.mnci !== void 0,
8536
+ projects: locateProjects(repositoryRoot).map(({ name, dir, ecosystem }) => ({ name, dir, ecosystem })),
8537
+ tagCount: tags.length,
8538
+ strandedTags: isGitRepo ? locateStrandedReleaseTags(repositoryRoot, nxJson.release?.releaseTag?.pattern) : [],
8539
+ ci,
8540
+ pipelineUsesMnci: pipelines.some((file) => /\bmnci ci\b/.test((0, import_node_fs31.readFileSync)(file, "utf8"))),
8541
+ retiredTooling: [.../* @__PURE__ */ new Set([...rootFiles.filter((file) => RETIRED_FILE_PATTERN.test(file) || file === ".verdaccio"), ...retiredNamesIn(manifest)])],
8542
+ personalFiles: PERSONAL_FILES.filter((file) => rootFiles.includes(file))
8543
+ };
8544
+ }
8545
+
8546
+ // src/repository-adoption/judge-adoption.policy.ts
8547
+ function judgeAdoption(facts) {
8548
+ const findings = [];
8549
+ const blocker = (detail, step) => {
8550
+ findings.push({ severity: "blocker", detail, step });
8551
+ };
8552
+ const warning = (detail, step) => {
8553
+ findings.push({ severity: "warning", detail, step });
8554
+ };
8555
+ if (!facts.isGitRepo) {
8556
+ blocker("this directory is not a git repository", "run `git init` (or clone the repository) so every step can be its own commit");
8557
+ } else if (facts.dirty) {
8558
+ blocker("the working tree has uncommitted changes", "commit or stash them, so adoption starts from a state you can return to");
8559
+ }
8560
+ if (facts.packageManager !== void 0 && facts.packageManager !== "npm") {
8561
+ blocker(`the repository uses ${facts.packageManager}; mnci workspaces are npm workspaces`, "convert to npm first (out of scope for adopt for now)");
8562
+ }
8563
+ if (facts.projects.length === 0) {
8564
+ blocker("no project manifest was found under the repository", "run mnci adopt from the repository root, or `mnci new` for an empty one");
8565
+ }
8566
+ if (facts.alreadyMnci) {
8567
+ warning("nx.json already carries an mnci block, so this is an mnci workspace", "use `mnci upgrade`; adopt is for repositories mnci has not touched");
8568
+ }
8569
+ if (facts.nxVersion === void 0) {
8570
+ warning("Nx is not installed", "the toolchain step installs the Nx family at the version mnci supports (#380)");
8571
+ }
8572
+ if (facts.strandedTags.length > 0) {
8573
+ warning(
8574
+ `${facts.strandedTags.length} project(s) have release tags only under an old name (${facts.strandedTags.map((entry) => entry.project).join(", ")})`,
8575
+ "the tags step creates the baseline tags before the first release (#379)"
8576
+ );
8577
+ }
8578
+ if (facts.retiredTooling.length > 0) {
8579
+ warning(`retired tooling is present: ${facts.retiredTooling.join(", ")}`, "the toolchain step removes it (#380)");
8580
+ }
8581
+ if (facts.ci.length > 0 && !facts.pipelineUsesMnci) {
8582
+ warning(`an existing ${facts.ci.join(" and ")} pipeline does not call mnci`, "the pipeline step moves its custom steps into slots (#381)");
8583
+ }
8584
+ if (facts.personalFiles.length > 0) {
8585
+ warning(`${facts.personalFiles.join(", ")} exist and are yours`, "adoption leaves them alone; only a block marked by Nx is ever removed");
8586
+ }
8587
+ const ordered = [...findings.filter((finding) => finding.severity === "blocker"), ...findings.filter((finding) => finding.severity === "warning")];
8588
+ return { ready: ordered.every((finding) => finding.severity !== "blocker"), facts, findings: ordered };
8589
+ }
8590
+
8591
+ // src/repository-adoption/run-adopt.use-case.ts
8592
+ function reportAdoption(repositoryRoot) {
8593
+ return judgeAdoption(inspectRepository(repositoryRoot));
8594
+ }
8595
+ function runAdopt(repositoryRoot, options = {}) {
8596
+ const report = reportAdoption(repositoryRoot);
8597
+ if (!report.ready) {
8598
+ process.exitCode = 1;
8599
+ }
8600
+ if (options.json === true) {
8601
+ printJson(report);
8602
+ return;
8603
+ }
8604
+ const { facts } = report;
8605
+ logger.info(`${facts.projects.length} project(s), ${facts.tagCount} tag(s), ${facts.packageManager ?? "no lockfile"}, nx ${facts.nxVersion ?? "not installed"}`);
8606
+ for (const finding of report.findings) {
8607
+ const line = `${finding.severity === "blocker" ? "BLOCKER" : "warning"}: ${finding.detail}
8608
+ next: ${finding.step}`;
8609
+ if (finding.severity === "blocker") {
8610
+ logger.error(line);
8611
+ } else {
8612
+ logger.warn(line);
8613
+ }
8614
+ }
8615
+ if (report.ready) {
8616
+ logger.success("Nothing blocks adoption. This was a read-only report; nothing was changed.");
8617
+ } else {
8618
+ logger.error("Adoption is blocked. Clear the blockers above and run the report again.");
8619
+ }
8620
+ }
8621
+
8488
8622
  // src/command-catalog/command-groups.config.ts
8489
8623
  var COMMAND_GROUPS = {
8490
8624
  new: "workspace",
8491
8625
  upgrade: "workspace",
8492
8626
  doctor: "workspace",
8627
+ adopt: "inspect",
8493
8628
  add: "projects",
8494
8629
  install: "dependencies",
8495
8630
  sync: "dependencies",
@@ -8555,11 +8690,11 @@ function runCommands(program, options) {
8555
8690
  }
8556
8691
 
8557
8692
  // src/workspace-info/list-projects.use-case.ts
8558
- var import_node_path36 = require("path");
8693
+ var import_node_path37 = require("path");
8559
8694
  var TYPE_TAG_PREFIX = "type:";
8560
8695
  function listProjects(workspaceRoot) {
8561
8696
  return locateProjects(workspaceRoot).map((location) => {
8562
- const projectJsonPath = (0, import_node_path36.join)(workspaceRoot, location.dir, "project.json");
8697
+ const projectJsonPath = (0, import_node_path37.join)(workspaceRoot, location.dir, "project.json");
8563
8698
  const project = fileExists(projectJsonPath) ? readJson(projectJsonPath) : {};
8564
8699
  const typeTag = (project.tags ?? []).find((tag) => tag.startsWith(TYPE_TAG_PREFIX));
8565
8700
  return {
@@ -8572,7 +8707,7 @@ function listProjects(workspaceRoot) {
8572
8707
  }).toSorted((a, b) => a.dir.localeCompare(b.dir));
8573
8708
  }
8574
8709
  function runProjects(workspaceRoot, options) {
8575
- if (!fileExists((0, import_node_path36.join)(workspaceRoot, "nx.json"))) {
8710
+ if (!fileExists((0, import_node_path37.join)(workspaceRoot, "nx.json"))) {
8576
8711
  throw new Error("No nx.json found here. Run `projects` from the workspace root.");
8577
8712
  }
8578
8713
  const projects = listProjects(workspaceRoot);
@@ -8586,11 +8721,11 @@ function runProjects(workspaceRoot, options) {
8586
8721
  }
8587
8722
 
8588
8723
  // src/workspace-info/read-workspace-info.use-case.ts
8589
- var import_node_path38 = require("path");
8724
+ var import_node_path39 = require("path");
8590
8725
 
8591
8726
  // src/cli-version/check-for-update.use-case.ts
8592
- var import_node_fs31 = require("fs");
8593
- var import_node_path37 = require("path");
8727
+ var import_node_fs32 = require("fs");
8728
+ var import_node_path38 = require("path");
8594
8729
  var import_cross_spawn3 = __toESM(require("cross-spawn"));
8595
8730
  var PACKAGE_NAME2 = "@mnci/cli";
8596
8731
  var REGISTRY_TIMEOUT_MS = 2e3;
@@ -8649,8 +8784,8 @@ function releaseTriple(version) {
8649
8784
  }
8650
8785
  function readCliVersion(packageDirectory) {
8651
8786
  try {
8652
- const manifestPath = (0, import_node_path37.join)(packageDirectory, "..", "package.json");
8653
- const manifest = JSON.parse((0, import_node_fs31.readFileSync)(manifestPath, "utf8"));
8787
+ const manifestPath = (0, import_node_path38.join)(packageDirectory, "..", "package.json");
8788
+ const manifest = JSON.parse((0, import_node_fs32.readFileSync)(manifestPath, "utf8"));
8654
8789
  return manifest.version ?? "0.0.0";
8655
8790
  } catch {
8656
8791
  return "0.0.0";
@@ -8660,7 +8795,7 @@ function readCliVersion(packageDirectory) {
8660
8795
  // src/workspace-info/read-workspace-info.use-case.ts
8661
8796
  function readWorkspaceInfo(workspaceRoot, cliVersion) {
8662
8797
  const latest = fetchLatestVersion() ?? null;
8663
- const isWorkspace = fileExists((0, import_node_path38.join)(workspaceRoot, "nx.json"));
8798
+ const isWorkspace = fileExists((0, import_node_path39.join)(workspaceRoot, "nx.json"));
8664
8799
  return {
8665
8800
  cli: {
8666
8801
  version: cliVersion,
@@ -8681,8 +8816,8 @@ function runInfo(workspaceRoot, cliVersion, options) {
8681
8816
  }
8682
8817
 
8683
8818
  // src/workspace-creation/adopt-directory.use-case.ts
8684
- var import_node_fs32 = require("fs");
8685
- var import_node_path39 = require("path");
8819
+ var import_node_fs33 = require("fs");
8820
+ var import_node_path40 = require("path");
8686
8821
  var NEVER_COPIED = /* @__PURE__ */ new Set([".git", "node_modules"]);
8687
8822
  var AGENT_AND_CI_ENTRIES = [
8688
8823
  "AGENTS.md",
@@ -8713,13 +8848,13 @@ var PREFLIGHT_BLOCKERS = [
8713
8848
  "packages"
8714
8849
  ];
8715
8850
  function assertAdoptableDirectory(target) {
8716
- if (!(0, import_node_fs32.existsSync)(target)) {
8851
+ if (!(0, import_node_fs33.existsSync)(target)) {
8717
8852
  throw new Error(`--into '${target}' does not exist. Create or clone it first.`);
8718
8853
  }
8719
- if (!(0, import_node_fs32.statSync)(target).isDirectory()) {
8854
+ if (!(0, import_node_fs33.statSync)(target).isDirectory()) {
8720
8855
  throw new Error(`--into '${target}' is not a directory.`);
8721
8856
  }
8722
- const blockers = PREFLIGHT_BLOCKERS.filter((entry) => (0, import_node_fs32.existsSync)((0, import_node_path39.join)(target, entry)));
8857
+ const blockers = PREFLIGHT_BLOCKERS.filter((entry) => (0, import_node_fs33.existsSync)((0, import_node_path40.join)(target, entry)));
8723
8858
  if (blockers.length > 0) {
8724
8859
  throw new Error(
8725
8860
  `--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.`
@@ -8727,15 +8862,15 @@ function assertAdoptableDirectory(target) {
8727
8862
  }
8728
8863
  }
8729
8864
  function mergeGitignore(generatedFile, targetFile) {
8730
- const existing = (0, import_node_fs32.readFileSync)(targetFile, "utf8");
8865
+ const existing = (0, import_node_fs33.readFileSync)(targetFile, "utf8");
8731
8866
  const present = new Set(existing.split("\n").map((line) => line.trim()));
8732
- const additions = (0, import_node_fs32.readFileSync)(generatedFile, "utf8").split("\n").map((line) => line.trimEnd()).filter((line) => {
8867
+ const additions = (0, import_node_fs33.readFileSync)(generatedFile, "utf8").split("\n").map((line) => line.trimEnd()).filter((line) => {
8733
8868
  const trimmed = line.trim();
8734
8869
  return trimmed !== "" && !trimmed.startsWith("#") && !present.has(trimmed);
8735
8870
  });
8736
8871
  if (additions.length === 0) return false;
8737
8872
  const separator = existing.endsWith("\n") ? "" : "\n";
8738
- (0, import_node_fs32.writeFileSync)(
8873
+ (0, import_node_fs33.writeFileSync)(
8739
8874
  targetFile,
8740
8875
  `${existing}${separator}
8741
8876
  # Added by mnci (Nx workspace)
@@ -8745,9 +8880,9 @@ ${additions.join("\n")}
8745
8880
  return true;
8746
8881
  }
8747
8882
  function adoptGeneratedWorkspace(generatedRoot, target) {
8748
- const entries = (0, import_node_fs32.readdirSync)(generatedRoot).filter((entry) => !NEVER_COPIED.has(entry));
8883
+ const entries = (0, import_node_fs33.readdirSync)(generatedRoot).filter((entry) => !NEVER_COPIED.has(entry));
8749
8884
  const collisions = entries.filter(
8750
- (entry) => (0, import_node_fs32.existsSync)((0, import_node_path39.join)(target, entry)) && !EXISTING_WINS.has(entry) && entry !== ".gitignore"
8885
+ (entry) => (0, import_node_fs33.existsSync)((0, import_node_path40.join)(target, entry)) && !EXISTING_WINS.has(entry) && entry !== ".gitignore"
8751
8886
  );
8752
8887
  if (collisions.length > 0) {
8753
8888
  throw new Error(
@@ -8756,28 +8891,28 @@ function adoptGeneratedWorkspace(generatedRoot, target) {
8756
8891
  }
8757
8892
  const result = { copied: [], kept: [], mergedGitignore: false };
8758
8893
  for (const entry of entries) {
8759
- const from = (0, import_node_path39.join)(generatedRoot, entry);
8760
- const to = (0, import_node_path39.join)(target, entry);
8761
- if (entry === ".gitignore" && (0, import_node_fs32.existsSync)(to)) {
8894
+ const from = (0, import_node_path40.join)(generatedRoot, entry);
8895
+ const to = (0, import_node_path40.join)(target, entry);
8896
+ if (entry === ".gitignore" && (0, import_node_fs33.existsSync)(to)) {
8762
8897
  result.mergedGitignore = mergeGitignore(from, to);
8763
8898
  result.kept.push(entry);
8764
8899
  continue;
8765
8900
  }
8766
- if ((0, import_node_fs32.existsSync)(to)) {
8901
+ if ((0, import_node_fs33.existsSync)(to)) {
8767
8902
  result.kept.push(entry);
8768
8903
  continue;
8769
8904
  }
8770
- (0, import_node_fs32.cpSync)(from, to, { recursive: true });
8905
+ (0, import_node_fs33.cpSync)(from, to, { recursive: true });
8771
8906
  result.copied.push(entry);
8772
8907
  }
8773
- (0, import_node_fs32.rmSync)(generatedRoot, { recursive: true, force: true });
8908
+ (0, import_node_fs33.rmSync)(generatedRoot, { recursive: true, force: true });
8774
8909
  return result;
8775
8910
  }
8776
8911
 
8777
8912
  // src/workspace-creation/create-workspace.use-case.ts
8778
- var import_node_fs33 = require("fs");
8913
+ var import_node_fs34 = require("fs");
8779
8914
  var import_node_os2 = require("os");
8780
- var import_node_path40 = require("path");
8915
+ var import_node_path41 = require("path");
8781
8916
  function nxCloudProviderValue(ci) {
8782
8917
  return ci === "azure" ? "azure" : "github";
8783
8918
  }
@@ -8820,9 +8955,9 @@ async function resolveRegistry(options) {
8820
8955
  return await promptRegistry();
8821
8956
  }
8822
8957
  async function runNew(name, options) {
8823
- const adoptTarget = options.into === void 0 ? void 0 : (0, import_node_path40.resolve)(process.cwd(), options.into);
8958
+ const adoptTarget = options.into === void 0 ? void 0 : (0, import_node_path41.resolve)(process.cwd(), options.into);
8824
8959
  if (adoptTarget !== void 0) assertAdoptableDirectory(adoptTarget);
8825
- const workspaceName = name ?? (adoptTarget === void 0 ? await promptText("Workspace name") : (0, import_node_path40.basename)(adoptTarget));
8960
+ const workspaceName = name ?? (adoptTarget === void 0 ? await promptText("Workspace name") : (0, import_node_path41.basename)(adoptTarget));
8826
8961
  assertValidProjectName(workspaceName, "Workspace name");
8827
8962
  const scope = options.scope ?? (options.yes ? `@${workspaceName}` : await promptText("npm scope for publishable packages", `@${workspaceName}`));
8828
8963
  const registry = await resolveRegistry(options);
@@ -8835,8 +8970,8 @@ async function runNew(name, options) {
8835
8970
  const npmAuth = resolveNpmAuth(options.npmAuth, registry, ci);
8836
8971
  const stack = await resolveStack(options);
8837
8972
  const nxCloud = options.nxCloud ?? (options.yes ? false : await promptNxCloud());
8838
- const stagingParent = adoptTarget === void 0 ? process.cwd() : (0, import_node_fs33.mkdtempSync)((0, import_node_path40.join)((0, import_node_os2.tmpdir)(), "mnci-new-"));
8839
- let workspaceRoot = (0, import_node_path40.join)(stagingParent, workspaceName);
8973
+ const stagingParent = adoptTarget === void 0 ? process.cwd() : (0, import_node_fs34.mkdtempSync)((0, import_node_path41.join)((0, import_node_os2.tmpdir)(), "mnci-new-"));
8974
+ let workspaceRoot = (0, import_node_path41.join)(stagingParent, workspaceName);
8840
8975
  try {
8841
8976
  logger.step(`Creating Nx workspace '${workspaceName}' (preset: ts)`);
8842
8977
  runNpx(
@@ -8859,7 +8994,7 @@ async function runNew(name, options) {
8859
8994
  workspaceRoot = adoptTarget;
8860
8995
  }
8861
8996
  } finally {
8862
- if (adoptTarget !== void 0) (0, import_node_fs33.rmSync)(stagingParent, { recursive: true, force: true });
8997
+ if (adoptTarget !== void 0) (0, import_node_fs34.rmSync)(stagingParent, { recursive: true, force: true });
8863
8998
  }
8864
8999
  logger.step(
8865
9000
  "Applying MoNecromanCI overlay (VS Code workspace, release config, .npmrc, commitlint, pipeline, stack)"
@@ -8869,8 +9004,8 @@ async function runNew(name, options) {
8869
9004
  { workspaceName, scope, registry, agent, variableGroup, ci, stack, ...npmAuth && { npmAuth } },
8870
9005
  logger.detail
8871
9006
  );
8872
- (0, import_node_fs33.rmSync)((0, import_node_path40.join)(workspaceRoot, "node_modules"), { recursive: true, force: true });
8873
- (0, import_node_fs33.rmSync)((0, import_node_path40.join)(workspaceRoot, "package-lock.json"), { force: true });
9007
+ (0, import_node_fs34.rmSync)((0, import_node_path41.join)(workspaceRoot, "node_modules"), { recursive: true, force: true });
9008
+ (0, import_node_fs34.rmSync)((0, import_node_path41.join)(workspaceRoot, "package-lock.json"), { force: true });
8874
9009
  logger.step("Installing the toolchain (commitlint + husky + linting/formatting)");
8875
9010
  const installStatus = runShell(
8876
9011
  "npm",
@@ -8908,12 +9043,12 @@ function projectKindHintLines(kinds = PROJECT_KINDS) {
8908
9043
  }
8909
9044
 
8910
9045
  // src/workspace-creation/interactive-wizard.use-case.ts
8911
- var import_node_path42 = require("path");
9046
+ var import_node_path43 = require("path");
8912
9047
  var import_prompts4 = require("@inquirer/prompts");
8913
9048
 
8914
9049
  // src/workspace-upgrade/upgrade-workspace.use-case.ts
8915
- var import_node_fs34 = require("fs");
8916
- var import_node_path41 = require("path");
9050
+ var import_node_fs35 = require("fs");
9051
+ var import_node_path42 = require("path");
8917
9052
  function resolveRegistry2(options, persisted) {
8918
9053
  if (options.registry === "azure-artifacts" || options.organization && options.artifactsFeed) {
8919
9054
  const persistedAzure = persisted?.kind === "azure-artifacts" ? persisted : void 0;
@@ -8942,7 +9077,7 @@ function resolveWorkspaceName(workspaceRoot, persisted) {
8942
9077
  return persisted.workspaceName;
8943
9078
  }
8944
9079
  try {
8945
- const existing = (0, import_node_fs34.readdirSync)(workspaceRoot).find(
9080
+ const existing = (0, import_node_fs35.readdirSync)(workspaceRoot).find(
8946
9081
  (file) => file.endsWith(".code-workspace") && file !== "undefined.code-workspace"
8947
9082
  );
8948
9083
  if (existing) {
@@ -8950,11 +9085,11 @@ function resolveWorkspaceName(workspaceRoot, persisted) {
8950
9085
  }
8951
9086
  } catch {
8952
9087
  }
8953
- return (0, import_node_path41.basename)(workspaceRoot);
9088
+ return (0, import_node_path42.basename)(workspaceRoot);
8954
9089
  }
8955
9090
  function detectNpmAuth(workspaceRoot) {
8956
9091
  try {
8957
- return (0, import_node_fs34.readFileSync)((0, import_node_path41.join)(workspaceRoot, "azure-pipelines.yml"), "utf8").includes("npmAuthenticate@0") ? "build-identity" : void 0;
9092
+ return (0, import_node_fs35.readFileSync)((0, import_node_path42.join)(workspaceRoot, "azure-pipelines.yml"), "utf8").includes("npmAuthenticate@0") ? "build-identity" : void 0;
8958
9093
  } catch {
8959
9094
  return void 0;
8960
9095
  }
@@ -9001,7 +9136,7 @@ function resolveOverlayOptions(workspaceRoot, options, persisted) {
9001
9136
  };
9002
9137
  }
9003
9138
  function runUpgrade(workspaceRoot, options) {
9004
- if (!fileExists((0, import_node_path41.join)(workspaceRoot, "nx.json"))) {
9139
+ if (!fileExists((0, import_node_path42.join)(workspaceRoot, "nx.json"))) {
9005
9140
  throw new Error(
9006
9141
  `No nx.json found in ${workspaceRoot} \u2014 this does not look like an Nx workspace. Run 'mnci upgrade' from the workspace root.`
9007
9142
  );
@@ -9075,7 +9210,7 @@ function runUpgrade(workspaceRoot, options) {
9075
9210
  "Formatting the workspace (eslint --fix) \u2014 the slowest step, minutes on a large workspace"
9076
9211
  );
9077
9212
  runFormatter(workspaceRoot);
9078
- const nxJson = readJson((0, import_node_path41.join)(workspaceRoot, "nx.json"));
9213
+ const nxJson = readJson((0, import_node_path42.join)(workspaceRoot, "nx.json"));
9079
9214
  const stranded = locateStrandedReleaseTags(workspaceRoot, nxJson.release?.releaseTag?.pattern);
9080
9215
  for (const entry of stranded) {
9081
9216
  logger.warn(`${entry.project} has release tags only under ${entry.oldTag}: nx release would restart it from its disk version. Run \`git tag ${entry.newTag} ${entry.oldTag}^{commit}\` before the next release (\`mnci doctor\` lists them all).`);
@@ -9086,7 +9221,7 @@ function runUpgrade(workspaceRoot, options) {
9086
9221
 
9087
9222
  // src/workspace-creation/interactive-wizard.use-case.ts
9088
9223
  async function runInteractive() {
9089
- const inWorkspace = fileExists((0, import_node_path42.join)(process.cwd(), "nx.json"));
9224
+ const inWorkspace = fileExists((0, import_node_path43.join)(process.cwd(), "nx.json"));
9090
9225
  const choiceNew = { name: "Create a new monorepo", value: "new" };
9091
9226
  const choiceAdd = { name: "Add a project to this workspace", value: "add" };
9092
9227
  const choiceUpgrade = {
@@ -9138,6 +9273,9 @@ function buildProgram(cliVersion) {
9138
9273
  ).option("--json", "print the findings as one JSON document, each with its remedy").action((options) => {
9139
9274
  runDoctor(process.cwd(), options);
9140
9275
  });
9276
+ 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; exits non-zero when something blocks").option("--json", "print the report as one JSON document").action((options) => {
9277
+ runAdopt(process.cwd(), options);
9278
+ });
9141
9279
  program.command("ci").addArgument(new import_commander.Argument("<phase>", "which phase of the pipeline to run").choices(CI_PHASES)).description(
9142
9280
  "Run a phase of the CI pipeline here, the same way CI runs it: `verify` is the sync check and then every project (or, in a pull request, the affected ones) through lint, typecheck, test and build. `setup` installs the Python, Go and Flutter toolchains the workspace needs, `audit` fails on a known advisory that has a fix, `pack` builds the per-app artifacts, `release` versions, tags and publishes, and `native` builds the apps that need a C toolchain on this OS. Exits non-zero when it fails"
9143
9281
  ).action(async (phase) => {