@mnci/cli 4.17.6 → 4.19.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
@@ -2342,7 +2342,7 @@ function runCiPhase(phase, workspaceRoot, dependencies = {}) {
2342
2342
  }
2343
2343
 
2344
2344
  // src/project-scaffolding/add-project.use-case.ts
2345
- var import_node_path23 = require("path");
2345
+ var import_node_path24 = require("path");
2346
2346
  var import_prompts3 = require("@inquirer/prompts");
2347
2347
 
2348
2348
  // src/project-name/project-name.validator.ts
@@ -2971,13 +2971,47 @@ function isNxGoPluginRegistered(workspaceRoot) {
2971
2971
  }
2972
2972
  return (readJson(nxJsonPath).plugins ?? []).some((entry) => (typeof entry === "string" ? entry : entry.plugin) === NX_GO_PLUGIN);
2973
2973
  }
2974
+ function modulePrefixFromRemoteUrl(url) {
2975
+ const trimmed = url.trim();
2976
+ const scpLike = /^[^@/]+@([^:]+):(.+)$/.exec(trimmed);
2977
+ const withScheme = /^[a-z][\w+.-]*:\/\/(?:[^@/]+@)?(.+)$/i.exec(trimmed);
2978
+ let path;
2979
+ if (scpLike) {
2980
+ path = `${scpLike[1]}/${scpLike[2]}`;
2981
+ } else if (withScheme) {
2982
+ path = withScheme[1];
2983
+ }
2984
+ if (path === void 0) {
2985
+ return void 0;
2986
+ }
2987
+ const cleaned = path.replace(/\.git$/, "").replace(/\/+$/, "");
2988
+ return cleaned === "" ? void 0 : cleaned;
2989
+ }
2990
+ function goModulePrefix(workspaceRoot) {
2991
+ const result = runCapture("git", ["remote", "get-url", "origin"], workspaceRoot);
2992
+ return result.status === 0 ? modulePrefixFromRemoteUrl(result.stdout) : void 0;
2993
+ }
2974
2994
  function registerNxGoPlugin(workspaceRoot) {
2975
2995
  const nxJsonPath = (0, import_node_path7.join)(workspaceRoot, "nx.json");
2976
- if (!fileExists(nxJsonPath) || isNxGoPluginRegistered(workspaceRoot)) {
2996
+ if (!fileExists(nxJsonPath)) {
2977
2997
  return false;
2978
2998
  }
2999
+ const modulePrefix = goModulePrefix(workspaceRoot);
3000
+ const entry = modulePrefix === void 0 ? NX_GO_PLUGIN : { plugin: NX_GO_PLUGIN, options: { modulePrefix } };
2979
3001
  const nxJson = readJson(nxJsonPath);
2980
- writeFileEnsured(nxJsonPath, toJson({ ...nxJson, plugins: [...nxJson.plugins ?? [], NX_GO_PLUGIN] }));
3002
+ const plugins = [...nxJson.plugins ?? []];
3003
+ const index = plugins.findIndex((plugin) => (typeof plugin === "string" ? plugin : plugin.plugin) === NX_GO_PLUGIN);
3004
+ if (index === -1) {
3005
+ plugins.push(entry);
3006
+ } else {
3007
+ const current = plugins[index];
3008
+ const currentPrefix = typeof current === "string" ? void 0 : current.options?.modulePrefix;
3009
+ if (modulePrefix === void 0 || currentPrefix === modulePrefix) {
3010
+ return false;
3011
+ }
3012
+ plugins[index] = entry;
3013
+ }
3014
+ writeFileEnsured(nxJsonPath, toJson({ ...nxJson, plugins }));
2981
3015
  return true;
2982
3016
  }
2983
3017
 
@@ -3322,21 +3356,36 @@ function ensureNxGoPlugin(workspaceRoot) {
3322
3356
  }
3323
3357
  }
3324
3358
  function ensureGoModule(workspaceRoot) {
3325
- if (fileExists((0, import_node_path10.join)(workspaceRoot, "go.mod"))) {
3359
+ if (fileExists((0, import_node_path10.join)(workspaceRoot, "go.work")) || fileExists((0, import_node_path10.join)(workspaceRoot, "go.mod"))) {
3326
3360
  return;
3327
3361
  }
3328
- logger.step("Bootstrapping the workspace Go module (single root go.mod)");
3362
+ logger.step("Bootstrapping the Go workspace (go.work, one module per project)");
3329
3363
  runNx(["g", "@nx-go/nx-go:init", "--no-interactive"], workspaceRoot);
3330
- runNx(["g", "@nx-go/nx-go:convert-to-one-mod", "--no-interactive"], workspaceRoot);
3331
3364
  }
3332
- function goModulePath(workspaceRoot) {
3365
+ function readGoModModule(goModPath) {
3333
3366
  try {
3334
- const contents = (0, import_node_fs9.readFileSync)((0, import_node_path10.join)(workspaceRoot, "go.mod"), "utf8");
3335
- return /^module\s+(\S+)/m.exec(contents)?.[1];
3367
+ return /^module\s+(\S+)/m.exec((0, import_node_fs9.readFileSync)(goModPath, "utf8"))?.[1];
3336
3368
  } catch {
3337
3369
  return void 0;
3338
3370
  }
3339
3371
  }
3372
+ function goModulePathFor(workspaceRoot, projectDir) {
3373
+ const projectModule = readGoModModule((0, import_node_path10.join)(workspaceRoot, projectDir, "go.mod"));
3374
+ if (projectModule !== void 0) {
3375
+ return projectModule;
3376
+ }
3377
+ const rootModule = readGoModModule((0, import_node_path10.join)(workspaceRoot, "go.mod"));
3378
+ return rootModule === void 0 ? void 0 : `${rootModule}/${projectDir}`;
3379
+ }
3380
+ function setGoModulePath(workspaceRoot, projectDir) {
3381
+ const prefix = goModulePrefix(workspaceRoot);
3382
+ const goModPath = (0, import_node_path10.join)(workspaceRoot, projectDir, "go.mod");
3383
+ if (prefix === void 0 || !fileExists(goModPath)) {
3384
+ return;
3385
+ }
3386
+ const rewritten = (0, import_node_fs9.readFileSync)(goModPath, "utf8").replace(/^module\s+\S+/m, () => `module ${prefix}/${projectDir}`);
3387
+ writeFileEnsured(goModPath, rewritten);
3388
+ }
3340
3389
  function goTestTarget() {
3341
3390
  return { executor: "@nx-go/nx-go:test" };
3342
3391
  }
@@ -3533,6 +3582,7 @@ function addGoApp(workspaceRoot, name, options = {}) {
3533
3582
  ],
3534
3583
  workspaceRoot
3535
3584
  );
3585
+ setGoModulePath(workspaceRoot, `apps/${name}`);
3536
3586
  addProjectJsonTargets((0, import_node_path10.join)(workspaceRoot, "apps", name, "project.json"), {
3537
3587
  build: goBuildTarget(name),
3538
3588
  test: goTestTarget(),
@@ -3571,6 +3621,7 @@ function addGoFunctionApp(workspaceRoot, name) {
3571
3621
  ],
3572
3622
  workspaceRoot
3573
3623
  );
3624
+ setGoModulePath(workspaceRoot, `apps/${name}`);
3574
3625
  addProjectJsonTargets((0, import_node_path10.join)(workspaceRoot, "apps", name, "project.json"), {
3575
3626
  "build": goBuildTarget(name),
3576
3627
  "test": goTestTarget(),
@@ -3594,15 +3645,16 @@ function addGoLib(workspaceRoot, name) {
3594
3645
  ],
3595
3646
  workspaceRoot
3596
3647
  );
3648
+ setGoModulePath(workspaceRoot, `packages/${name}`);
3597
3649
  addProjectJsonTargets((0, import_node_path10.join)(workspaceRoot, "packages", name, "project.json"), {
3598
3650
  test: goTestTarget(),
3599
3651
  lint: goLintTarget()
3600
3652
  });
3601
3653
  const slice = reshapeGoLibraryScaffold((0, import_node_path10.join)(workspaceRoot, "packages", name), name);
3602
3654
  registerProjectCommands(workspaceRoot, name, { build: false });
3603
- const module2 = goModulePath(workspaceRoot);
3655
+ const module2 = goModulePathFor(workspaceRoot, `packages/${name}`);
3604
3656
  if (module2) {
3605
- logger.step(`Import its starter slice as ${module2}/packages/${name}/${slice}`);
3657
+ logger.step(`Import its starter slice as ${module2}/${slice}`);
3606
3658
  }
3607
3659
  }
3608
3660
  function addGoInternalLib(workspaceRoot, name) {
@@ -3618,15 +3670,16 @@ function addGoInternalLib(workspaceRoot, name) {
3618
3670
  ],
3619
3671
  workspaceRoot
3620
3672
  );
3673
+ setGoModulePath(workspaceRoot, `libs/${name}`);
3621
3674
  addProjectJsonTargets((0, import_node_path10.join)(workspaceRoot, "libs", name, "project.json"), {
3622
3675
  test: goTestTarget(),
3623
3676
  lint: goLintTarget()
3624
3677
  });
3625
3678
  const slice = reshapeGoLibraryScaffold((0, import_node_path10.join)(workspaceRoot, "libs", name), name);
3626
3679
  registerProjectCommands(workspaceRoot, name, { build: false });
3627
- const module2 = goModulePath(workspaceRoot);
3680
+ const module2 = goModulePathFor(workspaceRoot, `libs/${name}`);
3628
3681
  if (module2) {
3629
- logger.step(`Import its starter slice as ${module2}/libs/${name}/${slice}`);
3682
+ logger.step(`Import its starter slice as ${module2}/${slice}`);
3630
3683
  }
3631
3684
  }
3632
3685
 
@@ -4946,7 +4999,8 @@ function addVscodeExtension(workspaceRoot, name, stack, options = {}) {
4946
4999
  refreshLockFile(workspaceRoot, name);
4947
5000
  }
4948
5001
 
4949
- // src/dependency-management/sync-dependencies.use-case.ts
5002
+ // src/dependency-management/install-dependencies.use-case.ts
5003
+ var import_node_fs17 = require("fs");
4950
5004
  var import_node_path21 = require("path");
4951
5005
 
4952
5006
  // src/dependency-management/manifest.repository.ts
@@ -5277,6 +5331,27 @@ function collectInventory(workspaceRoot, ecosystems = ECOSYSTEMS) {
5277
5331
  }
5278
5332
  return inventory;
5279
5333
  }
5334
+ var PROJECT_GLOBS = {
5335
+ ...MANIFEST_GLOBS,
5336
+ go: ["apps/*/go.mod", "libs/*/go.mod", "packages/*/go.mod"]
5337
+ };
5338
+ function locateProjects(workspaceRoot) {
5339
+ const locations = [];
5340
+ for (const ecosystem of ECOSYSTEMS) {
5341
+ const relativePaths = (0, import_node_fs16.globSync)(PROJECT_GLOBS[ecosystem], { cwd: workspaceRoot });
5342
+ for (const relativePath of relativePaths) {
5343
+ const posix = toPosix(relativePath);
5344
+ const dir = posix.slice(0, posix.lastIndexOf("/"));
5345
+ locations.push({
5346
+ ecosystem,
5347
+ dir,
5348
+ name: dir.slice(dir.lastIndexOf("/") + 1),
5349
+ manifestPath: (0, import_node_path20.join)(workspaceRoot, relativePath)
5350
+ });
5351
+ }
5352
+ }
5353
+ return locations;
5354
+ }
5280
5355
  function rewriteSpec(site, spec) {
5281
5356
  if (!site.rewritable || spec === site.spec) {
5282
5357
  return false;
@@ -5326,6 +5401,41 @@ function replacePipSpec(content, name, spec) {
5326
5401
  (match, lead, matched) => `${lead}${matched}${spec}`
5327
5402
  );
5328
5403
  }
5404
+ function addPipDependency(content, entry) {
5405
+ const name = parseRequirement(entry)?.name;
5406
+ const already = name !== void 0 && pyprojectDependencies(content).some((existing) => parseRequirement(existing)?.name === name);
5407
+ if (already) {
5408
+ return content;
5409
+ }
5410
+ const newline = content.includes("\r\n") ? "\r\n" : "\n";
5411
+ const lines = content.split(/\r?\n/);
5412
+ const quoted = `"${entry}"`;
5413
+ let inProjectTable = false;
5414
+ for (let index = 0; index < lines.length; index++) {
5415
+ const trimmed = lines[index].trim();
5416
+ if (trimmed.startsWith("[")) {
5417
+ inProjectTable = trimmed === "[project]";
5418
+ continue;
5419
+ }
5420
+ if (!inProjectTable) {
5421
+ continue;
5422
+ }
5423
+ const opener = /^(\s*)dependencies\s*=\s*\[(.*)$/.exec(lines[index]);
5424
+ if (!opener) {
5425
+ continue;
5426
+ }
5427
+ const [, indent, rest] = opener;
5428
+ if (rest.includes("]")) {
5429
+ const inner = rest.slice(0, rest.lastIndexOf("]")).trim().replace(/,+\s*$/, "");
5430
+ const list = inner.length > 0 ? `${inner}, ${quoted}` : quoted;
5431
+ lines[index] = `${indent}dependencies = [${list}]`;
5432
+ } else {
5433
+ lines.splice(index + 1, 0, `${indent} ${quoted},`);
5434
+ }
5435
+ return lines.join(newline);
5436
+ }
5437
+ return content;
5438
+ }
5329
5439
  function replacePubSpec(content, name, spec) {
5330
5440
  return content.replaceAll(
5331
5441
  new RegExp(String.raw`^(\s+${escapeForRegex(name)}:[^\S\n]*)\S[^\n]*$`, "gm"),
@@ -5397,6 +5507,158 @@ function resolvedVersion(workspaceRoot, ecosystem, name) {
5397
5507
  return void 0;
5398
5508
  }
5399
5509
 
5510
+ // src/dependency-management/install-dependencies.use-case.ts
5511
+ var WHOLE_WORKSPACE_INSTALL = [
5512
+ ["npm", "npm", ["install"]],
5513
+ ["pip", "npm", ["run", "python:install"]],
5514
+ ["pub", "flutter", ["pub", "get"]],
5515
+ ["nuget", "npx", ["nx", "run-many", "-t", "restore"]],
5516
+ ["go", "go", ["work", "sync"]]
5517
+ ];
5518
+ function runInstall(workspaceRoot, packages, options = {}) {
5519
+ const targets = options.workspace ?? [];
5520
+ if (targets.length === 0) {
5521
+ if (packages.length > 0) {
5522
+ logger.error(
5523
+ "Adding a package needs a target project \u2014 'mnci install -w <project> <package>'. A bare 'mnci install' (no package) installs the whole workspace"
5524
+ );
5525
+ process.exitCode = 1;
5526
+ return;
5527
+ }
5528
+ installEverything(workspaceRoot);
5529
+ return;
5530
+ }
5531
+ const locations = locateProjects(workspaceRoot);
5532
+ for (const target of targets) {
5533
+ const location = resolveTarget(locations, target);
5534
+ if (location === void 0) {
5535
+ logger.error(
5536
+ `No project '${target}' \u2014 looked for a manifest under apps/, libs/, packages/ (and python-packages/). Name it by directory (apps/${target}) or by its basename`
5537
+ );
5538
+ process.exitCode = 1;
5539
+ continue;
5540
+ }
5541
+ installInto(workspaceRoot, location, packages, Boolean(options.saveDev));
5542
+ }
5543
+ }
5544
+ function resolveTarget(locations, target) {
5545
+ const byDir = locations.find((location) => location.dir === target);
5546
+ if (byDir !== void 0) {
5547
+ return byDir;
5548
+ }
5549
+ const byName = locations.filter((location) => location.name === target);
5550
+ if (byName.length > 1) {
5551
+ logger.error(
5552
+ `'${target}' matches ${byName.length} projects (${byName.map((match) => match.dir).join(", ")}) \u2014 name it by directory`
5553
+ );
5554
+ return void 0;
5555
+ }
5556
+ return byName[0];
5557
+ }
5558
+ function installInto(workspaceRoot, location, packages, saveDev) {
5559
+ const projectDir = (0, import_node_path21.join)(workspaceRoot, location.dir);
5560
+ switch (location.ecosystem) {
5561
+ case "npm": {
5562
+ const flags = saveDev ? ["--save-dev"] : [];
5563
+ shell(workspaceRoot, "npm", ["install", ...packages, ...flags, "-w", location.dir], location);
5564
+ return;
5565
+ }
5566
+ case "go": {
5567
+ shell(projectDir, "go", packages.length > 0 ? ["get", ...packages] : ["mod", "download"], location);
5568
+ if (packages.length > 0) {
5569
+ runShell("go", ["work", "sync"], workspaceRoot);
5570
+ }
5571
+ return;
5572
+ }
5573
+ case "nuget": {
5574
+ if (packages.length === 0) {
5575
+ shell(workspaceRoot, "dotnet", ["restore", location.manifestPath], location);
5576
+ return;
5577
+ }
5578
+ for (const specifier of packages) {
5579
+ const [name, version] = splitAtLast(specifier, "@");
5580
+ const versionFlag = version === void 0 ? [] : ["--version", version];
5581
+ shell(workspaceRoot, "dotnet", ["add", location.manifestPath, "package", name, ...versionFlag], location);
5582
+ }
5583
+ return;
5584
+ }
5585
+ case "pub": {
5586
+ const devFlag = saveDev ? ["--dev"] : [];
5587
+ shell(
5588
+ projectDir,
5589
+ "flutter",
5590
+ packages.length > 0 ? ["pub", "add", ...devFlag, ...packages] : ["pub", "get"],
5591
+ location
5592
+ );
5593
+ return;
5594
+ }
5595
+ case "pip": {
5596
+ if (saveDev) {
5597
+ logger.warn(
5598
+ "pip has no per-project dev-dependency section \u2014 adding to [project].dependencies (dev tools live in the root requirements-dev.txt)"
5599
+ );
5600
+ }
5601
+ for (const specifier of packages) {
5602
+ addPythonDependency(location.manifestPath, specifier);
5603
+ }
5604
+ installEverything(workspaceRoot, ["pip"]);
5605
+ return;
5606
+ }
5607
+ }
5608
+ }
5609
+ function addPythonDependency(manifestPath, specifier) {
5610
+ let content;
5611
+ try {
5612
+ content = (0, import_node_fs17.readFileSync)(manifestPath, "utf8");
5613
+ } catch {
5614
+ logger.warn(`Could not read ${manifestPath} \u2014 skipped ${specifier}`);
5615
+ return;
5616
+ }
5617
+ const updated = addPipDependency(content, specifier);
5618
+ if (updated === content) {
5619
+ logger.info(`${specifier} is already declared \u2014 left as is`);
5620
+ return;
5621
+ }
5622
+ writeFileEnsured(manifestPath, updated);
5623
+ logger.step(`Added ${specifier} to ${manifestPath}`);
5624
+ }
5625
+ function installEverything(workspaceRoot, only) {
5626
+ for (const [ecosystem, command, arguments_] of WHOLE_WORKSPACE_INSTALL) {
5627
+ if (only !== void 0 && !only.includes(ecosystem)) {
5628
+ continue;
5629
+ }
5630
+ if (!ecosystemPresent(workspaceRoot, ecosystem)) {
5631
+ continue;
5632
+ }
5633
+ logger.step(`Installing ${ecosystem} dependencies (${command} ${arguments_.join(" ")})`);
5634
+ if (runShell(command, [...arguments_], workspaceRoot) !== 0) {
5635
+ logger.warn(`${command} ${arguments_.join(" ")} failed \u2014 re-run it once the cause is fixed`);
5636
+ }
5637
+ }
5638
+ }
5639
+ function ecosystemPresent(workspaceRoot, ecosystem) {
5640
+ if (ecosystem === "go") {
5641
+ return fileExists((0, import_node_path21.join)(workspaceRoot, "go.work")) || fileExists((0, import_node_path21.join)(workspaceRoot, "go.mod"));
5642
+ }
5643
+ return hasEcosystem(workspaceRoot, ecosystem);
5644
+ }
5645
+ function shell(cwd, command, arguments_, location) {
5646
+ logger.step(`${location.dir}: ${command} ${arguments_.join(" ")}`);
5647
+ if (runShell(command, [...arguments_], cwd) !== 0) {
5648
+ logger.warn(`${command} ${arguments_.join(" ")} failed in ${location.dir}`);
5649
+ }
5650
+ }
5651
+ function splitAtLast(value, separator) {
5652
+ const at = value.lastIndexOf(separator);
5653
+ if (at <= 0) {
5654
+ return [value, void 0];
5655
+ }
5656
+ return [value.slice(0, at), value.slice(at + 1)];
5657
+ }
5658
+
5659
+ // src/dependency-management/sync-dependencies.use-case.ts
5660
+ var import_node_path22 = require("path");
5661
+
5400
5662
  // src/dependency-management/semver.algorithm.ts
5401
5663
  var UPDATE_KINDS = ["patch", "minor", "major", "non-semver"];
5402
5664
  var UPDATE_KIND_LABELS = {
@@ -5572,7 +5834,7 @@ function describe(finding) {
5572
5834
  return `${finding.name} \u2192 ${finding.target} (${finding.reason}); disagreeing: ${sites}`;
5573
5835
  }
5574
5836
  function runSync(workspaceRoot, options) {
5575
- if (!fileExists((0, import_node_path21.join)(workspaceRoot, "nx.json"))) {
5837
+ if (!fileExists((0, import_node_path22.join)(workspaceRoot, "nx.json"))) {
5576
5838
  throw new Error(
5577
5839
  `No nx.json found in ${workspaceRoot} \u2014 run 'mnci sync' from the workspace root.`
5578
5840
  );
@@ -5622,7 +5884,7 @@ function runSync(workspaceRoot, options) {
5622
5884
  }
5623
5885
 
5624
5886
  // src/dependency-management/update-dependencies.use-case.ts
5625
- var import_node_path22 = require("path");
5887
+ var import_node_path23 = require("path");
5626
5888
  var import_prompts2 = require("@inquirer/prompts");
5627
5889
 
5628
5890
  // src/dependency-management/registry.client.ts
@@ -5905,7 +6167,7 @@ function reinstall(workspaceRoot, ecosystems) {
5905
6167
  function hasPythonInstallScript(workspaceRoot) {
5906
6168
  try {
5907
6169
  const manifest = readJson(
5908
- (0, import_node_path22.join)(workspaceRoot, "package.json")
6170
+ (0, import_node_path23.join)(workspaceRoot, "package.json")
5909
6171
  );
5910
6172
  return manifest.scripts?.["python:install"] !== void 0;
5911
6173
  } catch {
@@ -5921,7 +6183,7 @@ function applyGoUpdates(workspaceRoot, entries) {
5921
6183
  }
5922
6184
  }
5923
6185
  async function runUp(workspaceRoot, options) {
5924
- if (!fileExists((0, import_node_path22.join)(workspaceRoot, "nx.json"))) {
6186
+ if (!fileExists((0, import_node_path23.join)(workspaceRoot, "nx.json"))) {
5925
6187
  throw new Error(`No nx.json found in ${workspaceRoot} \u2014 run 'mnci up' from the workspace root.`);
5926
6188
  }
5927
6189
  const ecosystems = resolveEcosystems(options.ecosystem);
@@ -5997,7 +6259,7 @@ var PROJECT_KINDS = [
5997
6259
  ];
5998
6260
  async function runAdd(kind, name, options) {
5999
6261
  const workspaceRoot = process.cwd();
6000
- if (!fileExists((0, import_node_path23.join)(workspaceRoot, "nx.json"))) {
6262
+ if (!fileExists((0, import_node_path24.join)(workspaceRoot, "nx.json"))) {
6001
6263
  throw new Error("No nx.json found here. Run `add` from the workspace root.");
6002
6264
  }
6003
6265
  const stack = readWorkspaceStack(workspaceRoot);
@@ -6057,8 +6319,8 @@ async function runAdd(kind, name, options) {
6057
6319
  ],
6058
6320
  workspaceRoot
6059
6321
  );
6060
- markPrivate((0, import_node_path23.join)(workspaceRoot, "libs", resolvedName, "package.json"));
6061
- renameScaffoldPlaceholder((0, import_node_path23.join)(workspaceRoot, "libs", resolvedName), resolvedName);
6322
+ markPrivate((0, import_node_path24.join)(workspaceRoot, "libs", resolvedName, "package.json"));
6323
+ renameScaffoldPlaceholder((0, import_node_path24.join)(workspaceRoot, "libs", resolvedName), resolvedName);
6062
6324
  removeGeneratedEslintConfig(workspaceRoot, `libs/${resolvedName}`);
6063
6325
  registerProjectCommands(workspaceRoot, resolvedName, { build: true });
6064
6326
  break;
@@ -6150,7 +6412,7 @@ async function runAdd(kind, name, options) {
6150
6412
  }
6151
6413
  function readWorkspaceStack(workspaceRoot) {
6152
6414
  const nxJson = readJson(
6153
- (0, import_node_path23.join)(workspaceRoot, "nx.json")
6415
+ (0, import_node_path24.join)(workspaceRoot, "nx.json")
6154
6416
  );
6155
6417
  const stack = nxJson.mnci?.stack;
6156
6418
  return {
@@ -6160,15 +6422,15 @@ function readWorkspaceStack(workspaceRoot) {
6160
6422
 
6161
6423
  // src/workspace-diagnostics/check-invariants.use-case.ts
6162
6424
  var import_node_child_process = require("child_process");
6163
- var import_node_fs17 = require("fs");
6164
- var import_node_path24 = require("path");
6425
+ var import_node_fs18 = require("fs");
6426
+ var import_node_path25 = require("path");
6165
6427
  var SUPPORTED_ESLINT_MAJOR = ESLINT_VERSION.replace(/^\D*/, "").split(".", 1)[0];
6166
6428
  function toPosix2(path) {
6167
6429
  return path.replaceAll("\\", "/");
6168
6430
  }
6169
6431
  function checkEslintConfigs(workspaceRoot) {
6170
- const rootConfigs = (0, import_node_fs17.globSync)("eslint.config.{js,mjs,cjs,ts,mts,cts}", { cwd: workspaceRoot });
6171
- const projectConfigs = (0, import_node_fs17.globSync)("{apps,libs,packages}/*/eslint.config.{js,mjs,cjs,ts,mts,cts}", {
6432
+ const rootConfigs = (0, import_node_fs18.globSync)("eslint.config.{js,mjs,cjs,ts,mts,cts}", { cwd: workspaceRoot });
6433
+ const projectConfigs = (0, import_node_fs18.globSync)("{apps,libs,packages}/*/eslint.config.{js,mjs,cjs,ts,mts,cts}", {
6172
6434
  cwd: workspaceRoot
6173
6435
  }).map((config) => toPosix2(config));
6174
6436
  return [
@@ -6188,10 +6450,10 @@ function checkEslintConfigs(workspaceRoot) {
6188
6450
  ];
6189
6451
  }
6190
6452
  function checkEslintEntryPointReachesTheRules(workspaceRoot) {
6191
- const rules = (0, import_node_path24.join)(workspaceRoot, ESLINT_MNCI_FILENAME);
6192
- if (!(0, import_node_fs17.existsSync)(rules)) return [];
6193
- const entryPoint = (0, import_node_path24.join)(workspaceRoot, ESLINT_USER_FILENAME);
6194
- const source = (0, import_node_fs17.existsSync)(entryPoint) ? (0, import_node_fs17.readFileSync)(entryPoint, "utf8") : "";
6453
+ const rules = (0, import_node_path25.join)(workspaceRoot, ESLINT_MNCI_FILENAME);
6454
+ if (!(0, import_node_fs18.existsSync)(rules)) return [];
6455
+ const entryPoint = (0, import_node_path25.join)(workspaceRoot, ESLINT_USER_FILENAME);
6456
+ const source = (0, import_node_fs18.existsSync)(entryPoint) ? (0, import_node_fs18.readFileSync)(entryPoint, "utf8") : "";
6195
6457
  return [
6196
6458
  {
6197
6459
  check: `${ESLINT_USER_FILENAME} imports the mnci rules`,
@@ -6204,7 +6466,7 @@ function checkEslintEntryPointReachesTheRules(workspaceRoot) {
6204
6466
  function declaredDevDependencies(workspaceRoot) {
6205
6467
  try {
6206
6468
  const manifest = readJson(
6207
- (0, import_node_path24.join)(workspaceRoot, "package.json")
6469
+ (0, import_node_path25.join)(workspaceRoot, "package.json")
6208
6470
  );
6209
6471
  return manifest.devDependencies ?? {};
6210
6472
  } catch {
@@ -6212,7 +6474,7 @@ function declaredDevDependencies(workspaceRoot) {
6212
6474
  }
6213
6475
  }
6214
6476
  function checkNoRetiredFormatter(workspaceRoot) {
6215
- const files = RETIRED_FORMATTER_FILES.filter((file) => fileExists((0, import_node_path24.join)(workspaceRoot, file)));
6477
+ const files = RETIRED_FORMATTER_FILES.filter((file) => fileExists((0, import_node_path25.join)(workspaceRoot, file)));
6216
6478
  const declared = ["prettier", "eslint-config-prettier", "oxlint", "oxfmt", "@mnci/oxlint-config"].filter((name) => declaredDevDependencies(workspaceRoot)[name] !== void 0);
6217
6479
  const stale = [...files, ...declared];
6218
6480
  if (stale.length === 0) {
@@ -6238,7 +6500,7 @@ function checkEslintPlugin(nxJson) {
6238
6500
  };
6239
6501
  }
6240
6502
  function checkResolvedEslint(workspaceRoot) {
6241
- const manifestPath = (0, import_node_path24.join)(workspaceRoot, "node_modules/eslint/package.json");
6503
+ const manifestPath = (0, import_node_path25.join)(workspaceRoot, "node_modules/eslint/package.json");
6242
6504
  if (!fileExists(manifestPath)) {
6243
6505
  return void 0;
6244
6506
  }
@@ -6256,11 +6518,11 @@ function checkResolvedEslint(workspaceRoot) {
6256
6518
  }
6257
6519
  }
6258
6520
  function checkNpmrc(workspaceRoot, registry, scope) {
6259
- const npmrcPath = (0, import_node_path24.join)(workspaceRoot, ".npmrc");
6521
+ const npmrcPath = (0, import_node_path25.join)(workspaceRoot, ".npmrc");
6260
6522
  if (!registry || !fileExists(npmrcPath)) {
6261
6523
  return void 0;
6262
6524
  }
6263
- const npmrc = (0, import_node_fs17.readFileSync)(npmrcPath, "utf8");
6525
+ const npmrc = (0, import_node_fs18.readFileSync)(npmrcPath, "utf8");
6264
6526
  if (registry.kind === "npm") {
6265
6527
  return {
6266
6528
  check: ".npmrc authenticates the public registry",
@@ -6277,11 +6539,11 @@ function checkNpmrc(workspaceRoot, registry, scope) {
6277
6539
  };
6278
6540
  }
6279
6541
  function checkNpmrcCredentialResolves(workspaceRoot, environment = process.env) {
6280
- const npmrcPath = (0, import_node_path24.join)(workspaceRoot, ".npmrc");
6542
+ const npmrcPath = (0, import_node_path25.join)(workspaceRoot, ".npmrc");
6281
6543
  if (!fileExists(npmrcPath)) {
6282
6544
  return void 0;
6283
6545
  }
6284
- const npmrc = (0, import_node_fs17.readFileSync)(npmrcPath, "utf8");
6546
+ const npmrc = (0, import_node_fs18.readFileSync)(npmrcPath, "utf8");
6285
6547
  const credentials = npmrc.split("\n").filter((line) => !line.trimStart().startsWith(";") && !line.trimStart().startsWith("#")).filter((line) => /(?:_authToken|_password|_auth)\s*=/u.test(line));
6286
6548
  const referenced = [
6287
6549
  ...new Set(
@@ -6305,13 +6567,13 @@ function checkNpmrcCredentialResolves(workspaceRoot, environment = process.env)
6305
6567
  };
6306
6568
  }
6307
6569
  function checkNpmAuthMatchesPipeline(workspaceRoot, registry) {
6308
- const npmrcPath = (0, import_node_path24.join)(workspaceRoot, ".npmrc");
6309
- const pipelinePath = (0, import_node_path24.join)(workspaceRoot, "azure-pipelines.yml");
6570
+ const npmrcPath = (0, import_node_path25.join)(workspaceRoot, ".npmrc");
6571
+ const pipelinePath = (0, import_node_path25.join)(workspaceRoot, "azure-pipelines.yml");
6310
6572
  if (registry?.kind !== "azure-artifacts" || !fileExists(npmrcPath) || !fileExists(pipelinePath)) {
6311
6573
  return void 0;
6312
6574
  }
6313
- const holdsCredentials = (0, import_node_fs17.readFileSync)(npmrcPath, "utf8").split("\n").filter((line) => !line.trimStart().startsWith(";") && !line.trimStart().startsWith("#")).some((line) => /(?:_authToken|_password|_auth)\s*=/u.test(line));
6314
- const runsTask = (0, import_node_fs17.readFileSync)(pipelinePath, "utf8").includes("npmAuthenticate@0");
6575
+ const holdsCredentials = (0, import_node_fs18.readFileSync)(npmrcPath, "utf8").split("\n").filter((line) => !line.trimStart().startsWith(";") && !line.trimStart().startsWith("#")).some((line) => /(?:_authToken|_password|_auth)\s*=/u.test(line));
6576
+ const runsTask = (0, import_node_fs18.readFileSync)(pipelinePath, "utf8").includes("npmAuthenticate@0");
6315
6577
  if (!holdsCredentials && !runsTask) {
6316
6578
  return {
6317
6579
  check: "npm auth: .npmrc and azure-pipelines.yml agree",
@@ -6356,13 +6618,13 @@ function npmjsCredentialShape(credentials, environment) {
6356
6618
  }
6357
6619
  function checkVersionActions(workspaceRoot) {
6358
6620
  const candidates = [
6359
- ...(0, import_node_fs17.globSync)("packages/*/pubspec.yaml", { cwd: workspaceRoot }),
6360
- ...(0, import_node_fs17.globSync)("packages/*/*.csproj", { cwd: workspaceRoot }),
6361
- ...(0, import_node_fs17.globSync)("python-packages/*/pyproject.toml", { cwd: workspaceRoot })
6621
+ ...(0, import_node_fs18.globSync)("packages/*/pubspec.yaml", { cwd: workspaceRoot }),
6622
+ ...(0, import_node_fs18.globSync)("packages/*/*.csproj", { cwd: workspaceRoot }),
6623
+ ...(0, import_node_fs18.globSync)("python-packages/*/pyproject.toml", { cwd: workspaceRoot })
6362
6624
  ].map((manifest) => toPosix2(manifest));
6363
6625
  return candidates.flatMap((manifest) => {
6364
6626
  const projectRoot = manifest.slice(0, manifest.lastIndexOf("/"));
6365
- const projectJsonPath = (0, import_node_path24.join)(workspaceRoot, projectRoot, "project.json");
6627
+ const projectJsonPath = (0, import_node_path25.join)(workspaceRoot, projectRoot, "project.json");
6366
6628
  let hasOverride = false;
6367
6629
  try {
6368
6630
  const projectJson = readJson(projectJsonPath);
@@ -6391,7 +6653,7 @@ function declaredTargets(workspaceRoot, projectRoot) {
6391
6653
  ]) {
6392
6654
  try {
6393
6655
  const targets = extract(
6394
- readJson((0, import_node_path24.join)(workspaceRoot, projectRoot, file))
6656
+ readJson((0, import_node_path25.join)(workspaceRoot, projectRoot, file))
6395
6657
  );
6396
6658
  if (targets && typeof targets === "object") {
6397
6659
  sources.push({
@@ -6407,8 +6669,8 @@ function declaredTargets(workspaceRoot, projectRoot) {
6407
6669
  function checkTargetFilesExist(workspaceRoot) {
6408
6670
  const projectRoots = new Set(
6409
6671
  [
6410
- ...(0, import_node_fs17.globSync)("{apps,libs,packages}/*/project.json", { cwd: workspaceRoot }),
6411
- ...(0, import_node_fs17.globSync)("{apps,libs,packages}/*/package.json", { cwd: workspaceRoot })
6672
+ ...(0, import_node_fs18.globSync)("{apps,libs,packages}/*/project.json", { cwd: workspaceRoot }),
6673
+ ...(0, import_node_fs18.globSync)("{apps,libs,packages}/*/package.json", { cwd: workspaceRoot })
6412
6674
  ].map((manifest) => toPosix2(manifest).split("/").slice(0, 2).join("/"))
6413
6675
  );
6414
6676
  return [...projectRoots].flatMap(
@@ -6423,7 +6685,7 @@ function checkOneTargetsFiles(workspaceRoot, source, targetName, target) {
6423
6685
  return TARGET_FILE_OPTIONS.flatMap((option) => {
6424
6686
  const value = target?.options?.[option];
6425
6687
  const checkable = typeof value === "string" && !value.includes("{");
6426
- if (!checkable || fileExists((0, import_node_path24.join)(workspaceRoot, value))) {
6688
+ if (!checkable || fileExists((0, import_node_path25.join)(workspaceRoot, value))) {
6427
6689
  return [];
6428
6690
  }
6429
6691
  return [
@@ -6459,6 +6721,23 @@ function checkGoPluginRegistered(workspaceRoot) {
6459
6721
  remedy: "run `mnci upgrade`"
6460
6722
  };
6461
6723
  }
6724
+ function checkGoWorkInSync(workspaceRoot) {
6725
+ const goWorkPath = (0, import_node_path25.join)(workspaceRoot, "go.work");
6726
+ if (!fileExists(goWorkPath)) {
6727
+ return void 0;
6728
+ }
6729
+ const contents = (0, import_node_fs18.existsSync)(goWorkPath) ? (0, import_node_fs18.readFileSync)(goWorkPath, "utf8") : "";
6730
+ const blockEntries = Array.from(contents.matchAll(/(?:^|\n)\s*use\s+\(([^)]*)\)/g), (match) => match[1].split("\n")).flat();
6731
+ const singleEntries = Array.from(contents.matchAll(/(?:^|\n)\s*use\s+(\.\S+)/g), (match) => match[1]);
6732
+ const uses = [...blockEntries, ...singleEntries].map((entry) => entry.trim()).filter(Boolean);
6733
+ const stale = uses.filter((entry) => !(0, import_node_fs18.existsSync)((0, import_node_path25.join)(workspaceRoot, entry, "go.mod")));
6734
+ return {
6735
+ check: "every go.work `use` entry points to a module that still exists",
6736
+ ok: stale.length === 0,
6737
+ detail: `go.work lists ${stale.join(", ")} but there is no go.mod there \u2014 \`go list -m\` fails, which breaks the whole Nx project graph`,
6738
+ remedy: "drop the stale `use` line(s) from go.work (mnci owns the file; a removed Go project should have its entry removed)"
6739
+ };
6740
+ }
6462
6741
  function checkNativeApps(workspaceRoot) {
6463
6742
  if (!hasNativeGoApp(workspaceRoot)) {
6464
6743
  return [];
@@ -6467,9 +6746,9 @@ function checkNativeApps(workspaceRoot) {
6467
6746
  { file: ".github/workflows/ci.yml", marker: "native (${{ matrix.os }})" },
6468
6747
  { file: "azure-pipelines.yml", marker: "- job: native" }
6469
6748
  ];
6470
- const findings = pipelines.filter(({ file }) => fileExists((0, import_node_path24.join)(workspaceRoot, file))).map(({ file, marker }) => ({
6749
+ const findings = pipelines.filter(({ file }) => fileExists((0, import_node_path25.join)(workspaceRoot, file))).map(({ file, marker }) => ({
6471
6750
  check: `${file} builds the native apps on every OS`,
6472
- ok: (0, import_node_fs17.readFileSync)((0, import_node_path24.join)(workspaceRoot, file), "utf8").includes(marker),
6751
+ ok: (0, import_node_fs18.readFileSync)((0, import_node_path25.join)(workspaceRoot, file), "utf8").includes(marker),
6473
6752
  detail: `${file} has no native job: CI would verify a cgo app on the one agent that cannot build it`,
6474
6753
  remedy: "run `mnci upgrade`"
6475
6754
  }));
@@ -6482,7 +6761,7 @@ function checkNativeApps(workspaceRoot) {
6482
6761
  return findings;
6483
6762
  }
6484
6763
  function checkNoRootRuntimeDependencies(workspaceRoot) {
6485
- const manifestPath = (0, import_node_path24.join)(workspaceRoot, "package.json");
6764
+ const manifestPath = (0, import_node_path25.join)(workspaceRoot, "package.json");
6486
6765
  if (!fileExists(manifestPath)) {
6487
6766
  return void 0;
6488
6767
  }
@@ -6502,14 +6781,14 @@ function checkNoRootRuntimeDependencies(workspaceRoot) {
6502
6781
  };
6503
6782
  }
6504
6783
  function checkRollupSourceMaps(workspaceRoot) {
6505
- const configs = (0, import_node_fs17.globSync)(["packages/*/rollup.config.cjs", "libs/*/rollup.config.cjs"], {
6784
+ const configs = (0, import_node_fs18.globSync)(["packages/*/rollup.config.cjs", "libs/*/rollup.config.cjs"], {
6506
6785
  cwd: workspaceRoot
6507
6786
  }).map((config) => toPosix2(config));
6508
6787
  return configs.flatMap((relativePath) => {
6509
- const configPath = (0, import_node_path24.join)(workspaceRoot, relativePath);
6788
+ const configPath = (0, import_node_path25.join)(workspaceRoot, relativePath);
6510
6789
  let config;
6511
6790
  try {
6512
- config = (0, import_node_fs17.readFileSync)(configPath, "utf8");
6791
+ config = (0, import_node_fs18.readFileSync)(configPath, "utf8");
6513
6792
  } catch {
6514
6793
  return [];
6515
6794
  }
@@ -6524,11 +6803,11 @@ function checkRollupSourceMaps(workspaceRoot) {
6524
6803
  });
6525
6804
  }
6526
6805
  function checkDeclarationSpecifiers(workspaceRoot) {
6527
- const configs = (0, import_node_fs17.globSync)(["packages/*/rollup.config.cjs", "libs/*/rollup.config.cjs"], {
6806
+ const configs = (0, import_node_fs18.globSync)(["packages/*/rollup.config.cjs", "libs/*/rollup.config.cjs"], {
6528
6807
  cwd: workspaceRoot
6529
6808
  }).map((config) => toPosix2(config));
6530
6809
  return configs.flatMap((relativePath) => {
6531
- const configPath = (0, import_node_path24.join)(workspaceRoot, relativePath);
6810
+ const configPath = (0, import_node_path25.join)(workspaceRoot, relativePath);
6532
6811
  if (!fileExists(configPath)) {
6533
6812
  return [];
6534
6813
  }
@@ -6562,7 +6841,7 @@ function checkDeclarationSpecifiers(workspaceRoot) {
6562
6841
  });
6563
6842
  }
6564
6843
  function collectFindings(workspaceRoot) {
6565
- const nxJsonPath = (0, import_node_path24.join)(workspaceRoot, "nx.json");
6844
+ const nxJsonPath = (0, import_node_path25.join)(workspaceRoot, "nx.json");
6566
6845
  if (!fileExists(nxJsonPath)) {
6567
6846
  throw new Error(
6568
6847
  `No nx.json found in ${workspaceRoot} \u2014 run 'mnci doctor' from the workspace root.`
@@ -6584,6 +6863,7 @@ function collectFindings(workspaceRoot) {
6584
6863
  ...checkTargetFilesExist(workspaceRoot),
6585
6864
  checkSync(workspaceRoot),
6586
6865
  checkGoPluginRegistered(workspaceRoot),
6866
+ checkGoWorkInSync(workspaceRoot),
6587
6867
  ...checkNativeApps(workspaceRoot)
6588
6868
  ].filter((finding) => finding !== void 0);
6589
6869
  }
@@ -6609,8 +6889,8 @@ function runDoctor(workspaceRoot) {
6609
6889
  }
6610
6890
 
6611
6891
  // src/workspace-creation/adopt-directory.use-case.ts
6612
- var import_node_fs18 = require("fs");
6613
- var import_node_path25 = require("path");
6892
+ var import_node_fs19 = require("fs");
6893
+ var import_node_path26 = require("path");
6614
6894
  var NEVER_COPIED = /* @__PURE__ */ new Set([".git", "node_modules"]);
6615
6895
  var AGENT_AND_CI_ENTRIES = [
6616
6896
  "AGENTS.md",
@@ -6641,13 +6921,13 @@ var PREFLIGHT_BLOCKERS = [
6641
6921
  "packages"
6642
6922
  ];
6643
6923
  function assertAdoptableDirectory(target) {
6644
- if (!(0, import_node_fs18.existsSync)(target)) {
6924
+ if (!(0, import_node_fs19.existsSync)(target)) {
6645
6925
  throw new Error(`--into '${target}' does not exist. Create or clone it first.`);
6646
6926
  }
6647
- if (!(0, import_node_fs18.statSync)(target).isDirectory()) {
6927
+ if (!(0, import_node_fs19.statSync)(target).isDirectory()) {
6648
6928
  throw new Error(`--into '${target}' is not a directory.`);
6649
6929
  }
6650
- const blockers = PREFLIGHT_BLOCKERS.filter((entry) => (0, import_node_fs18.existsSync)((0, import_node_path25.join)(target, entry)));
6930
+ const blockers = PREFLIGHT_BLOCKERS.filter((entry) => (0, import_node_fs19.existsSync)((0, import_node_path26.join)(target, entry)));
6651
6931
  if (blockers.length > 0) {
6652
6932
  throw new Error(
6653
6933
  `--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.`
@@ -6655,15 +6935,15 @@ function assertAdoptableDirectory(target) {
6655
6935
  }
6656
6936
  }
6657
6937
  function mergeGitignore(generatedFile, targetFile) {
6658
- const existing = (0, import_node_fs18.readFileSync)(targetFile, "utf8");
6938
+ const existing = (0, import_node_fs19.readFileSync)(targetFile, "utf8");
6659
6939
  const present = new Set(existing.split("\n").map((line) => line.trim()));
6660
- const additions = (0, import_node_fs18.readFileSync)(generatedFile, "utf8").split("\n").map((line) => line.trimEnd()).filter((line) => {
6940
+ const additions = (0, import_node_fs19.readFileSync)(generatedFile, "utf8").split("\n").map((line) => line.trimEnd()).filter((line) => {
6661
6941
  const trimmed = line.trim();
6662
6942
  return trimmed !== "" && !trimmed.startsWith("#") && !present.has(trimmed);
6663
6943
  });
6664
6944
  if (additions.length === 0) return false;
6665
6945
  const separator = existing.endsWith("\n") ? "" : "\n";
6666
- (0, import_node_fs18.writeFileSync)(
6946
+ (0, import_node_fs19.writeFileSync)(
6667
6947
  targetFile,
6668
6948
  `${existing}${separator}
6669
6949
  # Added by mnci (Nx workspace)
@@ -6673,9 +6953,9 @@ ${additions.join("\n")}
6673
6953
  return true;
6674
6954
  }
6675
6955
  function adoptGeneratedWorkspace(generatedRoot, target) {
6676
- const entries = (0, import_node_fs18.readdirSync)(generatedRoot).filter((entry) => !NEVER_COPIED.has(entry));
6956
+ const entries = (0, import_node_fs19.readdirSync)(generatedRoot).filter((entry) => !NEVER_COPIED.has(entry));
6677
6957
  const collisions = entries.filter(
6678
- (entry) => (0, import_node_fs18.existsSync)((0, import_node_path25.join)(target, entry)) && !EXISTING_WINS.has(entry) && entry !== ".gitignore"
6958
+ (entry) => (0, import_node_fs19.existsSync)((0, import_node_path26.join)(target, entry)) && !EXISTING_WINS.has(entry) && entry !== ".gitignore"
6679
6959
  );
6680
6960
  if (collisions.length > 0) {
6681
6961
  throw new Error(
@@ -6684,28 +6964,28 @@ function adoptGeneratedWorkspace(generatedRoot, target) {
6684
6964
  }
6685
6965
  const result = { copied: [], kept: [], mergedGitignore: false };
6686
6966
  for (const entry of entries) {
6687
- const from = (0, import_node_path25.join)(generatedRoot, entry);
6688
- const to = (0, import_node_path25.join)(target, entry);
6689
- if (entry === ".gitignore" && (0, import_node_fs18.existsSync)(to)) {
6967
+ const from = (0, import_node_path26.join)(generatedRoot, entry);
6968
+ const to = (0, import_node_path26.join)(target, entry);
6969
+ if (entry === ".gitignore" && (0, import_node_fs19.existsSync)(to)) {
6690
6970
  result.mergedGitignore = mergeGitignore(from, to);
6691
6971
  result.kept.push(entry);
6692
6972
  continue;
6693
6973
  }
6694
- if ((0, import_node_fs18.existsSync)(to)) {
6974
+ if ((0, import_node_fs19.existsSync)(to)) {
6695
6975
  result.kept.push(entry);
6696
6976
  continue;
6697
6977
  }
6698
- (0, import_node_fs18.cpSync)(from, to, { recursive: true });
6978
+ (0, import_node_fs19.cpSync)(from, to, { recursive: true });
6699
6979
  result.copied.push(entry);
6700
6980
  }
6701
- (0, import_node_fs18.rmSync)(generatedRoot, { recursive: true, force: true });
6981
+ (0, import_node_fs19.rmSync)(generatedRoot, { recursive: true, force: true });
6702
6982
  return result;
6703
6983
  }
6704
6984
 
6705
6985
  // src/workspace-creation/create-workspace.use-case.ts
6706
- var import_node_fs19 = require("fs");
6986
+ var import_node_fs20 = require("fs");
6707
6987
  var import_node_os = require("os");
6708
- var import_node_path26 = require("path");
6988
+ var import_node_path27 = require("path");
6709
6989
  function nxCloudProviderValue(ci) {
6710
6990
  return ci === "azure" ? "azure" : "github";
6711
6991
  }
@@ -6748,9 +7028,9 @@ async function resolveRegistry(options) {
6748
7028
  return await promptRegistry();
6749
7029
  }
6750
7030
  async function runNew(name, options) {
6751
- const adoptTarget = options.into === void 0 ? void 0 : (0, import_node_path26.resolve)(process.cwd(), options.into);
7031
+ const adoptTarget = options.into === void 0 ? void 0 : (0, import_node_path27.resolve)(process.cwd(), options.into);
6752
7032
  if (adoptTarget !== void 0) assertAdoptableDirectory(adoptTarget);
6753
- const workspaceName = name ?? (adoptTarget === void 0 ? await promptText("Workspace name") : (0, import_node_path26.basename)(adoptTarget));
7033
+ const workspaceName = name ?? (adoptTarget === void 0 ? await promptText("Workspace name") : (0, import_node_path27.basename)(adoptTarget));
6754
7034
  assertValidProjectName(workspaceName, "Workspace name");
6755
7035
  const scope = options.scope ?? (options.yes ? `@${workspaceName}` : await promptText("npm scope for publishable packages", `@${workspaceName}`));
6756
7036
  const registry = await resolveRegistry(options);
@@ -6763,8 +7043,8 @@ async function runNew(name, options) {
6763
7043
  const npmAuth = resolveNpmAuth(options.npmAuth, registry, ci);
6764
7044
  const stack = await resolveStack(options);
6765
7045
  const nxCloud = options.nxCloud ?? (options.yes ? false : await promptNxCloud());
6766
- const stagingParent = adoptTarget === void 0 ? process.cwd() : (0, import_node_fs19.mkdtempSync)((0, import_node_path26.join)((0, import_node_os.tmpdir)(), "mnci-new-"));
6767
- let workspaceRoot = (0, import_node_path26.join)(stagingParent, workspaceName);
7046
+ const stagingParent = adoptTarget === void 0 ? process.cwd() : (0, import_node_fs20.mkdtempSync)((0, import_node_path27.join)((0, import_node_os.tmpdir)(), "mnci-new-"));
7047
+ let workspaceRoot = (0, import_node_path27.join)(stagingParent, workspaceName);
6768
7048
  try {
6769
7049
  logger.step(`Creating Nx workspace '${workspaceName}' (preset: ts)`);
6770
7050
  runNpx(
@@ -6787,7 +7067,7 @@ async function runNew(name, options) {
6787
7067
  workspaceRoot = adoptTarget;
6788
7068
  }
6789
7069
  } finally {
6790
- if (adoptTarget !== void 0) (0, import_node_fs19.rmSync)(stagingParent, { recursive: true, force: true });
7070
+ if (adoptTarget !== void 0) (0, import_node_fs20.rmSync)(stagingParent, { recursive: true, force: true });
6791
7071
  }
6792
7072
  logger.step(
6793
7073
  "Applying MoNecromanCI overlay (VS Code workspace, release config, .npmrc, commitlint, pipeline, stack)"
@@ -6797,8 +7077,8 @@ async function runNew(name, options) {
6797
7077
  { workspaceName, scope, registry, agent, variableGroup, ci, stack, ...npmAuth && { npmAuth } },
6798
7078
  logger.detail
6799
7079
  );
6800
- (0, import_node_fs19.rmSync)((0, import_node_path26.join)(workspaceRoot, "node_modules"), { recursive: true, force: true });
6801
- (0, import_node_fs19.rmSync)((0, import_node_path26.join)(workspaceRoot, "package-lock.json"), { force: true });
7080
+ (0, import_node_fs20.rmSync)((0, import_node_path27.join)(workspaceRoot, "node_modules"), { recursive: true, force: true });
7081
+ (0, import_node_fs20.rmSync)((0, import_node_path27.join)(workspaceRoot, "package-lock.json"), { force: true });
6802
7082
  logger.step("Installing the toolchain (commitlint + husky + linting/formatting)");
6803
7083
  const installStatus = runShell(
6804
7084
  "npm",
@@ -6836,12 +7116,12 @@ function projectKindHintLines(kinds = PROJECT_KINDS) {
6836
7116
  }
6837
7117
 
6838
7118
  // src/workspace-creation/interactive-wizard.use-case.ts
6839
- var import_node_path28 = require("path");
7119
+ var import_node_path29 = require("path");
6840
7120
  var import_prompts4 = require("@inquirer/prompts");
6841
7121
 
6842
7122
  // src/workspace-upgrade/upgrade-workspace.use-case.ts
6843
- var import_node_fs20 = require("fs");
6844
- var import_node_path27 = require("path");
7123
+ var import_node_fs21 = require("fs");
7124
+ var import_node_path28 = require("path");
6845
7125
  function resolveRegistry2(options, persisted) {
6846
7126
  if (options.registry === "azure-artifacts" || options.organization && options.artifactsFeed) {
6847
7127
  const persistedAzure = persisted?.kind === "azure-artifacts" ? persisted : void 0;
@@ -6870,7 +7150,7 @@ function resolveWorkspaceName(workspaceRoot, persisted) {
6870
7150
  return persisted.workspaceName;
6871
7151
  }
6872
7152
  try {
6873
- const existing = (0, import_node_fs20.readdirSync)(workspaceRoot).find(
7153
+ const existing = (0, import_node_fs21.readdirSync)(workspaceRoot).find(
6874
7154
  (file) => file.endsWith(".code-workspace") && file !== "undefined.code-workspace"
6875
7155
  );
6876
7156
  if (existing) {
@@ -6878,11 +7158,11 @@ function resolveWorkspaceName(workspaceRoot, persisted) {
6878
7158
  }
6879
7159
  } catch {
6880
7160
  }
6881
- return (0, import_node_path27.basename)(workspaceRoot);
7161
+ return (0, import_node_path28.basename)(workspaceRoot);
6882
7162
  }
6883
7163
  function detectNpmAuth(workspaceRoot) {
6884
7164
  try {
6885
- return (0, import_node_fs20.readFileSync)((0, import_node_path27.join)(workspaceRoot, "azure-pipelines.yml"), "utf8").includes("npmAuthenticate@0") ? "build-identity" : void 0;
7165
+ return (0, import_node_fs21.readFileSync)((0, import_node_path28.join)(workspaceRoot, "azure-pipelines.yml"), "utf8").includes("npmAuthenticate@0") ? "build-identity" : void 0;
6886
7166
  } catch {
6887
7167
  return void 0;
6888
7168
  }
@@ -6929,7 +7209,7 @@ function resolveOverlayOptions(workspaceRoot, options, persisted) {
6929
7209
  };
6930
7210
  }
6931
7211
  function runUpgrade(workspaceRoot, options) {
6932
- if (!fileExists((0, import_node_path27.join)(workspaceRoot, "nx.json"))) {
7212
+ if (!fileExists((0, import_node_path28.join)(workspaceRoot, "nx.json"))) {
6933
7213
  throw new Error(
6934
7214
  `No nx.json found in ${workspaceRoot} \u2014 this does not look like an Nx workspace. Run 'mnci upgrade' from the workspace root.`
6935
7215
  );
@@ -6994,7 +7274,7 @@ function runUpgrade(workspaceRoot, options) {
6994
7274
 
6995
7275
  // src/workspace-creation/interactive-wizard.use-case.ts
6996
7276
  async function runInteractive() {
6997
- const inWorkspace = fileExists((0, import_node_path28.join)(process.cwd(), "nx.json"));
7277
+ const inWorkspace = fileExists((0, import_node_path29.join)(process.cwd(), "nx.json"));
6998
7278
  const choiceNew = { name: "Create a new monorepo", value: "new" };
6999
7279
  const choiceAdd = { name: "Add a project to this workspace", value: "add" };
7000
7280
  const choiceUpgrade = {
@@ -7015,8 +7295,8 @@ async function runInteractive() {
7015
7295
  }
7016
7296
 
7017
7297
  // src/cli-version/check-for-update.use-case.ts
7018
- var import_node_fs21 = require("fs");
7019
- var import_node_path29 = require("path");
7298
+ var import_node_fs22 = require("fs");
7299
+ var import_node_path30 = require("path");
7020
7300
  var import_cross_spawn2 = __toESM(require("cross-spawn"));
7021
7301
  var PACKAGE_NAME = "@mnci/cli";
7022
7302
  var REGISTRY_TIMEOUT_MS = 2e3;
@@ -7072,8 +7352,8 @@ function releaseTriple(version) {
7072
7352
  }
7073
7353
  function readCliVersion(packageDirectory) {
7074
7354
  try {
7075
- const manifestPath = (0, import_node_path29.join)(packageDirectory, "..", "package.json");
7076
- const manifest = JSON.parse((0, import_node_fs21.readFileSync)(manifestPath, "utf8"));
7355
+ const manifestPath = (0, import_node_path30.join)(packageDirectory, "..", "package.json");
7356
+ const manifest = JSON.parse((0, import_node_fs22.readFileSync)(manifestPath, "utf8"));
7077
7357
  return manifest.version ?? "0.0.0";
7078
7358
  } catch {
7079
7359
  return "0.0.0";
@@ -7127,6 +7407,11 @@ function buildProgram(cliVersion) {
7127
7407
  ).option("--check", "report only; never prompt and never write (the default when piped)").option("-y, --yes", "select every available update without prompting").option("--ecosystem <name>", "restrict to one ecosystem: npm | pip | pub | go").option("--no-install", "update the manifests but skip the reinstall step").action(async (options) => {
7128
7408
  await runUp(process.cwd(), options);
7129
7409
  });
7410
+ program.command("install").alias("i").argument("[packages...]", "packages to add (a bare name or a pinned spec); omit to install only").description(
7411
+ "Add a dependency to a specific project using its own toolchain (npm/pip/pub/nuget/go), or \u2014 with no package \u2014 install everything. Each project owns its dependencies, so a package needs a target: 'mnci i -w <project> <package>'"
7412
+ ).option("-w, --workspace <project...>", "target project, by directory (apps/foo) or basename (foo); repeatable").option("-D, --save-dev", "add as a development dependency, where the ecosystem distinguishes one").action((packages, options) => {
7413
+ runInstall(process.cwd(), packages, options);
7414
+ });
7130
7415
  program.command("add").addArgument(new import_commander.Argument("[kind]", "project kind").choices(PROJECT_KINDS)).argument("[name]", "project name").description("Add a project by delegating to the matching Nx plugin generator").option("--scope <scope>", "npm scope for a publishable lib (defaults to @<workspace name>)").option(
7131
7416
  "--framework <framework>",
7132
7417
  "node-app only: express | fastify | koa | nest | none (default: none)"