@mnci/cli 4.1.4 → 4.2.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
@@ -38,8 +38,8 @@ module.exports = __toCommonJS(cli_exports);
38
38
  var import_commander = require("commander");
39
39
 
40
40
  // src/commands/add.ts
41
- var import_node_path13 = require("path");
42
- var import_prompts5 = require("@inquirer/prompts");
41
+ var import_node_path14 = require("path");
42
+ var import_prompts6 = require("@inquirer/prompts");
43
43
 
44
44
  // src/nx.ts
45
45
  var import_cross_spawn = __toESM(require("cross-spawn"));
@@ -291,13 +291,10 @@ function assertValidProjectName(name, label) {
291
291
  }
292
292
  }
293
293
 
294
- // src/commands/add/flutter.ts
294
+ // src/commands/add/csharp.ts
295
+ var import_node_fs4 = require("fs");
295
296
  var import_node_path4 = require("path");
296
297
 
297
- // src/commands/add/shared.ts
298
- var import_node_fs3 = require("fs");
299
- var import_node_path3 = require("path");
300
-
301
298
  // src/overlay.ts
302
299
  var import_node_fs2 = require("fs");
303
300
  var import_node_path2 = require("path");
@@ -425,7 +422,7 @@ var COMMITLINT_CONFIG = `export default { extends: ['@commitlint/config-conventi
425
422
  `;
426
423
  var COMMIT_MSG_HOOK = `npx --no -- commitlint --edit "$1"
427
424
  `;
428
- var ESLINT_CONFIG_VERSION = "^0.1.0";
425
+ var ESLINT_CONFIG_VERSION = ">=0.1.0 <1.0.0";
429
426
  function eslintConfigSpec() {
430
427
  return process.env.MNCI_ESLINT_CONFIG_SPEC ?? ESLINT_CONFIG_VERSION;
431
428
  }
@@ -436,7 +433,8 @@ function devcontainerJson(workspaceName) {
436
433
  image: `mcr.microsoft.com/devcontainers/typescript-node:${NODE_VERSION}-bookworm`,
437
434
  features: {
438
435
  "ghcr.io/devcontainers/features/python:1": { version: "3.12" },
439
- "ghcr.io/devcontainers/features/go:1": { version: "latest" }
436
+ "ghcr.io/devcontainers/features/go:1": { version: "latest" },
437
+ "ghcr.io/devcontainers/features/dotnet:2": { version: DOTNET_SDK_VERSION.replace(/\.x$/, "") }
440
438
  },
441
439
  // `npm ci` first: every guard after it runs through the workspace's own
442
440
  // scripts and Nx, which do not exist until the install completes.
@@ -779,6 +777,65 @@ function pythonPublishUrl(registry) {
779
777
  }
780
778
  return void 0;
781
779
  }
780
+ function nugetFeedUrl(registry) {
781
+ if (registry.kind === "azure-artifacts") {
782
+ return `https://pkgs.dev.azure.com/${registry.organization}/${registry.project}/_packaging/${registry.artifactsFeed}/nuget/v3/index.json`;
783
+ }
784
+ return void 0;
785
+ }
786
+ var NUGET_AZURE_SOURCE = "AzureArtifacts";
787
+ function nugetConfigContent(registry, scope) {
788
+ if (registry.kind === "npm") {
789
+ return `<?xml version="1.0" encoding="utf-8"?>
790
+ <!-- Publish authentication for NuGet is deliberately UNCONFIGURED for the
791
+ public npm registry choice: publishing to public nuget.org needs a
792
+ nuget.org-issued API key, a credential mnci collects nowhere (the same
793
+ gap Python's PyPI publish has for this same registry choice). A
794
+ csharp-lib is still versioned and tagged; run "dotnet nuget push"
795
+ yourself with your own key, or regenerate with --registry
796
+ azure-artifacts. -->
797
+ <configuration>
798
+ <packageSources>
799
+ <clear />
800
+ <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
801
+ </packageSources>
802
+ </configuration>
803
+ `;
804
+ }
805
+ const pascalScope2 = scope.replace(/^@/, "").split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
806
+ const feedUrl = nugetFeedUrl(registry);
807
+ return `<?xml version="1.0" encoding="utf-8"?>
808
+ <!-- NuGet's own environment-variable syntax is '%VAR%' on every platform \u2014
809
+ never '\${VAR}'/'$VAR', which resolve on none of them. NUGET_PAT is the
810
+ RAW PAT (not base64 \u2014 unlike .npmrc's _password, NuGet's HTTP client
811
+ handles Basic auth itself from Username + ClearTextPassword), exported
812
+ only by the CI release step (see nugetPublishEnvFragment in mnci); an
813
+ ordinary build/test/lint step never needs it, since packageSourceMapping
814
+ below scopes this feed to ${pascalScope2}.* packages only. -->
815
+ <configuration>
816
+ <packageSources>
817
+ <clear />
818
+ <add key="nuget.org" value="https://api.nuget.org/v3/index.json" />
819
+ <add key="${NUGET_AZURE_SOURCE}" value="${feedUrl}" />
820
+ </packageSources>
821
+ <packageSourceCredentials>
822
+ <${NUGET_AZURE_SOURCE}>
823
+ <add key="Username" value="AzureArtifacts" />
824
+ <add key="ClearTextPassword" value="%NUGET_PAT%" />
825
+ </${NUGET_AZURE_SOURCE}>
826
+ </packageSourceCredentials>
827
+ <packageSourceMapping>
828
+ <clear />
829
+ <packageSource key="${NUGET_AZURE_SOURCE}">
830
+ <package pattern="${pascalScope2}.*" />
831
+ </packageSource>
832
+ <packageSource key="nuget.org">
833
+ <package pattern="*" />
834
+ </packageSource>
835
+ </packageSourceMapping>
836
+ </configuration>
837
+ `;
838
+ }
782
839
  var PYTHON_INSTALL_GUARD = `node -e "if(!require('node:fs').existsSync('requirements-dev.txt')){console.log('No Python projects - skipping.');process.exit(0)}const py=process.platform==='win32'?'python':'python3';process.exit(require('node:child_process').spawnSync(py+' -m pip install -r requirements-dev.txt',{stdio:'inherit',shell:true}).status ?? 1)"`;
783
840
  var PYTHON_WORKSPACE_INSTALL_GUARD = `node -e "const fs=require('node:fs'),path=require('node:path');const editableDirs=[...fs.globSync('apps/*/pyproject.toml'),...fs.globSync('python-packages/*/pyproject.toml'),...fs.globSync('libs/*/pyproject.toml')].map((p)=>path.dirname(p));const requirementsFiles=fs.globSync('apps/*/requirements.txt');if(editableDirs.length===0&&requirementsFiles.length===0){console.log('No Python projects - skipping.');process.exit(0)}const args=['-m','pip','install','--quiet',...editableDirs.flatMap((d)=>['-e',d]),...requirementsFiles.flatMap((f)=>['-r',f])];const py=process.platform==='win32'?'python':'python3';process.exit(require('node:child_process').spawnSync(py,args,{stdio:'inherit'}).status ?? 1)"`;
784
841
  var PIP_AUDIT_GUARD = `node -e "if(!require('node:fs').existsSync('requirements-dev.txt')){console.log('No Python projects - skipping.');process.exit(0)}const py=process.platform==='win32'?'python':'python3';require('node:child_process').spawnSync(py,['-m','pip_audit'],{stdio:'inherit'});process.exit(0)"`;
@@ -790,6 +847,8 @@ var GO_TOOL_PATH_GITHUB = `node -e "${GO_TOOL_PATH_PRELUDE}if(!process.env.GITHU
790
847
  var FLUTTER_SDK_VERSION = "3.44.8";
791
848
  var NODE_VERSION = "24";
792
849
  var NPM_VERSION = "11";
850
+ var DOTNET_SDK_VERSION = "10.0.x";
851
+ var DOTNET_DETECT_AZURE = `node -e "const fs=require('node:fs');const has=[...fs.globSync('apps/*/*.csproj'),...fs.globSync('packages/*/*.csproj'),...fs.globSync('libs/*/*.csproj')].length>0;console.log('##vso[task.setvariable variable=hasDotnetProjects]'+has)"`;
793
852
  var FLUTTER_SDK_DIRECTORY_EXPRESSION = `require('node:path').join(require('node:os').homedir(),'.mnci-flutter-${FLUTTER_SDK_VERSION}')`;
794
853
  var FLUTTER_SDK_INSTALL_GUARD = `node -e "const fs=require('node:fs'),cp=require('node:child_process');if(!fs.existsSync('pubspec.yaml')){console.log('No Flutter projects - skipping.');process.exit(0)}if(cp.spawnSync('flutter',['--version'],{stdio:'ignore',shell:process.platform==='win32'}).status===0){console.log('Flutter SDK already on PATH - skipping.');process.exit(0)}const sdk=${FLUTTER_SDK_DIRECTORY_EXPRESSION};if(fs.existsSync(sdk)){console.log('Flutter SDK already installed at '+sdk+' - skipping.');process.exit(0)}process.exit(cp.spawnSync('git',['clone','--depth','1','--branch','${FLUTTER_SDK_VERSION}','https://github.com/flutter/flutter.git',sdk],{stdio:'inherit'}).status ?? 1)"`;
795
854
  var FLUTTER_TOOL_PATH_PRELUDE = `const fs=require('node:fs'),path=require('node:path');if(!fs.existsSync('pubspec.yaml')){console.log('No Flutter projects - skipping.');process.exit(0)}const sdk=${FLUTTER_SDK_DIRECTORY_EXPRESSION};if(!fs.existsSync(sdk)){console.log('Flutter SDK was not installed by mnci (already on PATH) - skipping.');process.exit(0)}const bin=path.join(sdk,'bin');`;
@@ -799,20 +858,23 @@ var FLUTTER_PUB_GET_GUARD = `node -e "const fs=require('node:fs');if(!fs.existsS
799
858
  var NPM_AUDIT_STEP = `node -e "const cp=require('node:child_process');const r=cp.spawnSync('npm',['audit','--json'],{encoding:'utf8',shell:process.platform==='win32',maxBuffer:33554432});let d;try{d=JSON.parse(r.stdout)}catch{console.log('npm audit produced no JSON (exit '+r.status+') - not blocking on a broken audit.');process.exit(0)}const all=Object.values(d.vulnerabilities||{});const BLOCK=['critical','high','moderate'];const act=all.filter(v=>v.fixAvailable&&BLOCK.includes(v.severity));const rest=all.filter(v=>!act.includes(v));for(const v of rest)console.log(' note ['+v.severity+'] '+v.name+(v.fixAvailable?' - fix available, below the blocking threshold':' - NO fix available upstream, nothing to do here'));if(act.length===0){console.log('npm audit - '+all.length+' advisory(ies), none actionable at moderate or above.');process.exit(0)}for(const v of act)console.log(' BLOCKING ['+v.severity+'] '+v.name+' - fix available'+(v.fixAvailable&&v.fixAvailable.isSemVerMajor?' (semver-major)':''));console.log('Each has a published fix. Add a targeted overrides entry in package.json rather than npm audit fix --force.');process.exit(1)"`;
800
859
  var VERIFY_TARGETS = "lint,typecheck,test,build";
801
860
  var AFFECTED_OR_ALL_GUARD = `node -e "const cp=require('node:child_process');const T='${VERIFY_TARGETS}';const all=()=>process.exit(cp.spawnSync('npx nx run-many -t '+T,{stdio:'inherit',shell:true}).status ?? 1);const ref=process.env.GITHUB_BASE_REF||process.env.SYSTEM_PULLREQUEST_TARGETBRANCH||'';if(!ref){console.log('Not a pull request - verifying EVERY project.');all()}const target=ref.replace('refs/heads/','');const mergeBase=r=>{const o=cp.spawnSync('git',['merge-base',r,'HEAD'],{encoding:'utf8'});return o.status===0?o.stdout.trim():''};let base=mergeBase('origin/'+target);if(!base){console.log('No origin/'+target+' ref - fetching it to resolve a merge-base.');cp.spawnSync('git',['fetch','--no-tags','origin',target],{stdio:'inherit'});base=mergeBase('FETCH_HEAD')}if(!base){console.log('Could not resolve a merge-base with '+target+' - verifying EVERY project.');all()}console.log('Pull request against '+target+' - verifying projects affected since '+base);process.exit(cp.spawnSync('npx nx affected -t '+T+' --base='+base,{stdio:'inherit',shell:true}).status ?? 1)"`;
802
- var PACK_APPS_GUARD = `node -e "const fs=require('node:fs');fs.mkdirSync('dist/drop',{recursive:true});const hasProjectJson=fs.globSync('apps/*/project.json').length>0;const hasInlineNx=fs.globSync('apps/*/package.json').some((f)=>{try{return Boolean(JSON.parse(fs.readFileSync(f,'utf8')).nx)}catch{return false}});if(!hasProjectJson&&!hasInlineNx){console.log('No apps to pack - skipping.');process.exit(0)}process.exit(require('node:child_process').spawnSync('npx nx run-many -t package',{stdio:'inherit',shell:true}).status ?? 1)"`;
803
- function releaseGuard(pythonPublishEnv) {
804
- return `node -e "const fs=require('node:fs'),cp=require('node:child_process');const hasNpm=fs.globSync('packages/*/package.json').length>0;const hasPython=fs.globSync('python-packages/*/pyproject.toml').length>0;if(!hasNpm&&!hasPython){console.log('Nothing to release - skipping.');process.exit(0)}const env={...process.env};${pythonPublishEnv}process.exit(cp.spawnSync('npx nx release --yes',{stdio:'inherit',shell:true,env}).status ?? 1)"`;
861
+ var PACK_APPS_GUARD = `node -e "const fs=require('node:fs');fs.mkdirSync('dist/drop',{recursive:true});const hasProjectJson=fs.globSync('apps/*/project.json').length>0;const hasInlineNx=fs.globSync('apps/*/package.json').some((f)=>{try{return Boolean(JSON.parse(fs.readFileSync(f,'utf8')).nx)}catch{return false}});const hasCsproj=fs.globSync('apps/*/*.csproj').length>0;if(!hasProjectJson&&!hasInlineNx&&!hasCsproj){console.log('No apps to pack - skipping.');process.exit(0)}process.exit(require('node:child_process').spawnSync('npx nx run-many -t package',{stdio:'inherit',shell:true}).status ?? 1)"`;
862
+ function releaseGuard(pythonPublishEnv, nugetPublishEnv) {
863
+ return `node -e "const fs=require('node:fs'),cp=require('node:child_process');const hasNpm=fs.globSync('packages/*/package.json').length>0;const hasPython=fs.globSync('python-packages/*/pyproject.toml').length>0;const hasCsharp=fs.globSync('packages/*/*.csproj').length>0;if(!hasNpm&&!hasPython&&!hasCsharp){console.log('Nothing to release - skipping.');process.exit(0)}const env={...process.env};${pythonPublishEnv}${nugetPublishEnv}process.exit(cp.spawnSync('npx nx release --yes',{stdio:'inherit',shell:true,env}).status ?? 1)"`;
805
864
  }
806
865
  function pythonPublishEnvFragment(pythonPublishUrl2) {
807
866
  return pythonPublishUrl2 ? `if(hasPython){env.TWINE_REPOSITORY_URL='${pythonPublishUrl2}';env.TWINE_USERNAME='AzureArtifacts';env.TWINE_PASSWORD=Buffer.from(process.env.PAT,'base64').toString()}` : "";
808
867
  }
868
+ function nugetPublishEnvFragment(nugetFeedUrl2) {
869
+ return nugetFeedUrl2 ? "if(hasCsharp){env.NUGET_PAT=Buffer.from(process.env.PAT,'base64').toString()}" : "";
870
+ }
809
871
  function npmAuthEnvVariable(registryKind, variableReference) {
810
872
  return registryKind === "npm" ? ["NODE_AUTH_TOKEN", variableReference("NPM_TOKEN")] : ["PAT", variableReference("PAT")];
811
873
  }
812
874
  function poolBlock(agent) {
813
875
  return /^(?:ubuntu|windows|macos)-/i.test(agent) ? ` vmImage: ${agent}` : ` name: ${agent}`;
814
876
  }
815
- function azurePipelinesYaml(agent, variableGroup, pythonPublishUrl2, registryKind = "azure-artifacts") {
877
+ function azurePipelinesYaml(agent, variableGroup, pythonPublishUrl2, registryKind = "azure-artifacts", nugetFeedUrl2) {
816
878
  const onMain = "and(succeeded(), in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI'), eq(variables['Build.SourceBranchName'], 'main'))";
817
879
  const [npmAuthName, npmAuthValue] = npmAuthEnvVariable(registryKind, (name) => `$(${name})`);
818
880
  return `name: monorepo-ci-$(Date:yyyyMMdd)$(Rev:.r)
@@ -984,6 +1046,20 @@ steps:
984
1046
  - script: ${FLUTTER_PUB_GET_GUARD}
985
1047
  displayName: Resolve Dart dependencies (one pub get for the whole workspace)
986
1048
 
1049
+ # .NET, if the workspace has any. Azure's 'condition:' expression language
1050
+ # has no glob function, so this script step detects C# projects first and
1051
+ # hands the answer to the install task below through a pipeline variable \u2014
1052
+ # see DOTNET_DETECT_AZURE's remarks for why this differs from every other
1053
+ # toolchain's self-gating 'node -e' guard.
1054
+ - script: ${DOTNET_DETECT_AZURE}
1055
+ displayName: Detect .NET projects
1056
+
1057
+ - task: UseDotNet@2
1058
+ displayName: Install the .NET SDK (${DOTNET_SDK_VERSION})
1059
+ condition: eq(variables['hasDotnetProjects'], 'true')
1060
+ inputs:
1061
+ version: ${DOTNET_SDK_VERSION}
1062
+
987
1063
  # Fails fast, with an unambiguous message, when a stale TypeScript project
988
1064
  # reference (or another sync generator's drift) was never synced+committed
989
1065
  # locally \u2014 sync.applyChanges (nx.json) only auto-applies interactively, so
@@ -1024,13 +1100,14 @@ steps:
1024
1100
  displayName: Tag the run per app (type-name)
1025
1101
  condition: ${onMain}
1026
1102
 
1027
- # Version + tag + publish, in one release, for npm (packages/*) AND Python
1028
- # (python-packages/*) \u2014 conventional commits, tag-only push. Portable guard:
1029
- # nx release errors on an empty scope, so skip cleanly when there is nothing
1030
- # to release. When there are Python packages and an Azure feed, twine
1031
- # publish credentials are exported (raw PAT, decoded from the base64 variable).
1032
- - script: ${releaseGuard(pythonPublishEnvFragment(pythonPublishUrl2))}
1033
- displayName: Release \u2014 version, tag and publish (npm + Python)
1103
+ # Version + tag + publish, in one release, for npm (packages/*), Python
1104
+ # (python-packages/*) AND C# (packages/*/*.csproj) \u2014 conventional commits,
1105
+ # tag-only push. Portable guard: nx release errors on an empty scope, so
1106
+ # skip cleanly when there is nothing to release. When there are Python or
1107
+ # C# packages and an Azure feed, twine/NuGet publish credentials are
1108
+ # exported (raw PAT, decoded from the base64 variable).
1109
+ - script: ${releaseGuard(pythonPublishEnvFragment(pythonPublishUrl2), nugetPublishEnvFragment(nugetFeedUrl2))}
1110
+ displayName: Release \u2014 version, tag and publish (npm + Python + C#)
1034
1111
  condition: ${onMain}
1035
1112
  env:
1036
1113
  ${npmAuthName}: ${npmAuthValue}
@@ -1045,7 +1122,7 @@ steps:
1045
1122
  condition: ${onMain}
1046
1123
  `;
1047
1124
  }
1048
- function githubActionsYaml(agent, pythonPublishUrl2, registryKind = "azure-artifacts", ci = "github") {
1125
+ function githubActionsYaml(agent, pythonPublishUrl2, registryKind = "azure-artifacts", ci = "github", nugetFeedUrl2) {
1049
1126
  const onMain = "github.event_name == 'push' && github.ref_name == 'main'";
1050
1127
  const [npmAuthName, npmAuthValue] = npmAuthEnvVariable(
1051
1128
  registryKind,
@@ -1180,6 +1257,16 @@ jobs:
1180
1257
  - run: ${FLUTTER_PUB_GET_GUARD}
1181
1258
  name: Resolve Dart dependencies (one pub get for the whole workspace)
1182
1259
 
1260
+ # .NET, if the workspace has any. actions/setup-dotnet is the
1261
+ # maintained, cached installer GitHub itself ships, so this uses it
1262
+ # directly rather than a hand-rolled 'node -e' guard \u2014 see
1263
+ # DOTNET_DETECT_AZURE's remarks for the full reasoning. hashFiles()
1264
+ # gates it inline, so a JS-only workspace pays nothing.
1265
+ - uses: actions/setup-dotnet@v4
1266
+ if: \${{ hashFiles('apps/*/*.csproj', 'packages/*/*.csproj', 'libs/*/*.csproj') != '' }}
1267
+ with:
1268
+ dotnet-version: ${DOTNET_SDK_VERSION}
1269
+
1183
1270
  # Fails fast, with an unambiguous message, when a stale TypeScript project
1184
1271
  # reference (or another sync generator's drift) was never synced+committed
1185
1272
  # locally \u2014 sync.applyChanges (nx.json) only auto-applies interactively, so
@@ -1214,18 +1301,19 @@ jobs:
1214
1301
  path: dist/drop
1215
1302
  if-no-files-found: ignore
1216
1303
 
1217
- # Version + tag + publish, in one release, for npm (packages/*) AND Python
1218
- # (python-packages/*) \u2014 conventional commits, tag-only push. Portable guard:
1219
- # nx release errors on an empty scope, so skip cleanly when there is nothing
1220
- # to release. When there are Python packages and an Azure feed, twine
1221
- # publish credentials are exported (raw PAT, decoded from the base64 secret).${githubReleases ? `
1304
+ # Version + tag + publish, in one release, for npm (packages/*), Python
1305
+ # (python-packages/*) AND C# (packages/*/*.csproj) \u2014 conventional
1306
+ # commits, tag-only push. Portable guard: nx release errors on an empty
1307
+ # scope, so skip cleanly when there is nothing to release. When there
1308
+ # are Python or C# packages and an Azure feed, twine/NuGet publish
1309
+ # credentials are exported (raw PAT, decoded from the base64 secret).${githubReleases ? `
1222
1310
  # This provider also creates a per-project GitHub Release (changelog
1223
1311
  # generated from conventional commits since that project's last tag),
1224
1312
  # which is why 'nx release' pushes the tag itself here \u2014 see
1225
1313
  # releaseConfig's remarks for why that is safe on this Nx version and
1226
1314
  # why every other provider combination keeps the explicit push step below.` : ""}
1227
- - run: ${releaseGuard(pythonPublishEnvFragment(pythonPublishUrl2))}
1228
- name: Release \u2014 version, tag${githubReleases ? ", publish and GitHub Release" : " and publish"} (npm + Python)
1315
+ - run: ${releaseGuard(pythonPublishEnvFragment(pythonPublishUrl2), nugetPublishEnvFragment(nugetFeedUrl2))}
1316
+ name: Release \u2014 version, tag${githubReleases ? ", publish and GitHub Release" : " and publish"} (npm + Python + C#)
1229
1317
  if: \${{ ${onMain} }}
1230
1318
  env:
1231
1319
  ${npmAuthName}: ${npmAuthValue}${githubReleases ? `
@@ -1407,24 +1495,27 @@ function applyOverlay(workspaceRoot, options, onProgress = () => {
1407
1495
  (0, import_node_fs2.rmSync)((0, import_node_path2.join)(workspaceRoot, "undefined.code-workspace"), { force: true });
1408
1496
  }
1409
1497
  const publishUrl = pythonPublishUrl(options.registry);
1498
+ const nugetUrl = nugetFeedUrl(options.registry);
1410
1499
  if (options.ci === "azure" || options.ci === "both") {
1411
1500
  onProgress("azure-pipelines.yml \u2014 build, verify, pack and release");
1412
1501
  writeFileEnsured(
1413
1502
  (0, import_node_path2.join)(workspaceRoot, "azure-pipelines.yml"),
1414
- azurePipelinesYaml(options.agent, options.variableGroup, publishUrl, options.registry.kind)
1503
+ azurePipelinesYaml(options.agent, options.variableGroup, publishUrl, options.registry.kind, nugetUrl)
1415
1504
  );
1416
1505
  }
1417
1506
  if (options.ci === "github" || options.ci === "both") {
1418
1507
  onProgress(".github/workflows/ci.yml and dependabot.yml");
1419
1508
  writeFileEnsured(
1420
1509
  (0, import_node_path2.join)(workspaceRoot, ".github/workflows/ci.yml"),
1421
- githubActionsYaml(options.agent, publishUrl, options.registry.kind, options.ci)
1510
+ githubActionsYaml(options.agent, publishUrl, options.registry.kind, options.ci, nugetUrl)
1422
1511
  );
1423
1512
  writeFileEnsured((0, import_node_path2.join)(workspaceRoot, ".github/dependabot.yml"), dependabotConfig(workspaceRoot));
1424
1513
  }
1425
1514
  }
1426
1515
 
1427
1516
  // src/commands/add/shared.ts
1517
+ var import_node_fs3 = require("fs");
1518
+ var import_node_path3 = require("path");
1428
1519
  function hasPlugin(workspaceRoot, packageName) {
1429
1520
  const manifest = readJson((0, import_node_path3.join)(workspaceRoot, "package.json"));
1430
1521
  const installed = { ...manifest.dependencies, ...manifest.devDependencies };
@@ -1671,6 +1762,11 @@ function addNxTargets(manifestPath, newTargets) {
1671
1762
  toJson({ ...manifest, nx: { ...nx, targets: { ...targets, ...newTargets } } })
1672
1763
  );
1673
1764
  }
1765
+ function addProjectJsonTargets(projectJsonPath, newTargets) {
1766
+ const project = fileExists(projectJsonPath) ? readJson(projectJsonPath) : {};
1767
+ const targets = project.targets ?? {};
1768
+ writeFileEnsured(projectJsonPath, toJson({ ...project, targets: { ...targets, ...newTargets } }));
1769
+ }
1674
1770
  var ESLINT_CONFIG_FILENAMES = [
1675
1771
  "eslint.config.js",
1676
1772
  "eslint.config.mjs",
@@ -1798,12 +1894,261 @@ function relocateRootRuntimeDependencies(workspaceRoot, projectName, before) {
1798
1894
  }
1799
1895
  }
1800
1896
 
1801
- // src/commands/add/flutter.ts
1802
- function addProjectJsonTargets(projectJsonPath, newTargets) {
1803
- const project = readJson(projectJsonPath);
1804
- const targets = project.targets ?? {};
1805
- writeFileEnsured(projectJsonPath, toJson({ ...project, targets: { ...targets, ...newTargets } }));
1897
+ // src/commands/add/csharp.ts
1898
+ function ensureDotnet(workspaceRoot) {
1899
+ if (runShell("dotnet", ["--version"], workspaceRoot) !== 0) {
1900
+ throw new Error(".NET SDK not found. Install .NET 8+ first: https://dotnet.microsoft.com/download");
1901
+ }
1902
+ }
1903
+ function targetFramework() {
1904
+ const [major, minor] = DOTNET_SDK_VERSION.split(".", 2);
1905
+ return `net${major}.${minor}`;
1906
+ }
1907
+ function pascalCase(name) {
1908
+ return name.split("-").map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
1909
+ }
1910
+ function scaffoldDotnetProject(workspaceRoot, projectRoot, identity, template) {
1911
+ if (runShell(
1912
+ "dotnet",
1913
+ ["new", template, "-n", identity, "-o", projectRoot, "--framework", targetFramework()],
1914
+ workspaceRoot
1915
+ ) !== 0) {
1916
+ throw new Error(`dotnet new ${template} failed for ${projectRoot}`);
1917
+ }
1918
+ }
1919
+ function csharpAppPackageTarget(tag, projectRoot, name) {
1920
+ const outDir = `dist/apps/${name}`;
1921
+ const zip = `dist/drop/${tag}-${name}.zip`;
1922
+ const command = `node -e "const cp=require('node:child_process');const r=cp.spawnSync('dotnet',['publish','${projectRoot}','-c','Release','-o','${outDir}'],{stdio:'inherit',shell:true});if(r.status!==0)process.exit(r.status??1);const fs=require('node:fs');fs.mkdirSync('dist/drop',{recursive:true});const A=require('adm-zip');const z=new A();z.addLocalFolder('${outDir}');z.writeZip('${zip}')"`;
1923
+ return {
1924
+ executor: "nx:run-commands",
1925
+ outputs: [`{workspaceRoot}/${zip}`],
1926
+ options: { command }
1927
+ };
1928
+ }
1929
+ function csharpAppStartTarget(projectRoot) {
1930
+ return {
1931
+ executor: "nx:run-commands",
1932
+ continuous: true,
1933
+ options: { command: "dotnet run", cwd: projectRoot }
1934
+ };
1935
+ }
1936
+ function addCsharpApp(workspaceRoot, name, template = "console") {
1937
+ ensureDotnet(workspaceRoot);
1938
+ ensurePlugin(workspaceRoot, "@nx/dotnet");
1939
+ ensureAdmZip(workspaceRoot);
1940
+ const projectRoot = `apps/${name}`;
1941
+ scaffoldDotnetProject(workspaceRoot, projectRoot, pascalCase(name), template);
1942
+ addProjectJsonTargets((0, import_node_path4.join)(workspaceRoot, projectRoot, "project.json"), {
1943
+ package: csharpAppPackageTarget("csharp-app", projectRoot, name),
1944
+ start: csharpAppStartTarget(projectRoot)
1945
+ });
1946
+ registerProjectCommands(workspaceRoot, name, { build: true, start: `nx run ${name}:start` });
1947
+ }
1948
+ function pascalScope(scope) {
1949
+ return pascalCase(scope.replace(/^@/, ""));
1950
+ }
1951
+ var CSHARP_VERSION_ACTIONS_PATH = "tools/csharp-version-actions.cjs";
1952
+ var CSHARP_VERSION_ACTIONS = `'use strict'
1953
+
1954
+ const { join } = require('node:path')
1955
+ const { VersionActions } = require('nx/release')
1956
+
1957
+ const VERSION_TAG = /<Version>([^<]*)<\\/Version>/
1958
+
1959
+ function findCsproj (tree, root) {
1960
+ const name = tree.children(root).find((entry) => entry.endsWith('.csproj'))
1961
+ return name ? join(root, name) : null
1962
+ }
1963
+
1964
+ /**
1965
+ * Hand-written Nx release VersionActions for a publishable C# library: reads
1966
+ * and writes the <Version> element of the project's own .csproj. Written by
1967
+ * \`mnci add csharp-lib\` \u2014 see that command's source for why this is a
1968
+ * workspace file rather than an npm package.
1969
+ */
1970
+ class CsharpVersionActions extends VersionActions {
1971
+ validManifestFilenames = ['*.csproj']
1972
+
1973
+ async readCurrentVersionFromSourceManifest (tree) {
1974
+ const manifestPath = findCsproj(tree, this.projectGraphNode.data.root)
1975
+ if (!manifestPath) {
1976
+ return null
1977
+ }
1978
+ const contents = tree.read(manifestPath, 'utf8') ?? ''
1979
+ const match = VERSION_TAG.exec(contents)
1980
+ if (!match || !match[1]) {
1981
+ throw new Error(\`Could not find a <Version> element in \${manifestPath}\`)
1982
+ }
1983
+
1984
+ return { currentVersion: match[1], manifestPath }
1985
+ }
1986
+
1987
+ async readCurrentVersionFromRegistry (_tree, _currentVersionResolverMetadata) {
1988
+ return {
1989
+ currentVersion: null,
1990
+ logText: 'C# libraries resolve their current version from git tags, not a registry lookup',
1991
+ }
1992
+ }
1993
+
1994
+ async readCurrentVersionOfDependency (_tree, _projectGraph, _dependencyProjectName) {
1995
+ return { currentVersion: null, dependencyCollection: null }
1996
+ }
1997
+
1998
+ async updateProjectVersion (tree, newVersion) {
1999
+ const manifestPath = findCsproj(tree, this.projectGraphNode.data.root)
2000
+ if (!manifestPath) {
2001
+ throw new Error(\`No .csproj found in \${this.projectGraphNode.data.root}\`)
2002
+ }
2003
+ const contents = tree.read(manifestPath, 'utf8') ?? ''
2004
+ if (!VERSION_TAG.test(contents)) {
2005
+ throw new Error(\`Could not find a <Version> element in \${manifestPath}\`)
2006
+ }
2007
+ tree.write(manifestPath, contents.replace(VERSION_TAG, \`<Version>\${newVersion}</Version>\`))
2008
+
2009
+ return [\`Updated \${manifestPath} to version \${newVersion}\`]
2010
+ }
2011
+
2012
+ async updateProjectDependencies (_tree, _projectGraph, _dependenciesToUpdate) {
2013
+ return []
2014
+ }
2015
+
2016
+ async validate (tree) {
2017
+ if (!findCsproj(tree, this.projectGraphNode.data.root)) {
2018
+ throw new Error(\`The project "\${this.projectGraphNode.name}" does not have a .csproj file available in \${this.projectGraphNode.data.root}\`)
2019
+ }
2020
+ }
1806
2021
  }
2022
+
2023
+ module.exports = CsharpVersionActions
2024
+ `;
2025
+ function writeCsharpVersionActions(workspaceRoot) {
2026
+ writeFileEnsured((0, import_node_path4.join)(workspaceRoot, CSHARP_VERSION_ACTIONS_PATH), CSHARP_VERSION_ACTIONS);
2027
+ }
2028
+ function addProjectJsonReleaseVersionActions(projectJsonPath) {
2029
+ const project = fileExists(projectJsonPath) ? readJson(projectJsonPath) : {};
2030
+ const release = project.release ?? {};
2031
+ const version = release.version ?? {};
2032
+ writeFileEnsured(
2033
+ projectJsonPath,
2034
+ toJson({
2035
+ ...project,
2036
+ release: { ...release, version: { ...version, versionActions: CSHARP_VERSION_ACTIONS_PATH } }
2037
+ })
2038
+ );
2039
+ }
2040
+ function addInitialVersion(csprojPath) {
2041
+ const contents = (0, import_node_fs4.readFileSync)(csprojPath, "utf8");
2042
+ writeFileEnsured(csprojPath, contents.replace("<PropertyGroup>", "<PropertyGroup>\n <Version>0.1.0</Version>"));
2043
+ }
2044
+ function csharpLibPublishTarget(projectRoot) {
2045
+ const outDir = `${projectRoot}/dist`;
2046
+ const command = `node -e "if(!process.env.NUGET_PAT){console.log('NuGet publish is not configured for this registry choice (no NUGET_PAT) - skipping. Regenerate with --registry azure-artifacts, or run dotnet nuget push manually.');process.exit(0)}const cp=require('node:child_process');const pack=cp.spawnSync('dotnet',['pack','${projectRoot}','-c','Release','-o','${outDir}'],{stdio:'inherit',shell:true});if(pack.status!==0)process.exit(pack.status??1);const fs=require('node:fs');for(const pkg of fs.globSync('${outDir}/*.nupkg')){const push=cp.spawnSync('dotnet',['nuget','push',pkg,'--source','${NUGET_AZURE_SOURCE}','--api-key','AZ'],{stdio:'inherit',shell:true});if(push.status!==0)process.exit(push.status??1)}"`;
2047
+ return { executor: "nx:run-commands", options: { command } };
2048
+ }
2049
+ async function addCsharpLib(workspaceRoot, name, options, kindProvided) {
2050
+ ensureDotnet(workspaceRoot);
2051
+ ensurePlugin(workspaceRoot, "@nx/dotnet");
2052
+ const scope = options.scope ?? (kindProvided ? defaultScope(workspaceRoot) : await promptText("NuGet package scope for the published library", defaultScope(workspaceRoot)));
2053
+ const projectRoot = `packages/${name}`;
2054
+ const identity = `${pascalScope(scope)}.${pascalCase(name)}`;
2055
+ scaffoldDotnetProject(workspaceRoot, projectRoot, identity, "classlib");
2056
+ addInitialVersion((0, import_node_path4.join)(workspaceRoot, projectRoot, `${identity}.csproj`));
2057
+ writeCsharpVersionActions(workspaceRoot);
2058
+ addProjectJsonReleaseVersionActions((0, import_node_path4.join)(workspaceRoot, projectRoot, "project.json"));
2059
+ addProjectJsonTargets((0, import_node_path4.join)(workspaceRoot, projectRoot, "project.json"), {
2060
+ "nx-release-publish": csharpLibPublishTarget(projectRoot)
2061
+ });
2062
+ const registry = readMnciConfig(workspaceRoot).registry ?? { kind: "npm" };
2063
+ writeFileEnsured((0, import_node_path4.join)(workspaceRoot, "nuget.config"), nugetConfigContent(registry, scope));
2064
+ registerProjectCommands(workspaceRoot, name, { build: true });
2065
+ }
2066
+ function addCsharpInternalLib(workspaceRoot, name) {
2067
+ ensureDotnet(workspaceRoot);
2068
+ ensurePlugin(workspaceRoot, "@nx/dotnet");
2069
+ const projectRoot = `libs/${name}`;
2070
+ const identity = pascalCase(name);
2071
+ scaffoldDotnetProject(workspaceRoot, projectRoot, identity, "classlib");
2072
+ registerProjectCommands(workspaceRoot, name, { build: false });
2073
+ logger.step(
2074
+ `Reference it from a consumer with: dotnet add <consumer>.csproj reference ${projectRoot}/${identity}.csproj`
2075
+ );
2076
+ }
2077
+ var CSHARP_FUNCTION_APP_HOST_JSON = `{
2078
+ "version": "2.0",
2079
+ "extensionBundle": {
2080
+ "id": "Microsoft.Azure.Functions.ExtensionBundle",
2081
+ "version": "[4.*, 5.0.0)"
2082
+ }
2083
+ }
2084
+ `;
2085
+ function csharpFunctionAppCsproj() {
2086
+ return `<Project Sdk="Azure.Functions.Sdk/1.0.0">
2087
+ <PropertyGroup>
2088
+ <TargetFramework>${targetFramework()}</TargetFramework>
2089
+ </PropertyGroup>
2090
+
2091
+ <ItemGroup>
2092
+ <PackageReference Include="Microsoft.Azure.Functions.Worker" Version="2.52.0" />
2093
+ <PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http.AspNetCore" Version="2.1.0" />
2094
+ </ItemGroup>
2095
+ </Project>
2096
+ `;
2097
+ }
2098
+ var CSHARP_FUNCTION_APP_PROGRAM = `using Microsoft.Azure.Functions.Worker.Builder;
2099
+ using Microsoft.Extensions.Hosting;
2100
+
2101
+ var builder = FunctionsApplication.CreateBuilder(args);
2102
+
2103
+ builder.ConfigureFunctionsWebApplication();
2104
+
2105
+ builder.Build().Run();
2106
+ `;
2107
+ function csharpFunctionAppHello(identity) {
2108
+ return `using Microsoft.AspNetCore.Http;
2109
+ using Microsoft.AspNetCore.Mvc;
2110
+ using Microsoft.Azure.Functions.Worker;
2111
+
2112
+ namespace ${identity};
2113
+
2114
+ public class Hello
2115
+ {
2116
+ [Function("Hello")]
2117
+ public IActionResult Run([HttpTrigger(AuthorizationLevel.Anonymous, "get")] HttpRequest req)
2118
+ {
2119
+ return new OkObjectResult("Hello from mnci.");
2120
+ }
2121
+ }
2122
+ `;
2123
+ }
2124
+ function csharpFunctionAppStartTarget(projectRoot) {
2125
+ return {
2126
+ executor: "nx:run-commands",
2127
+ continuous: true,
2128
+ options: { command: "dotnet run", cwd: projectRoot }
2129
+ };
2130
+ }
2131
+ function addCsharpFunctionApp(workspaceRoot, name) {
2132
+ ensureDotnet(workspaceRoot);
2133
+ ensurePlugin(workspaceRoot, "@nx/dotnet");
2134
+ ensureAdmZip(workspaceRoot);
2135
+ const projectRoot = `apps/${name}`;
2136
+ const identity = pascalCase(name);
2137
+ scaffoldDotnetProject(workspaceRoot, projectRoot, identity, "console");
2138
+ const absoluteRoot = (0, import_node_path4.join)(workspaceRoot, projectRoot);
2139
+ writeFileEnsured((0, import_node_path4.join)(absoluteRoot, `${identity}.csproj`), csharpFunctionAppCsproj());
2140
+ writeFileEnsured((0, import_node_path4.join)(absoluteRoot, "Program.cs"), CSHARP_FUNCTION_APP_PROGRAM);
2141
+ writeFileEnsured((0, import_node_path4.join)(absoluteRoot, "Hello.cs"), csharpFunctionAppHello(identity));
2142
+ writeFileEnsured((0, import_node_path4.join)(absoluteRoot, "host.json"), CSHARP_FUNCTION_APP_HOST_JSON);
2143
+ addProjectJsonTargets((0, import_node_path4.join)(absoluteRoot, "project.json"), {
2144
+ package: csharpAppPackageTarget("csharp-function-app", projectRoot, name),
2145
+ start: csharpFunctionAppStartTarget(projectRoot)
2146
+ });
2147
+ registerProjectCommands(workspaceRoot, name, { build: true, start: `nx run ${name}:start` });
2148
+ }
2149
+
2150
+ // src/commands/add/flutter.ts
2151
+ var import_node_path5 = require("path");
1807
2152
  function flutterAppStartTarget(name) {
1808
2153
  return {
1809
2154
  executor: "nx:run-commands",
@@ -1839,7 +2184,7 @@ function addFlutterApp(workspaceRoot, name) {
1839
2184
  prepareFlutter(workspaceRoot);
1840
2185
  ensureAdmZip(workspaceRoot);
1841
2186
  runNx(["g", "@mnci/nx-flutter:application", name, "--no-interactive"], workspaceRoot);
1842
- addProjectJsonTargets((0, import_node_path4.join)(workspaceRoot, "apps", name, "project.json"), {
2187
+ addProjectJsonTargets((0, import_node_path5.join)(workspaceRoot, "apps", name, "project.json"), {
1843
2188
  start: flutterAppStartTarget(name)
1844
2189
  });
1845
2190
  registerProjectCommands(workspaceRoot, name, { build: true, start: `nx run ${name}:start` });
@@ -1856,8 +2201,8 @@ function addFlutterInternalLib(workspaceRoot, name) {
1856
2201
  }
1857
2202
 
1858
2203
  // src/commands/add/go.ts
1859
- var import_node_fs4 = require("fs");
1860
- var import_node_path5 = require("path");
2204
+ var import_node_fs5 = require("fs");
2205
+ var import_node_path6 = require("path");
1861
2206
  function ensureGo(workspaceRoot) {
1862
2207
  if (runShell("go", ["version"], workspaceRoot) !== 0) {
1863
2208
  throw new Error("Go not found. Install Go 1.21+ first: https://go.dev/dl/");
@@ -1884,7 +2229,7 @@ function ensureNxGoPlugin(workspaceRoot) {
1884
2229
  }
1885
2230
  }
1886
2231
  function ensureGoModule(workspaceRoot) {
1887
- if (fileExists((0, import_node_path5.join)(workspaceRoot, "go.mod"))) {
2232
+ if (fileExists((0, import_node_path6.join)(workspaceRoot, "go.mod"))) {
1888
2233
  return;
1889
2234
  }
1890
2235
  logger.step("Bootstrapping the workspace Go module (single root go.mod)");
@@ -1893,17 +2238,12 @@ function ensureGoModule(workspaceRoot) {
1893
2238
  }
1894
2239
  function goModulePath(workspaceRoot) {
1895
2240
  try {
1896
- const contents = (0, import_node_fs4.readFileSync)((0, import_node_path5.join)(workspaceRoot, "go.mod"), "utf8");
2241
+ const contents = (0, import_node_fs5.readFileSync)((0, import_node_path6.join)(workspaceRoot, "go.mod"), "utf8");
1897
2242
  return /^module\s+(\S+)/m.exec(contents)?.[1];
1898
2243
  } catch {
1899
2244
  return void 0;
1900
2245
  }
1901
2246
  }
1902
- function addProjectJsonTargets2(projectJsonPath, newTargets) {
1903
- const project = readJson(projectJsonPath);
1904
- const targets = project.targets ?? {};
1905
- writeFileEnsured(projectJsonPath, toJson({ ...project, targets: { ...targets, ...newTargets } }));
1906
- }
1907
2247
  function goTestTarget() {
1908
2248
  return { executor: "@nx-go/nx-go:test" };
1909
2249
  }
@@ -1958,7 +2298,7 @@ function addGoApp(workspaceRoot, name) {
1958
2298
  ],
1959
2299
  workspaceRoot
1960
2300
  );
1961
- addProjectJsonTargets2((0, import_node_path5.join)(workspaceRoot, "apps", name, "project.json"), {
2301
+ addProjectJsonTargets((0, import_node_path6.join)(workspaceRoot, "apps", name, "project.json"), {
1962
2302
  build: goBuildTarget(name),
1963
2303
  test: goTestTarget(),
1964
2304
  lint: goLintTarget(),
@@ -1981,7 +2321,7 @@ function addGoFunctionApp(workspaceRoot, name) {
1981
2321
  ],
1982
2322
  workspaceRoot
1983
2323
  );
1984
- addProjectJsonTargets2((0, import_node_path5.join)(workspaceRoot, "apps", name, "project.json"), {
2324
+ addProjectJsonTargets((0, import_node_path6.join)(workspaceRoot, "apps", name, "project.json"), {
1985
2325
  build: goBuildTarget(name),
1986
2326
  test: goTestTarget(),
1987
2327
  lint: goLintTarget(),
@@ -2002,7 +2342,7 @@ function addGoLib(workspaceRoot, name) {
2002
2342
  ],
2003
2343
  workspaceRoot
2004
2344
  );
2005
- addProjectJsonTargets2((0, import_node_path5.join)(workspaceRoot, "packages", name, "project.json"), {
2345
+ addProjectJsonTargets((0, import_node_path6.join)(workspaceRoot, "packages", name, "project.json"), {
2006
2346
  test: goTestTarget(),
2007
2347
  lint: goLintTarget()
2008
2348
  });
@@ -2025,7 +2365,7 @@ function addGoInternalLib(workspaceRoot, name) {
2025
2365
  ],
2026
2366
  workspaceRoot
2027
2367
  );
2028
- addProjectJsonTargets2((0, import_node_path5.join)(workspaceRoot, "libs", name, "project.json"), {
2368
+ addProjectJsonTargets((0, import_node_path6.join)(workspaceRoot, "libs", name, "project.json"), {
2029
2369
  test: goTestTarget(),
2030
2370
  lint: goLintTarget()
2031
2371
  });
@@ -2037,7 +2377,7 @@ function addGoInternalLib(workspaceRoot, name) {
2037
2377
  }
2038
2378
 
2039
2379
  // src/commands/add/node.ts
2040
- var import_node_path6 = require("path");
2380
+ var import_node_path7 = require("path");
2041
2381
  function ensurePlugin2(workspaceRoot, packageName) {
2042
2382
  if (hasPlugin(workspaceRoot, packageName)) {
2043
2383
  return;
@@ -2075,7 +2415,7 @@ function nodeAppPackageTarget(name) {
2075
2415
  function addNodeApp(workspaceRoot, name, stack, framework = "none") {
2076
2416
  runNodeApp(workspaceRoot, name, stack, framework);
2077
2417
  ensureAdmZip(workspaceRoot);
2078
- addNxTargets((0, import_node_path6.join)(workspaceRoot, "apps", name, "package.json"), {
2418
+ addNxTargets((0, import_node_path7.join)(workspaceRoot, "apps", name, "package.json"), {
2079
2419
  package: nodeAppPackageTarget(name)
2080
2420
  });
2081
2421
  removeGeneratedEslintConfig(workspaceRoot, `apps/${name}`);
@@ -2131,10 +2471,10 @@ describe('buildGreeting', () => {
2131
2471
  })
2132
2472
  `;
2133
2473
  function repairNodeFunctionAppManifest(nodeFunctionAppRoot, workspaceRoot) {
2134
- const manifestPath = (0, import_node_path6.join)(nodeFunctionAppRoot, "package.json");
2474
+ const manifestPath = (0, import_node_path7.join)(nodeFunctionAppRoot, "package.json");
2135
2475
  const manifest = readJson(manifestPath);
2136
2476
  const azureFunctionsVersion = readJson(
2137
- (0, import_node_path6.join)(workspaceRoot, "node_modules/@azure/functions/package.json")
2477
+ (0, import_node_path7.join)(workspaceRoot, "node_modules/@azure/functions/package.json")
2138
2478
  ).version;
2139
2479
  const dependencies = manifest.dependencies ?? {};
2140
2480
  writeFileEnsured(
@@ -2168,21 +2508,21 @@ function nodeFunctionAppStartTarget(name) {
2168
2508
  function addNodeFunctionApp(workspaceRoot, name, stack) {
2169
2509
  runNodeApp(workspaceRoot, name, stack, "none");
2170
2510
  ensureAzureFunctionsPackage(workspaceRoot);
2171
- const nodeFunctionAppRoot = (0, import_node_path6.join)(workspaceRoot, "apps", name);
2172
- writeFileEnsured((0, import_node_path6.join)(nodeFunctionAppRoot, "src/main.ts"), NODE_FUNCTION_APP_MAIN);
2173
- writeFileEnsured((0, import_node_path6.join)(nodeFunctionAppRoot, "src/functions/hello.ts"), NODE_FUNCTION_APP_HELLO);
2511
+ const nodeFunctionAppRoot = (0, import_node_path7.join)(workspaceRoot, "apps", name);
2512
+ writeFileEnsured((0, import_node_path7.join)(nodeFunctionAppRoot, "src/main.ts"), NODE_FUNCTION_APP_MAIN);
2513
+ writeFileEnsured((0, import_node_path7.join)(nodeFunctionAppRoot, "src/functions/hello.ts"), NODE_FUNCTION_APP_HELLO);
2174
2514
  writeFileEnsured(
2175
- (0, import_node_path6.join)(nodeFunctionAppRoot, "src/functions/greeting.ts"),
2515
+ (0, import_node_path7.join)(nodeFunctionAppRoot, "src/functions/greeting.ts"),
2176
2516
  NODE_FUNCTION_APP_GREETING
2177
2517
  );
2178
2518
  writeFileEnsured(
2179
- (0, import_node_path6.join)(nodeFunctionAppRoot, "src/functions/greeting.spec.ts"),
2519
+ (0, import_node_path7.join)(nodeFunctionAppRoot, "src/functions/greeting.spec.ts"),
2180
2520
  NODE_FUNCTION_APP_GREETING_SPEC
2181
2521
  );
2182
- writeFileEnsured((0, import_node_path6.join)(nodeFunctionAppRoot, "host.json"), NODE_FUNCTION_APP_HOST_JSON);
2522
+ writeFileEnsured((0, import_node_path7.join)(nodeFunctionAppRoot, "host.json"), NODE_FUNCTION_APP_HOST_JSON);
2183
2523
  repairNodeFunctionAppManifest(nodeFunctionAppRoot, workspaceRoot);
2184
2524
  ensureAdmZip(workspaceRoot);
2185
- addNxTargets((0, import_node_path6.join)(nodeFunctionAppRoot, "package.json"), {
2525
+ addNxTargets((0, import_node_path7.join)(nodeFunctionAppRoot, "package.json"), {
2186
2526
  package: nodeFunctionAppPackageTarget(name),
2187
2527
  start: nodeFunctionAppStartTarget(name)
2188
2528
  });
@@ -2191,7 +2531,7 @@ function addNodeFunctionApp(workspaceRoot, name, stack) {
2191
2531
  }
2192
2532
 
2193
2533
  // src/commands/add/npmLib.ts
2194
- var import_node_path7 = require("path");
2534
+ var import_node_path8 = require("path");
2195
2535
  async function addNpmLib(workspaceRoot, name, options, kindProvided, stack) {
2196
2536
  const scope = options.scope ?? (kindProvided ? defaultScope(workspaceRoot) : await promptText("npm scope for the published package", defaultScope(workspaceRoot)));
2197
2537
  runNx(
@@ -2208,18 +2548,18 @@ async function addNpmLib(workspaceRoot, name, options, kindProvided, stack) {
2208
2548
  ],
2209
2549
  workspaceRoot
2210
2550
  );
2211
- const manifestPath = (0, import_node_path7.join)(workspaceRoot, "packages", name, "package.json");
2551
+ const manifestPath = (0, import_node_path8.join)(workspaceRoot, "packages", name, "package.json");
2212
2552
  markPublic(manifestPath);
2213
2553
  repairPublishableManifest(manifestPath);
2214
- repairDeclarationSpecifiers((0, import_node_path7.join)(workspaceRoot, `packages/${name}`));
2215
- writeProjectReadme((0, import_node_path7.join)(workspaceRoot, `packages/${name}`), `${scope}/${name}`, stack.testRunner);
2554
+ repairDeclarationSpecifiers((0, import_node_path8.join)(workspaceRoot, `packages/${name}`));
2555
+ writeProjectReadme((0, import_node_path8.join)(workspaceRoot, `packages/${name}`), `${scope}/${name}`, stack.testRunner);
2216
2556
  removeGeneratedEslintConfig(workspaceRoot, `packages/${name}`);
2217
2557
  registerProjectCommands(workspaceRoot, name, { build: true });
2218
2558
  }
2219
2559
 
2220
2560
  // src/commands/add/python.ts
2221
- var import_node_fs5 = require("fs");
2222
- var import_node_path8 = require("path");
2561
+ var import_node_fs6 = require("fs");
2562
+ var import_node_path9 = require("path");
2223
2563
  function ensurePython(workspaceRoot) {
2224
2564
  const hasPython = runShell("python3", ["--version"], workspaceRoot) === 0 || runShell("python", ["--version"], workspaceRoot) === 0;
2225
2565
  if (!hasPython) {
@@ -2248,7 +2588,7 @@ pytest
2248
2588
  pip-audit
2249
2589
  `;
2250
2590
  function ensureRequirementsDev(workspaceRoot) {
2251
- const requirementsDevPath = (0, import_node_path8.join)(workspaceRoot, "requirements-dev.txt");
2591
+ const requirementsDevPath = (0, import_node_path9.join)(workspaceRoot, "requirements-dev.txt");
2252
2592
  if (!fileExists(requirementsDevPath)) {
2253
2593
  writeFileEnsured(requirementsDevPath, PYTHON_REQUIREMENTS_DEV);
2254
2594
  }
@@ -2256,11 +2596,6 @@ function ensureRequirementsDev(workspaceRoot) {
2256
2596
  function pythonModuleDirectory(name) {
2257
2597
  return name.replaceAll("-", "_");
2258
2598
  }
2259
- function addProjectJsonTargets3(projectJsonPath, newTargets) {
2260
- const project = readJson(projectJsonPath);
2261
- const targets = project.targets ?? {};
2262
- writeFileEnsured(projectJsonPath, toJson({ ...project, targets: { ...targets, ...newTargets } }));
2263
- }
2264
2599
  function pythonAppMain(name) {
2265
2600
  return `def main() -> None:
2266
2601
  print('${name} is running')
@@ -2313,8 +2648,8 @@ function addPythonApp(workspaceRoot, name) {
2313
2648
  ["g", "@mnci/nx-python-pip:application", name, `--directory=apps/${name}`, "--no-interactive"],
2314
2649
  workspaceRoot
2315
2650
  );
2316
- writeFileEnsured((0, import_node_path8.join)(workspaceRoot, "apps", name, "main.py"), pythonAppMain(name));
2317
- addProjectJsonTargets3((0, import_node_path8.join)(workspaceRoot, "apps", name, "project.json"), {
2651
+ writeFileEnsured((0, import_node_path9.join)(workspaceRoot, "apps", name, "main.py"), pythonAppMain(name));
2652
+ addProjectJsonTargets((0, import_node_path9.join)(workspaceRoot, "apps", name, "project.json"), {
2318
2653
  package: pythonAppPackageTarget(name),
2319
2654
  start: pythonAppStartTarget(name)
2320
2655
  });
@@ -2336,7 +2671,7 @@ function addPythonFunctionApp(workspaceRoot, name) {
2336
2671
  workspaceRoot
2337
2672
  );
2338
2673
  const moduleDirectory = pythonModuleDirectory(name);
2339
- addProjectJsonTargets3((0, import_node_path8.join)(workspaceRoot, "apps", name, "project.json"), {
2674
+ addProjectJsonTargets((0, import_node_path9.join)(workspaceRoot, "apps", name, "project.json"), {
2340
2675
  package: pythonFunctionAppPackageTarget(name, moduleDirectory),
2341
2676
  start: pythonFunctionAppStartTarget(name)
2342
2677
  });
@@ -2377,7 +2712,7 @@ function addPythonInternalLib(workspaceRoot, name) {
2377
2712
  var PYTHON_PROJECT_DIRS = ["apps", "python-packages", "libs"];
2378
2713
  function findPythonProjectDirectory(workspaceRoot, name) {
2379
2714
  return PYTHON_PROJECT_DIRS.find(
2380
- (directory) => fileExists((0, import_node_path8.join)(workspaceRoot, directory, name, "pyproject.toml"))
2715
+ (directory) => fileExists((0, import_node_path9.join)(workspaceRoot, directory, name, "pyproject.toml"))
2381
2716
  );
2382
2717
  }
2383
2718
  function parseVendorEntries(pyprojectToml) {
@@ -2416,7 +2751,7 @@ async function addPythonVendor(workspaceRoot, consumer, options, kindProvided) {
2416
2751
  `No Python project named '${consumer}' with a pyproject.toml found (checked apps/, python-packages/, libs/). A Python function app has no pyproject.toml \u2014 there is nothing to vendor into.`
2417
2752
  );
2418
2753
  }
2419
- if (!fileExists((0, import_node_path8.join)(workspaceRoot, "libs", lib, "pyproject.toml"))) {
2754
+ if (!fileExists((0, import_node_path9.join)(workspaceRoot, "libs", lib, "pyproject.toml"))) {
2420
2755
  throw new Error(
2421
2756
  `No internal Python library named '${lib}' found at libs/${lib}/pyproject.toml. Only an internal library (added via 'mnci add python-internal-lib') can be vendored.`
2422
2757
  );
@@ -2424,8 +2759,8 @@ async function addPythonVendor(workspaceRoot, consumer, options, kindProvided) {
2424
2759
  if (consumerDirectory === "libs" && consumer === lib) {
2425
2760
  throw new Error(`'${consumer}' cannot vendor itself.`);
2426
2761
  }
2427
- const pyprojectPath = (0, import_node_path8.join)(workspaceRoot, consumerDirectory, consumer, "pyproject.toml");
2428
- const pyprojectToml = (0, import_node_fs5.readFileSync)(pyprojectPath, "utf8");
2762
+ const pyprojectPath = (0, import_node_path9.join)(workspaceRoot, consumerDirectory, consumer, "pyproject.toml");
2763
+ const pyprojectToml = (0, import_node_fs6.readFileSync)(pyprojectPath, "utf8");
2429
2764
  if (parseVendorEntries(pyprojectToml).includes(lib)) {
2430
2765
  logger.info(`'${consumer}' already vendors '${lib}' \u2014 nothing to do.`);
2431
2766
  return;
@@ -2437,8 +2772,8 @@ async function addPythonVendor(workspaceRoot, consumer, options, kindProvided) {
2437
2772
  }
2438
2773
 
2439
2774
  // src/commands/add/reactApp.ts
2440
- var import_node_fs6 = require("fs");
2441
- var import_node_path9 = require("path");
2775
+ var import_node_fs7 = require("fs");
2776
+ var import_node_path10 = require("path");
2442
2777
  var REACT_ENVIRONMENTS = ["dev", "uat", "prod"];
2443
2778
  function reactEnvironmentFile(environment) {
2444
2779
  return `# Vite compiles VITE_-prefixed vars into the '${environment}' bundle at build time.
@@ -2475,12 +2810,12 @@ function reactAppTargets(name) {
2475
2810
  return targets;
2476
2811
  }
2477
2812
  function allowEnvFiles(workspaceRoot) {
2478
- const gitignorePath = (0, import_node_path9.join)(workspaceRoot, ".gitignore");
2813
+ const gitignorePath = (0, import_node_path10.join)(workspaceRoot, ".gitignore");
2479
2814
  if (!fileExists(gitignorePath)) {
2480
2815
  return;
2481
2816
  }
2482
2817
  const marker = "# MoNecromanCI: keep the committed per-environment Vite config (public VITE_ values)";
2483
- const current = (0, import_node_fs6.readFileSync)(gitignorePath, "utf8");
2818
+ const current = (0, import_node_fs7.readFileSync)(gitignorePath, "utf8");
2484
2819
  if (current.includes(marker)) {
2485
2820
  return;
2486
2821
  }
@@ -2507,18 +2842,18 @@ function addReactApp(workspaceRoot, name, stack) {
2507
2842
  workspaceRoot
2508
2843
  );
2509
2844
  ensureAdmZip(workspaceRoot);
2510
- const reactAppRoot = (0, import_node_path9.join)(workspaceRoot, "apps", name);
2845
+ const reactAppRoot = (0, import_node_path10.join)(workspaceRoot, "apps", name);
2511
2846
  for (const environment of REACT_ENVIRONMENTS) {
2512
- writeFileEnsured((0, import_node_path9.join)(reactAppRoot, `.env.${environment}`), reactEnvironmentFile(environment));
2847
+ writeFileEnsured((0, import_node_path10.join)(reactAppRoot, `.env.${environment}`), reactEnvironmentFile(environment));
2513
2848
  }
2514
2849
  allowEnvFiles(workspaceRoot);
2515
- addNxTargets((0, import_node_path9.join)(reactAppRoot, "package.json"), reactAppTargets(name));
2850
+ addNxTargets((0, import_node_path10.join)(reactAppRoot, "package.json"), reactAppTargets(name));
2516
2851
  removeGeneratedEslintConfig(workspaceRoot, `apps/${name}`);
2517
2852
  registerProjectCommands(workspaceRoot, name, { build: true, start: `nx run ${name}:serve` });
2518
2853
  }
2519
2854
 
2520
2855
  // src/commands/add/reactLib.ts
2521
- var import_node_path10 = require("path");
2856
+ var import_node_path11 = require("path");
2522
2857
  var REACT_LIB_BUNDLER = "rollup";
2523
2858
  async function addReactLib(workspaceRoot, name, options, kindProvided, stack) {
2524
2859
  ensurePlugin(workspaceRoot, "@nx/react");
@@ -2538,11 +2873,11 @@ async function addReactLib(workspaceRoot, name, options, kindProvided, stack) {
2538
2873
  ],
2539
2874
  workspaceRoot
2540
2875
  );
2541
- const publishableManifest = (0, import_node_path10.join)(workspaceRoot, "packages", name, "package.json");
2876
+ const publishableManifest = (0, import_node_path11.join)(workspaceRoot, "packages", name, "package.json");
2542
2877
  markPublic(publishableManifest);
2543
2878
  repairPublishableManifest(publishableManifest);
2544
- repairDeclarationSpecifiers((0, import_node_path10.join)(workspaceRoot, "packages", name));
2545
- writeProjectReadme((0, import_node_path10.join)(workspaceRoot, "packages", name), `${scope}/${name}`, stack.testRunner);
2879
+ repairDeclarationSpecifiers((0, import_node_path11.join)(workspaceRoot, "packages", name));
2880
+ writeProjectReadme((0, import_node_path11.join)(workspaceRoot, "packages", name), `${scope}/${name}`, stack.testRunner);
2546
2881
  removeGeneratedEslintConfig(workspaceRoot, `packages/${name}`);
2547
2882
  registerProjectCommands(workspaceRoot, name, { build: true });
2548
2883
  }
@@ -2561,22 +2896,22 @@ function addReactInternalLib(workspaceRoot, name, stack) {
2561
2896
  ],
2562
2897
  workspaceRoot
2563
2898
  );
2564
- const privateManifest = (0, import_node_path10.join)(workspaceRoot, "libs", name, "package.json");
2899
+ const privateManifest = (0, import_node_path11.join)(workspaceRoot, "libs", name, "package.json");
2565
2900
  markPrivate(privateManifest);
2566
2901
  repairPublishableManifest(privateManifest);
2567
- repairDeclarationSpecifiers((0, import_node_path10.join)(workspaceRoot, "libs", name));
2568
- writeProjectReadme((0, import_node_path10.join)(workspaceRoot, "libs", name), name, stack.testRunner);
2902
+ repairDeclarationSpecifiers((0, import_node_path11.join)(workspaceRoot, "libs", name));
2903
+ writeProjectReadme((0, import_node_path11.join)(workspaceRoot, "libs", name), name, stack.testRunner);
2569
2904
  removeGeneratedEslintConfig(workspaceRoot, `libs/${name}`);
2570
2905
  registerProjectCommands(workspaceRoot, name, { build: true });
2571
2906
  }
2572
2907
 
2573
2908
  // src/commands/sync.ts
2574
- var import_node_path12 = require("path");
2909
+ var import_node_path13 = require("path");
2575
2910
 
2576
2911
  // src/deps/inventory.ts
2577
- var import_node_fs7 = require("fs");
2578
- var import_node_path11 = require("path");
2579
- var ECOSYSTEMS = ["npm", "pip", "pub", "go"];
2912
+ var import_node_fs8 = require("fs");
2913
+ var import_node_path12 = require("path");
2914
+ var ECOSYSTEMS = ["npm", "pip", "pub", "nuget", "go"];
2580
2915
  var ROOT_LABEL = "(root)";
2581
2916
  var MANIFEST_GLOBS = {
2582
2917
  // `libs/*` carries private internal libs; `packages/*` the publishable ones.
@@ -2584,6 +2919,10 @@ var MANIFEST_GLOBS = {
2584
2919
  // Matches PYTHON_PROJECT_DIRS in commands/add/python.ts.
2585
2920
  pip: ["apps/*/pyproject.toml", "python-packages/*/pyproject.toml", "libs/*/pyproject.toml"],
2586
2921
  pub: ["apps/*/pubspec.yaml", "packages/*/pubspec.yaml", "libs/*/pubspec.yaml"],
2922
+ // Unlike every other ecosystem, the filename itself varies (the project's own
2923
+ // PascalCase identity, not a fixed name) — the same three roots
2924
+ // PACK_APPS_GUARD and add/csharp.ts already scan.
2925
+ nuget: ["apps/*/*.csproj", "packages/*/*.csproj", "libs/*/*.csproj"],
2587
2926
  // Single root module by design — there are no per-project Go manifests.
2588
2927
  go: []
2589
2928
  };
@@ -2604,20 +2943,23 @@ function projectOf(manifestRelativePath) {
2604
2943
  }
2605
2944
  function hasEcosystem(workspaceRoot, ecosystem) {
2606
2945
  if (ecosystem === "go") {
2607
- return fileExists((0, import_node_path11.join)(workspaceRoot, "go.mod"));
2946
+ return fileExists((0, import_node_path12.join)(workspaceRoot, "go.mod"));
2947
+ }
2948
+ if (ecosystem === "nuget") {
2949
+ return (0, import_node_fs8.globSync)(MANIFEST_GLOBS.nuget, { cwd: workspaceRoot }).length > 0;
2608
2950
  }
2609
2951
  const rootMarker = {
2610
2952
  npm: "package.json",
2611
2953
  pip: "requirements-dev.txt",
2612
2954
  pub: "pubspec.yaml"
2613
2955
  };
2614
- return fileExists((0, import_node_path11.join)(workspaceRoot, rootMarker[ecosystem])) || (0, import_node_fs7.globSync)(MANIFEST_GLOBS[ecosystem], { cwd: workspaceRoot }).length > 0;
2956
+ return fileExists((0, import_node_path12.join)(workspaceRoot, rootMarker[ecosystem])) || (0, import_node_fs8.globSync)(MANIFEST_GLOBS[ecosystem], { cwd: workspaceRoot }).length > 0;
2615
2957
  }
2616
2958
  function collectNpm(workspaceRoot) {
2617
2959
  const sites = [];
2618
- const relativePaths = ["package.json", ...(0, import_node_fs7.globSync)(MANIFEST_GLOBS.npm, { cwd: workspaceRoot })];
2960
+ const relativePaths = ["package.json", ...(0, import_node_fs8.globSync)(MANIFEST_GLOBS.npm, { cwd: workspaceRoot })];
2619
2961
  for (const relativePath of relativePaths) {
2620
- const manifestPath = (0, import_node_path11.join)(workspaceRoot, relativePath);
2962
+ const manifestPath = (0, import_node_path12.join)(workspaceRoot, relativePath);
2621
2963
  try {
2622
2964
  const manifest = readJson(manifestPath);
2623
2965
  sites.push(...npmManifestSites(manifest, relativePath, manifestPath));
@@ -2678,17 +3020,17 @@ function collectPip(workspaceRoot) {
2678
3020
  name: parsed.name,
2679
3021
  ecosystem: "pip",
2680
3022
  project: projectOf(relativePath),
2681
- manifestPath: (0, import_node_path11.join)(workspaceRoot, relativePath),
3023
+ manifestPath: (0, import_node_path12.join)(workspaceRoot, relativePath),
2682
3024
  section,
2683
3025
  spec: parsed.spec,
2684
3026
  rewritable: parsed.rewritable && parsed.spec !== ""
2685
3027
  });
2686
3028
  };
2687
- const pyprojectPaths = (0, import_node_fs7.globSync)(MANIFEST_GLOBS.pip, { cwd: workspaceRoot });
3029
+ const pyprojectPaths = (0, import_node_fs8.globSync)(MANIFEST_GLOBS.pip, { cwd: workspaceRoot });
2688
3030
  for (const relativePath of pyprojectPaths) {
2689
3031
  let content;
2690
3032
  try {
2691
- content = (0, import_node_fs7.readFileSync)((0, import_node_path11.join)(workspaceRoot, relativePath), "utf8");
3033
+ content = (0, import_node_fs8.readFileSync)((0, import_node_path12.join)(workspaceRoot, relativePath), "utf8");
2692
3034
  } catch {
2693
3035
  continue;
2694
3036
  }
@@ -2699,14 +3041,14 @@ function collectPip(workspaceRoot) {
2699
3041
  }
2700
3042
  const requirementsPaths = [
2701
3043
  "requirements-dev.txt",
2702
- ...(0, import_node_fs7.globSync)("apps/*/requirements.txt", { cwd: workspaceRoot })
3044
+ ...(0, import_node_fs8.globSync)("apps/*/requirements.txt", { cwd: workspaceRoot })
2703
3045
  ];
2704
3046
  for (const relativePath of requirementsPaths) {
2705
- if (!fileExists((0, import_node_path11.join)(workspaceRoot, relativePath))) {
3047
+ if (!fileExists((0, import_node_path12.join)(workspaceRoot, relativePath))) {
2706
3048
  continue;
2707
3049
  }
2708
3050
  const section = relativePath === "requirements-dev.txt" ? "devDep" : "dep";
2709
- const lines = (0, import_node_fs7.readFileSync)((0, import_node_path11.join)(workspaceRoot, relativePath), "utf8").split(/\r?\n/);
3051
+ const lines = (0, import_node_fs8.readFileSync)((0, import_node_path12.join)(workspaceRoot, relativePath), "utf8").split(/\r?\n/);
2710
3052
  for (const line of lines) {
2711
3053
  push(relativePath, section, line);
2712
3054
  }
@@ -2744,11 +3086,11 @@ function pyprojectDependencies(content) {
2744
3086
  }
2745
3087
  function collectPub(workspaceRoot) {
2746
3088
  const sites = [];
2747
- const pubspecPaths = (0, import_node_fs7.globSync)(MANIFEST_GLOBS.pub, { cwd: workspaceRoot });
3089
+ const pubspecPaths = (0, import_node_fs8.globSync)(MANIFEST_GLOBS.pub, { cwd: workspaceRoot });
2748
3090
  for (const relativePath of pubspecPaths) {
2749
3091
  let content;
2750
3092
  try {
2751
- content = (0, import_node_fs7.readFileSync)((0, import_node_path11.join)(workspaceRoot, relativePath), "utf8");
3093
+ content = (0, import_node_fs8.readFileSync)((0, import_node_path12.join)(workspaceRoot, relativePath), "utf8");
2752
3094
  } catch {
2753
3095
  continue;
2754
3096
  }
@@ -2758,7 +3100,7 @@ function collectPub(workspaceRoot) {
2758
3100
  name: entry.name,
2759
3101
  ecosystem: "pub",
2760
3102
  project: projectOf(relativePath),
2761
- manifestPath: (0, import_node_path11.join)(workspaceRoot, relativePath),
3103
+ manifestPath: (0, import_node_path12.join)(workspaceRoot, relativePath),
2762
3104
  section: block === "dependencies" ? "dep" : "devDep",
2763
3105
  spec: entry.spec,
2764
3106
  rewritable: entry.spec !== ""
@@ -2806,14 +3148,53 @@ function pubspecBlockEntries(content, block) {
2806
3148
  function stripQuotes(value) {
2807
3149
  return /^(['"]).*\1$/.test(value) ? value.slice(1, -1) : value;
2808
3150
  }
3151
+ function collectNuget(workspaceRoot) {
3152
+ const sites = [];
3153
+ const csprojPaths = (0, import_node_fs8.globSync)(MANIFEST_GLOBS.nuget, { cwd: workspaceRoot });
3154
+ for (const relativePath of csprojPaths) {
3155
+ let content;
3156
+ try {
3157
+ content = (0, import_node_fs8.readFileSync)((0, import_node_path12.join)(workspaceRoot, relativePath), "utf8");
3158
+ } catch {
3159
+ continue;
3160
+ }
3161
+ for (const entry of csprojPackageReferences(content)) {
3162
+ sites.push({
3163
+ name: entry.name,
3164
+ ecosystem: "nuget",
3165
+ project: projectOf(relativePath),
3166
+ manifestPath: (0, import_node_path12.join)(workspaceRoot, relativePath),
3167
+ section: "dep",
3168
+ spec: entry.spec,
3169
+ // A plain SemVer string is rewritable; an MSBuild property reference
3170
+ // ($(SharedVersion)) or an interval-notation range ([1.0,2.0)) is
3171
+ // reported only — neither is a version this can safely overwrite.
3172
+ rewritable: /^\d/.test(entry.spec)
3173
+ });
3174
+ }
3175
+ }
3176
+ return sites;
3177
+ }
3178
+ function csprojPackageReferences(content) {
3179
+ const entries = [];
3180
+ for (const tag of content.matchAll(/<PackageReference([^>]*)\/>/g)) {
3181
+ const attributes = tag[1];
3182
+ const name = /\bInclude\s*=\s*"([^"]*)"/.exec(attributes)?.[1];
3183
+ const spec = /\bVersion\s*=\s*"([^"]*)"/.exec(attributes)?.[1];
3184
+ if (name && spec) {
3185
+ entries.push({ name, spec });
3186
+ }
3187
+ }
3188
+ return entries;
3189
+ }
2809
3190
  function collectGo(workspaceRoot) {
2810
- const manifestPath = (0, import_node_path11.join)(workspaceRoot, "go.mod");
3191
+ const manifestPath = (0, import_node_path12.join)(workspaceRoot, "go.mod");
2811
3192
  if (!fileExists(manifestPath)) {
2812
3193
  return [];
2813
3194
  }
2814
3195
  let content;
2815
3196
  try {
2816
- content = (0, import_node_fs7.readFileSync)(manifestPath, "utf8");
3197
+ content = (0, import_node_fs8.readFileSync)(manifestPath, "utf8");
2817
3198
  } catch {
2818
3199
  return [];
2819
3200
  }
@@ -2835,6 +3216,7 @@ function collectInventory(workspaceRoot, ecosystems = ECOSYSTEMS) {
2835
3216
  npm: collectNpm,
2836
3217
  pip: collectPip,
2837
3218
  pub: collectPub,
3219
+ nuget: collectNuget,
2838
3220
  go: collectGo
2839
3221
  };
2840
3222
  const inventory = /* @__PURE__ */ new Map();
@@ -2869,8 +3251,27 @@ function rewriteSpec(site, spec) {
2869
3251
  writeFileEnsured(site.manifestPath, toJson(manifest));
2870
3252
  return true;
2871
3253
  }
2872
- const before = (0, import_node_fs7.readFileSync)(site.manifestPath, "utf8");
2873
- const after = site.ecosystem === "pip" ? replacePipSpec(before, name, spec) : replacePubSpec(before, name, spec);
3254
+ const before = (0, import_node_fs8.readFileSync)(site.manifestPath, "utf8");
3255
+ const after = (() => {
3256
+ switch (site.ecosystem) {
3257
+ case "pip": {
3258
+ return replacePipSpec(before, name, spec);
3259
+ }
3260
+ case "pub": {
3261
+ return replacePubSpec(before, name, spec);
3262
+ }
3263
+ case "nuget": {
3264
+ return replaceNugetSpec(before, name, spec);
3265
+ }
3266
+ // 'go' entries are never rewritable (see collectGo), so this is
3267
+ // unreachable in practice — returning the text unchanged is the safe
3268
+ // no-op if that invariant is ever broken, rather than guessing at a
3269
+ // rewrite for a format this branch was never meant to handle.
3270
+ default: {
3271
+ return before;
3272
+ }
3273
+ }
3274
+ })();
2874
3275
  if (after === before) {
2875
3276
  return false;
2876
3277
  }
@@ -2890,19 +3291,29 @@ function replacePubSpec(content, name, spec) {
2890
3291
  (match, lead) => `${lead}${spec}`
2891
3292
  );
2892
3293
  }
3294
+ function replaceNugetSpec(content, name, spec) {
3295
+ const escaped = escapeForRegex(name);
3296
+ const includePattern = new RegExp(String.raw`\bInclude\s*=\s*"${escaped}"`);
3297
+ return content.replaceAll(/<PackageReference([^>]*)\/>/g, (tag, attributes) => {
3298
+ if (!includePattern.test(attributes)) {
3299
+ return tag;
3300
+ }
3301
+ return tag.replace(/\bVersion\s*=\s*"[^"]*"/, () => `Version="${spec}"`);
3302
+ });
3303
+ }
2893
3304
  function escapeForRegex(value) {
2894
3305
  return value.replaceAll(/[.*+?^${}()|[\]\\]/g, String.raw`\$&`);
2895
3306
  }
2896
3307
  function isWorkspaceProject(workspaceRoot, name) {
2897
3308
  const short = name.includes("/") ? name.split("/").at(-1) : name;
2898
3309
  return ["apps", "libs", "packages"].some(
2899
- (directory) => fileExists((0, import_node_path11.join)(workspaceRoot, directory, short ?? name, "package.json"))
3310
+ (directory) => fileExists((0, import_node_path12.join)(workspaceRoot, directory, short ?? name, "package.json"))
2900
3311
  );
2901
3312
  }
2902
3313
  function isAliasedInstall(workspaceRoot, name) {
2903
3314
  try {
2904
3315
  const installed = readJson(
2905
- (0, import_node_path11.join)(workspaceRoot, "node_modules", name, "package.json")
3316
+ (0, import_node_path12.join)(workspaceRoot, "node_modules", name, "package.json")
2906
3317
  );
2907
3318
  return installed.name !== void 0 && installed.name !== name;
2908
3319
  } catch {
@@ -2913,14 +3324,14 @@ function resolvedVersion(workspaceRoot, ecosystem, name) {
2913
3324
  if (ecosystem === "npm") {
2914
3325
  try {
2915
3326
  return readJson(
2916
- (0, import_node_path11.join)(workspaceRoot, "node_modules", name, "package.json")
3327
+ (0, import_node_path12.join)(workspaceRoot, "node_modules", name, "package.json")
2917
3328
  ).version;
2918
3329
  } catch {
2919
3330
  return void 0;
2920
3331
  }
2921
3332
  }
2922
3333
  if (ecosystem === "pub") {
2923
- const lockPath = (0, import_node_path11.join)(workspaceRoot, "pubspec.lock");
3334
+ const lockPath = (0, import_node_path12.join)(workspaceRoot, "pubspec.lock");
2924
3335
  if (!fileExists(lockPath)) {
2925
3336
  return void 0;
2926
3337
  }
@@ -2928,7 +3339,7 @@ function resolvedVersion(workspaceRoot, ecosystem, name) {
2928
3339
  const match = new RegExp(
2929
3340
  String.raw`^\s{2}${escapeForRegex(name)}:[^]*?^\s+version:\s*["']?([^"'\s]+)`,
2930
3341
  "m"
2931
- ).exec((0, import_node_fs7.readFileSync)(lockPath, "utf8"));
3342
+ ).exec((0, import_node_fs8.readFileSync)(lockPath, "utf8"));
2932
3343
  return match?.[1];
2933
3344
  } catch {
2934
3345
  return void 0;
@@ -3120,7 +3531,7 @@ function describe(finding) {
3120
3531
  return `${finding.name} \u2192 ${finding.target} (${finding.reason}); disagreeing: ${sites}`;
3121
3532
  }
3122
3533
  function runSync(workspaceRoot, options) {
3123
- if (!fileExists((0, import_node_path12.join)(workspaceRoot, "nx.json"))) {
3534
+ if (!fileExists((0, import_node_path13.join)(workspaceRoot, "nx.json"))) {
3124
3535
  throw new Error(
3125
3536
  `No nx.json found in ${workspaceRoot} \u2014 run 'mnci sync' from the workspace root.`
3126
3537
  );
@@ -3189,16 +3600,20 @@ var PROJECT_KINDS = [
3189
3600
  "go-internal-lib",
3190
3601
  "flutter-app",
3191
3602
  "flutter-lib",
3192
- "flutter-internal-lib"
3603
+ "flutter-internal-lib",
3604
+ "csharp-app",
3605
+ "csharp-function-app",
3606
+ "csharp-lib",
3607
+ "csharp-internal-lib"
3193
3608
  ];
3194
3609
  async function runAdd(kind, name, options) {
3195
3610
  const workspaceRoot = process.cwd();
3196
- if (!fileExists((0, import_node_path13.join)(workspaceRoot, "nx.json"))) {
3611
+ if (!fileExists((0, import_node_path14.join)(workspaceRoot, "nx.json"))) {
3197
3612
  throw new Error("No nx.json found here. Run `add` from the workspace root.");
3198
3613
  }
3199
3614
  const stack = readWorkspaceStack(workspaceRoot);
3200
3615
  const kindProvided = kind !== void 0;
3201
- const resolvedKind = kind ?? await (0, import_prompts5.select)({
3616
+ const resolvedKind = kind ?? await (0, import_prompts6.select)({
3202
3617
  message: "What kind of project?",
3203
3618
  choices: PROJECT_KINDS.map((value) => ({ name: value, value }))
3204
3619
  });
@@ -3243,7 +3658,7 @@ async function runAdd(kind, name, options) {
3243
3658
  ],
3244
3659
  workspaceRoot
3245
3660
  );
3246
- markPrivate((0, import_node_path13.join)(workspaceRoot, "libs", resolvedName, "package.json"));
3661
+ markPrivate((0, import_node_path14.join)(workspaceRoot, "libs", resolvedName, "package.json"));
3247
3662
  removeGeneratedEslintConfig(workspaceRoot, `libs/${resolvedName}`);
3248
3663
  registerProjectCommands(workspaceRoot, resolvedName, { build: true });
3249
3664
  break;
@@ -3292,6 +3707,22 @@ async function runAdd(kind, name, options) {
3292
3707
  addFlutterInternalLib(workspaceRoot, resolvedName);
3293
3708
  break;
3294
3709
  }
3710
+ case "csharp-app": {
3711
+ addCsharpApp(workspaceRoot, resolvedName);
3712
+ break;
3713
+ }
3714
+ case "csharp-function-app": {
3715
+ addCsharpFunctionApp(workspaceRoot, resolvedName);
3716
+ break;
3717
+ }
3718
+ case "csharp-lib": {
3719
+ await addCsharpLib(workspaceRoot, resolvedName, options, kindProvided);
3720
+ break;
3721
+ }
3722
+ case "csharp-internal-lib": {
3723
+ addCsharpInternalLib(workspaceRoot, resolvedName);
3724
+ break;
3725
+ }
3295
3726
  case "python-vendor": {
3296
3727
  await addPythonVendor(workspaceRoot, resolvedName, options, kindProvided);
3297
3728
  syncProjectReferences(workspaceRoot);
@@ -3312,7 +3743,7 @@ async function runAdd(kind, name, options) {
3312
3743
  }
3313
3744
  function readWorkspaceStack(workspaceRoot) {
3314
3745
  const nxJson = readJson(
3315
- (0, import_node_path13.join)(workspaceRoot, "nx.json")
3746
+ (0, import_node_path14.join)(workspaceRoot, "nx.json")
3316
3747
  );
3317
3748
  const stack = nxJson.mnci?.stack;
3318
3749
  return {
@@ -3321,15 +3752,15 @@ function readWorkspaceStack(workspaceRoot) {
3321
3752
  }
3322
3753
 
3323
3754
  // src/commands/doctor.ts
3324
- var import_node_fs8 = require("fs");
3325
- var import_node_path14 = require("path");
3755
+ var import_node_fs9 = require("fs");
3756
+ var import_node_path15 = require("path");
3326
3757
  var SUPPORTED_ESLINT_MAJOR = ESLINT_VERSION.replace(/^\D*/, "").split(".", 1)[0];
3327
3758
  function toPosix2(path) {
3328
3759
  return path.replaceAll("\\", "/");
3329
3760
  }
3330
3761
  function checkEslintConfigs(workspaceRoot) {
3331
- const rootConfigs = (0, import_node_fs8.globSync)("eslint.config.{js,mjs,cjs,ts,mts,cts}", { cwd: workspaceRoot });
3332
- const projectConfigs = (0, import_node_fs8.globSync)("{apps,libs,packages}/*/eslint.config.{js,mjs,cjs,ts,mts,cts}", {
3762
+ const rootConfigs = (0, import_node_fs9.globSync)("eslint.config.{js,mjs,cjs,ts,mts,cts}", { cwd: workspaceRoot });
3763
+ const projectConfigs = (0, import_node_fs9.globSync)("{apps,libs,packages}/*/eslint.config.{js,mjs,cjs,ts,mts,cts}", {
3333
3764
  cwd: workspaceRoot
3334
3765
  }).map((config) => toPosix2(config));
3335
3766
  return [
@@ -3350,7 +3781,7 @@ function checkEslintConfigs(workspaceRoot) {
3350
3781
  function declaredDevDependencies(workspaceRoot) {
3351
3782
  try {
3352
3783
  const manifest = readJson(
3353
- (0, import_node_path14.join)(workspaceRoot, "package.json")
3784
+ (0, import_node_path15.join)(workspaceRoot, "package.json")
3354
3785
  );
3355
3786
  return manifest.devDependencies ?? {};
3356
3787
  } catch {
@@ -3358,7 +3789,7 @@ function declaredDevDependencies(workspaceRoot) {
3358
3789
  }
3359
3790
  }
3360
3791
  function checkNoRetiredFormatter(workspaceRoot) {
3361
- const files = RETIRED_FORMATTER_FILES.filter((file) => fileExists((0, import_node_path14.join)(workspaceRoot, file)));
3792
+ const files = RETIRED_FORMATTER_FILES.filter((file) => fileExists((0, import_node_path15.join)(workspaceRoot, file)));
3362
3793
  const declared = ["prettier", "eslint-config-prettier", "oxlint", "oxfmt", "@mnci/oxlint-config"].filter((name) => declaredDevDependencies(workspaceRoot)[name] !== void 0);
3363
3794
  const stale = [...files, ...declared];
3364
3795
  if (stale.length === 0) {
@@ -3384,7 +3815,7 @@ function checkEslintPlugin(nxJson) {
3384
3815
  };
3385
3816
  }
3386
3817
  function checkResolvedEslint(workspaceRoot) {
3387
- const manifestPath = (0, import_node_path14.join)(workspaceRoot, "node_modules/eslint/package.json");
3818
+ const manifestPath = (0, import_node_path15.join)(workspaceRoot, "node_modules/eslint/package.json");
3388
3819
  if (!fileExists(manifestPath)) {
3389
3820
  return void 0;
3390
3821
  }
@@ -3402,11 +3833,11 @@ function checkResolvedEslint(workspaceRoot) {
3402
3833
  }
3403
3834
  }
3404
3835
  function checkNpmrc(workspaceRoot, registry, scope) {
3405
- const npmrcPath = (0, import_node_path14.join)(workspaceRoot, ".npmrc");
3836
+ const npmrcPath = (0, import_node_path15.join)(workspaceRoot, ".npmrc");
3406
3837
  if (!registry || !fileExists(npmrcPath)) {
3407
3838
  return void 0;
3408
3839
  }
3409
- const npmrc = (0, import_node_fs8.readFileSync)(npmrcPath, "utf8");
3840
+ const npmrc = (0, import_node_fs9.readFileSync)(npmrcPath, "utf8");
3410
3841
  if (registry.kind === "npm") {
3411
3842
  return {
3412
3843
  check: ".npmrc authenticates the public registry",
@@ -3424,12 +3855,12 @@ function checkNpmrc(workspaceRoot, registry, scope) {
3424
3855
  }
3425
3856
  function checkVersionActions(workspaceRoot) {
3426
3857
  const candidates = [
3427
- ...(0, import_node_fs8.globSync)("packages/*/pubspec.yaml", { cwd: workspaceRoot }),
3428
- ...(0, import_node_fs8.globSync)("python-packages/*/pyproject.toml", { cwd: workspaceRoot })
3858
+ ...(0, import_node_fs9.globSync)("packages/*/pubspec.yaml", { cwd: workspaceRoot }),
3859
+ ...(0, import_node_fs9.globSync)("python-packages/*/pyproject.toml", { cwd: workspaceRoot })
3429
3860
  ].map((manifest) => toPosix2(manifest));
3430
3861
  return candidates.flatMap((manifest) => {
3431
3862
  const projectRoot = manifest.slice(0, manifest.lastIndexOf("/"));
3432
- const projectJsonPath = (0, import_node_path14.join)(workspaceRoot, projectRoot, "project.json");
3863
+ const projectJsonPath = (0, import_node_path15.join)(workspaceRoot, projectRoot, "project.json");
3433
3864
  let hasOverride = false;
3434
3865
  try {
3435
3866
  const projectJson = readJson(projectJsonPath);
@@ -3458,7 +3889,7 @@ function declaredTargets(workspaceRoot, projectRoot) {
3458
3889
  ]) {
3459
3890
  try {
3460
3891
  const targets = extract(
3461
- readJson((0, import_node_path14.join)(workspaceRoot, projectRoot, file))
3892
+ readJson((0, import_node_path15.join)(workspaceRoot, projectRoot, file))
3462
3893
  );
3463
3894
  if (targets && typeof targets === "object") {
3464
3895
  sources.push({
@@ -3474,8 +3905,8 @@ function declaredTargets(workspaceRoot, projectRoot) {
3474
3905
  function checkTargetFilesExist(workspaceRoot) {
3475
3906
  const projectRoots = new Set(
3476
3907
  [
3477
- ...(0, import_node_fs8.globSync)("{apps,libs,packages}/*/project.json", { cwd: workspaceRoot }),
3478
- ...(0, import_node_fs8.globSync)("{apps,libs,packages}/*/package.json", { cwd: workspaceRoot })
3908
+ ...(0, import_node_fs9.globSync)("{apps,libs,packages}/*/project.json", { cwd: workspaceRoot }),
3909
+ ...(0, import_node_fs9.globSync)("{apps,libs,packages}/*/package.json", { cwd: workspaceRoot })
3479
3910
  ].map((manifest) => toPosix2(manifest).split("/").slice(0, 2).join("/"))
3480
3911
  );
3481
3912
  return [...projectRoots].flatMap(
@@ -3490,7 +3921,7 @@ function checkOneTargetsFiles(workspaceRoot, source, targetName, target) {
3490
3921
  return TARGET_FILE_OPTIONS.flatMap((option) => {
3491
3922
  const value = target?.options?.[option];
3492
3923
  const checkable = typeof value === "string" && !value.includes("{");
3493
- if (!checkable || fileExists((0, import_node_path14.join)(workspaceRoot, value))) {
3924
+ if (!checkable || fileExists((0, import_node_path15.join)(workspaceRoot, value))) {
3494
3925
  return [];
3495
3926
  }
3496
3927
  return [
@@ -3512,7 +3943,7 @@ function checkSync(workspaceRoot) {
3512
3943
  };
3513
3944
  }
3514
3945
  function checkNoRootRuntimeDependencies(workspaceRoot) {
3515
- const manifestPath = (0, import_node_path14.join)(workspaceRoot, "package.json");
3946
+ const manifestPath = (0, import_node_path15.join)(workspaceRoot, "package.json");
3516
3947
  if (!fileExists(manifestPath)) {
3517
3948
  return void 0;
3518
3949
  }
@@ -3532,14 +3963,14 @@ function checkNoRootRuntimeDependencies(workspaceRoot) {
3532
3963
  };
3533
3964
  }
3534
3965
  function checkRollupSourceMaps(workspaceRoot) {
3535
- const configs = (0, import_node_fs8.globSync)(["packages/*/rollup.config.cjs", "libs/*/rollup.config.cjs"], {
3966
+ const configs = (0, import_node_fs9.globSync)(["packages/*/rollup.config.cjs", "libs/*/rollup.config.cjs"], {
3536
3967
  cwd: workspaceRoot
3537
3968
  }).map((config) => toPosix2(config));
3538
3969
  return configs.flatMap((relativePath) => {
3539
- const configPath = (0, import_node_path14.join)(workspaceRoot, relativePath);
3970
+ const configPath = (0, import_node_path15.join)(workspaceRoot, relativePath);
3540
3971
  let config;
3541
3972
  try {
3542
- config = (0, import_node_fs8.readFileSync)(configPath, "utf8");
3973
+ config = (0, import_node_fs9.readFileSync)(configPath, "utf8");
3543
3974
  } catch {
3544
3975
  return [];
3545
3976
  }
@@ -3554,7 +3985,7 @@ function checkRollupSourceMaps(workspaceRoot) {
3554
3985
  });
3555
3986
  }
3556
3987
  function collectFindings(workspaceRoot) {
3557
- const nxJsonPath = (0, import_node_path14.join)(workspaceRoot, "nx.json");
3988
+ const nxJsonPath = (0, import_node_path15.join)(workspaceRoot, "nx.json");
3558
3989
  if (!fileExists(nxJsonPath)) {
3559
3990
  throw new Error(
3560
3991
  `No nx.json found in ${workspaceRoot} \u2014 run 'mnci doctor' from the workspace root.`
@@ -3596,12 +4027,12 @@ function runDoctor(workspaceRoot) {
3596
4027
  }
3597
4028
 
3598
4029
  // src/commands/interactive.ts
3599
- var import_node_path17 = require("path");
3600
- var import_prompts8 = require("@inquirer/prompts");
4030
+ var import_node_path18 = require("path");
4031
+ var import_prompts9 = require("@inquirer/prompts");
3601
4032
 
3602
4033
  // src/commands/new.ts
3603
- var import_node_fs9 = require("fs");
3604
- var import_node_path15 = require("path");
4034
+ var import_node_fs10 = require("fs");
4035
+ var import_node_path16 = require("path");
3605
4036
  function nxCloudProviderValue(ci) {
3606
4037
  return ci === "azure" ? "azure" : "github";
3607
4038
  }
@@ -3662,7 +4093,7 @@ async function runNew(name, options) {
3662
4093
  ],
3663
4094
  process.cwd()
3664
4095
  );
3665
- const workspaceRoot = (0, import_node_path15.join)(process.cwd(), workspaceName);
4096
+ const workspaceRoot = (0, import_node_path16.join)(process.cwd(), workspaceName);
3666
4097
  logger.step(
3667
4098
  "Applying MoNecromanCI overlay (VS Code workspace, release config, .npmrc, commitlint, pipeline, stack)"
3668
4099
  );
@@ -3671,8 +4102,8 @@ async function runNew(name, options) {
3671
4102
  { workspaceName, scope, registry, agent, variableGroup, ci, stack },
3672
4103
  logger.detail
3673
4104
  );
3674
- (0, import_node_fs9.rmSync)((0, import_node_path15.join)(workspaceRoot, "node_modules"), { recursive: true, force: true });
3675
- (0, import_node_fs9.rmSync)((0, import_node_path15.join)(workspaceRoot, "package-lock.json"), { force: true });
4105
+ (0, import_node_fs10.rmSync)((0, import_node_path16.join)(workspaceRoot, "node_modules"), { recursive: true, force: true });
4106
+ (0, import_node_fs10.rmSync)((0, import_node_path16.join)(workspaceRoot, "package-lock.json"), { force: true });
3676
4107
  logger.step("Installing the toolchain (commitlint + husky + linting/formatting)");
3677
4108
  const installStatus = runShell(
3678
4109
  "npm",
@@ -3702,8 +4133,8 @@ async function runNew(name, options) {
3702
4133
  }
3703
4134
 
3704
4135
  // src/commands/upgrade.ts
3705
- var import_node_fs10 = require("fs");
3706
- var import_node_path16 = require("path");
4136
+ var import_node_fs11 = require("fs");
4137
+ var import_node_path17 = require("path");
3707
4138
  function resolveRegistry2(options, persisted) {
3708
4139
  if (options.registry === "azure-artifacts" || options.organization && options.artifactsFeed) {
3709
4140
  const persistedAzure = persisted?.kind === "azure-artifacts" ? persisted : void 0;
@@ -3732,7 +4163,7 @@ function resolveWorkspaceName(workspaceRoot, persisted) {
3732
4163
  return persisted.workspaceName;
3733
4164
  }
3734
4165
  try {
3735
- const existing = (0, import_node_fs10.readdirSync)(workspaceRoot).find(
4166
+ const existing = (0, import_node_fs11.readdirSync)(workspaceRoot).find(
3736
4167
  (file) => file.endsWith(".code-workspace") && file !== "undefined.code-workspace"
3737
4168
  );
3738
4169
  if (existing) {
@@ -3740,7 +4171,7 @@ function resolveWorkspaceName(workspaceRoot, persisted) {
3740
4171
  }
3741
4172
  } catch {
3742
4173
  }
3743
- return (0, import_node_path16.basename)(workspaceRoot);
4174
+ return (0, import_node_path17.basename)(workspaceRoot);
3744
4175
  }
3745
4176
  function resolveOverlayOptions(workspaceRoot, options, persisted) {
3746
4177
  const scope = options.scope ?? persisted.scope;
@@ -3778,7 +4209,7 @@ function resolveOverlayOptions(workspaceRoot, options, persisted) {
3778
4209
  };
3779
4210
  }
3780
4211
  function runUpgrade(workspaceRoot, options) {
3781
- if (!fileExists((0, import_node_path16.join)(workspaceRoot, "nx.json"))) {
4212
+ if (!fileExists((0, import_node_path17.join)(workspaceRoot, "nx.json"))) {
3782
4213
  throw new Error(
3783
4214
  `No nx.json found in ${workspaceRoot} \u2014 this does not look like an Nx workspace. Run 'mnci upgrade' from the workspace root.`
3784
4215
  );
@@ -3803,14 +4234,14 @@ function runUpgrade(workspaceRoot, options) {
3803
4234
 
3804
4235
  // src/commands/interactive.ts
3805
4236
  async function runInteractive() {
3806
- const inWorkspace = fileExists((0, import_node_path17.join)(process.cwd(), "nx.json"));
4237
+ const inWorkspace = fileExists((0, import_node_path18.join)(process.cwd(), "nx.json"));
3807
4238
  const choiceNew = { name: "Create a new monorepo", value: "new" };
3808
4239
  const choiceAdd = { name: "Add a project to this workspace", value: "add" };
3809
4240
  const choiceUpgrade = {
3810
4241
  name: "Upgrade this workspace (re-apply the latest overlay)",
3811
4242
  value: "upgrade"
3812
4243
  };
3813
- const action = await (0, import_prompts8.select)({
4244
+ const action = await (0, import_prompts9.select)({
3814
4245
  message: "What would you like to do?",
3815
4246
  choices: inWorkspace ? [choiceAdd, choiceUpgrade, choiceNew] : [choiceNew, choiceAdd, choiceUpgrade]
3816
4247
  });
@@ -3824,8 +4255,8 @@ async function runInteractive() {
3824
4255
  }
3825
4256
 
3826
4257
  // src/commands/up.ts
3827
- var import_node_path18 = require("path");
3828
- var import_prompts9 = require("@inquirer/prompts");
4258
+ var import_node_path19 = require("path");
4259
+ var import_prompts10 = require("@inquirer/prompts");
3829
4260
 
3830
4261
  // src/deps/registry.ts
3831
4262
  var LOOKUP_CONCURRENCY = 8;
@@ -3897,6 +4328,27 @@ async function latestPubVersions(cwd) {
3897
4328
  return /* @__PURE__ */ new Map();
3898
4329
  }
3899
4330
  }
4331
+ async function latestNugetVersions(names, cwd) {
4332
+ const results = await pool(names, LOOKUP_CONCURRENCY, async (name) => {
4333
+ const result = await runCaptureAsync(
4334
+ "dotnet",
4335
+ ["package", "search", name, "--exact-match", "--format", "json"],
4336
+ cwd
4337
+ );
4338
+ if (result.status !== 0) {
4339
+ return;
4340
+ }
4341
+ try {
4342
+ const parsed = JSON.parse(result.stdout);
4343
+ const packages = parsed.searchResult?.flatMap((source) => source.packages ?? []) ?? [];
4344
+ const match = packages.find((entry) => entry.id?.toLowerCase() === name.toLowerCase()) ?? packages[0];
4345
+ return match?.latestVersion ? [name, match.latestVersion] : void 0;
4346
+ } catch {
4347
+ return;
4348
+ }
4349
+ });
4350
+ return new Map(results.filter((entry) => entry !== void 0));
4351
+ }
3900
4352
  async function latestVersions(ecosystem, names, cwd) {
3901
4353
  switch (ecosystem) {
3902
4354
  case "npm": {
@@ -3905,10 +4357,17 @@ async function latestVersions(ecosystem, names, cwd) {
3905
4357
  case "pip": {
3906
4358
  return await latestPipVersions(names, cwd);
3907
4359
  }
4360
+ case "nuget": {
4361
+ return await latestNugetVersions(names, cwd);
4362
+ }
3908
4363
  case "go": {
3909
4364
  return await latestGoVersions(cwd);
3910
4365
  }
3911
- default: {
4366
+ // 'pub' explicitly, not a default: a future ecosystem added to the union
4367
+ // without a case here should fail to compile, not silently resolve as
4368
+ // Dart/Flutter's whole-graph query — exactly the trap this switch would
4369
+ // otherwise have laid for 'nuget' itself.
4370
+ case "pub": {
3912
4371
  return await latestPubVersions(cwd);
3913
4372
  }
3914
4373
  }
@@ -4025,13 +4484,13 @@ async function promptForUpdates(outdated) {
4025
4484
  continue;
4026
4485
  }
4027
4486
  const { title, blurb } = UPDATE_KIND_LABELS[kind];
4028
- choices.push(new import_prompts9.Separator(`
4487
+ choices.push(new import_prompts10.Separator(`
4029
4488
  ${title} ${blurb}`));
4030
4489
  for (const entry of section) {
4031
4490
  choices.push({ name: row(entry, widths), value: entry });
4032
4491
  }
4033
4492
  }
4034
- return await (0, import_prompts9.checkbox)({
4493
+ return await (0, import_prompts10.checkbox)({
4035
4494
  message: "Choose which packages to update.",
4036
4495
  choices,
4037
4496
  pageSize: 20
@@ -4055,6 +4514,7 @@ function reinstall(workspaceRoot, ecosystems) {
4055
4514
  ["npm", "npm", ["install"]],
4056
4515
  ["pip", "npm", ["run", "python:install"]],
4057
4516
  ["pub", "flutter", ["pub", "get"]],
4517
+ ["nuget", "npx", ["nx", "run-many", "-t", "restore"]],
4058
4518
  ["go", "go", ["mod", "tidy"]]
4059
4519
  ];
4060
4520
  for (const [ecosystem, command, arguments_] of commands) {
@@ -4078,7 +4538,7 @@ function reinstall(workspaceRoot, ecosystems) {
4078
4538
  function hasPythonInstallScript(workspaceRoot) {
4079
4539
  try {
4080
4540
  const manifest = readJson(
4081
- (0, import_node_path18.join)(workspaceRoot, "package.json")
4541
+ (0, import_node_path19.join)(workspaceRoot, "package.json")
4082
4542
  );
4083
4543
  return manifest.scripts?.["python:install"] !== void 0;
4084
4544
  } catch {
@@ -4094,7 +4554,7 @@ function applyGoUpdates(workspaceRoot, entries) {
4094
4554
  }
4095
4555
  }
4096
4556
  async function runUp(workspaceRoot, options) {
4097
- if (!fileExists((0, import_node_path18.join)(workspaceRoot, "nx.json"))) {
4557
+ if (!fileExists((0, import_node_path19.join)(workspaceRoot, "nx.json"))) {
4098
4558
  throw new Error(`No nx.json found in ${workspaceRoot} \u2014 run 'mnci up' from the workspace root.`);
4099
4559
  }
4100
4560
  const ecosystems = resolveEcosystems(options.ecosystem);
@@ -4142,8 +4602,8 @@ async function runUp(workspaceRoot, options) {
4142
4602
  }
4143
4603
 
4144
4604
  // src/util/versionChecker.ts
4145
- var import_node_fs11 = require("fs");
4146
- var import_node_path19 = require("path");
4605
+ var import_node_fs12 = require("fs");
4606
+ var import_node_path20 = require("path");
4147
4607
  var import_cross_spawn2 = __toESM(require("cross-spawn"));
4148
4608
  var PACKAGE_NAME = "@mnci/cli";
4149
4609
  var REGISTRY_TIMEOUT_MS = 2e3;
@@ -4199,8 +4659,8 @@ function releaseTriple(version) {
4199
4659
  }
4200
4660
  function readCliVersion(packageDirectory) {
4201
4661
  try {
4202
- const manifestPath = (0, import_node_path19.join)(packageDirectory, "..", "package.json");
4203
- const manifest = JSON.parse((0, import_node_fs11.readFileSync)(manifestPath, "utf8"));
4662
+ const manifestPath = (0, import_node_path20.join)(packageDirectory, "..", "package.json");
4663
+ const manifest = JSON.parse((0, import_node_fs12.readFileSync)(manifestPath, "utf8"));
4204
4664
  return manifest.version ?? "0.0.0";
4205
4665
  } catch {
4206
4666
  return "0.0.0";