@mnci/cli 1.6.0 → 1.8.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
@@ -35,13 +35,11 @@ __export(cli_exports, {
35
35
  main: () => main
36
36
  });
37
37
  module.exports = __toCommonJS(cli_exports);
38
- var import_node_fs3 = require("fs");
39
- var import_node_path12 = require("path");
40
38
  var import_commander = require("commander");
41
39
 
42
40
  // src/commands/add.ts
43
- var import_node_path7 = require("path");
44
- var import_prompts3 = require("@inquirer/prompts");
41
+ var import_node_path8 = require("path");
42
+ var import_prompts4 = require("@inquirer/prompts");
45
43
 
46
44
  // src/nx.ts
47
45
  var import_cross_spawn = __toESM(require("cross-spawn"));
@@ -100,14 +98,13 @@ async function promptCi() {
100
98
  ]
101
99
  });
102
100
  }
103
- async function promptStack() {
104
- const linter = await (0, import_prompts.select)({
105
- message: "Linter",
106
- choices: [
107
- { name: "ESLint", value: "eslint" },
108
- { name: "Oxlint", value: "oxlint" }
109
- ]
101
+ async function promptNxCloud() {
102
+ return await (0, import_prompts.confirm)({
103
+ message: "Connect this workspace to Nx Cloud (remote caching + CI insights)?",
104
+ default: false
110
105
  });
106
+ }
107
+ async function promptStack() {
111
108
  const testRunner = await (0, import_prompts.select)({
112
109
  message: "Unit-test runner",
113
110
  choices: [
@@ -115,7 +112,7 @@ async function promptStack() {
115
112
  { name: "Vitest", value: "vitest" }
116
113
  ]
117
114
  });
118
- return { linter, testRunner };
115
+ return { testRunner };
119
116
  }
120
117
 
121
118
  // src/util/fsx.ts
@@ -161,11 +158,14 @@ var logger = {
161
158
  // src/util/names.ts
162
159
  function assertValidProjectName(name, label) {
163
160
  if (!/^[a-z][a-z0-9-]*$/.test(name)) {
164
- throw new Error(`${label} '${name}' is invalid \u2014 use lowercase letters, digits and hyphens, starting with a letter (e.g. 'my-project').`);
161
+ throw new Error(
162
+ `${label} '${name}' is invalid \u2014 use lowercase letters, digits and hyphens, starting with a letter (e.g. 'my-project').`
163
+ );
165
164
  }
166
165
  }
167
166
 
168
- // src/commands/add/node.ts
167
+ // src/commands/add/go.ts
168
+ var import_node_fs2 = require("fs");
169
169
  var import_node_path3 = require("path");
170
170
 
171
171
  // src/commands/add/shared.ts
@@ -180,7 +180,11 @@ function ensureAdmZip(workspaceRoot) {
180
180
  return;
181
181
  }
182
182
  logger.step("Installing the app packager (adm-zip)");
183
- if (runShell("npm", ["install", "--save-dev", "adm-zip", "--no-audit", "--no-fund"], workspaceRoot) !== 0) {
183
+ if (runShell(
184
+ "npm",
185
+ ["install", "--save-dev", "adm-zip", "--no-audit", "--no-fund"],
186
+ workspaceRoot
187
+ ) !== 0) {
184
188
  throw new Error("npm install of adm-zip failed");
185
189
  }
186
190
  }
@@ -193,10 +197,179 @@ function addNxTargets(manifestPath, newTargets) {
193
197
  const manifest = fileExists(manifestPath) ? readJson(manifestPath) : {};
194
198
  const nx = manifest.nx ?? {};
195
199
  const targets = nx.targets ?? {};
196
- writeFileEnsured(manifestPath, toJson({ ...manifest, nx: { ...nx, targets: { ...targets, ...newTargets } } }));
200
+ writeFileEnsured(
201
+ manifestPath,
202
+ toJson({ ...manifest, nx: { ...nx, targets: { ...targets, ...newTargets } } })
203
+ );
204
+ }
205
+
206
+ // src/commands/add/go.ts
207
+ function ensureGo(workspaceRoot) {
208
+ if (runShell("go", ["version"], workspaceRoot) !== 0) {
209
+ throw new Error("Go not found. Install Go 1.21+ first: https://go.dev/dl/");
210
+ }
211
+ }
212
+ function warnIfNoGolangciLint(workspaceRoot) {
213
+ if (runShell("golangci-lint", ["--version"], workspaceRoot) !== 0) {
214
+ logger.warn(
215
+ "golangci-lint not found \u2014 the generated lint target needs it. Install: https://golangci-lint.run/welcome/install/"
216
+ );
217
+ }
218
+ }
219
+ function nxGoPluginSpec() {
220
+ return process.env.MNCI_NX_GO_SPEC ?? "@nx-go/nx-go";
221
+ }
222
+ function ensureNxGoPlugin(workspaceRoot) {
223
+ if (hasPlugin(workspaceRoot, "@nx-go/nx-go")) {
224
+ return;
225
+ }
226
+ const spec = nxGoPluginSpec();
227
+ logger.step(`Installing the Go toolchain plugin (${spec})`);
228
+ if (runShell("npm", ["install", "--save-dev", spec, "--no-audit", "--no-fund"], workspaceRoot) !== 0) {
229
+ throw new Error("npm install of @nx-go/nx-go failed");
230
+ }
231
+ }
232
+ function ensureGoModule(workspaceRoot) {
233
+ if (fileExists((0, import_node_path3.join)(workspaceRoot, "go.mod"))) {
234
+ return;
235
+ }
236
+ logger.step("Bootstrapping the workspace Go module (single root go.mod)");
237
+ runNx(["g", "@nx-go/nx-go:init", "--no-interactive"], workspaceRoot);
238
+ runNx(["g", "@nx-go/nx-go:convert-to-one-mod", "--no-interactive"], workspaceRoot);
239
+ }
240
+ function goModulePath(workspaceRoot) {
241
+ try {
242
+ const contents = (0, import_node_fs2.readFileSync)((0, import_node_path3.join)(workspaceRoot, "go.mod"), "utf8");
243
+ return /^module\s+(\S+)/m.exec(contents)?.[1];
244
+ } catch {
245
+ return void 0;
246
+ }
247
+ }
248
+ function addProjectJsonTargets(projectJsonPath, newTargets) {
249
+ const project = readJson(projectJsonPath);
250
+ const targets = project.targets ?? {};
251
+ writeFileEnsured(projectJsonPath, toJson({ ...project, targets: { ...targets, ...newTargets } }));
252
+ }
253
+ function goTestTarget() {
254
+ return { executor: "@nx-go/nx-go:test" };
255
+ }
256
+ function goLintTarget() {
257
+ return { executor: "@nx-go/nx-go:lint", options: { linter: "golangci-lint", args: ["run"] } };
258
+ }
259
+ function goBuildTarget(name) {
260
+ return {
261
+ executor: "@nx-go/nx-go:build",
262
+ // eslint-disable-next-line unicorn/no-incorrect-template-string-interpolation -- {workspaceRoot} is an Nx output token
263
+ outputs: [`{workspaceRoot}/dist/apps/${name}`],
264
+ options: { outputPath: `../../dist/apps/${name}/${name}` }
265
+ };
266
+ }
267
+ function goPackageTarget(tag, name) {
268
+ const zip = `dist/drop/${tag}-${name}.zip`;
269
+ const command = `node -e "const fs=require('node:fs');fs.mkdirSync('dist/drop',{recursive:true});const A=require('adm-zip');const z=new A();z.addLocalFolder('dist/apps/${name}');z.writeZip('${zip}')"`;
270
+ return {
271
+ executor: "nx:run-commands",
272
+ dependsOn: ["build"],
273
+ // eslint-disable-next-line unicorn/no-incorrect-template-string-interpolation -- {workspaceRoot} is an Nx output token
274
+ outputs: [`{workspaceRoot}/${zip}`],
275
+ options: { command }
276
+ };
277
+ }
278
+ function prepareGo(workspaceRoot) {
279
+ ensureGo(workspaceRoot);
280
+ warnIfNoGolangciLint(workspaceRoot);
281
+ ensureNxGoPlugin(workspaceRoot);
282
+ ensureGoModule(workspaceRoot);
283
+ }
284
+ function addGoApp(workspaceRoot, name) {
285
+ prepareGo(workspaceRoot);
286
+ ensureAdmZip(workspaceRoot);
287
+ runNx(
288
+ [
289
+ "g",
290
+ "@nx-go/nx-go:application",
291
+ `apps/${name}`,
292
+ `--name=${name}`,
293
+ "--tags=type:go-app",
294
+ "--no-interactive"
295
+ ],
296
+ workspaceRoot
297
+ );
298
+ addProjectJsonTargets((0, import_node_path3.join)(workspaceRoot, "apps", name, "project.json"), {
299
+ build: goBuildTarget(name),
300
+ test: goTestTarget(),
301
+ lint: goLintTarget(),
302
+ package: goPackageTarget("go-app", name)
303
+ });
304
+ }
305
+ function addGoFunctionApp(workspaceRoot, name) {
306
+ prepareGo(workspaceRoot);
307
+ ensureAdmZip(workspaceRoot);
308
+ runNx(
309
+ [
310
+ "g",
311
+ "@nx-go/nx-go:application",
312
+ `apps/${name}`,
313
+ `--name=${name}`,
314
+ "--tags=type:go-function-app",
315
+ "--no-interactive"
316
+ ],
317
+ workspaceRoot
318
+ );
319
+ addProjectJsonTargets((0, import_node_path3.join)(workspaceRoot, "apps", name, "project.json"), {
320
+ build: goBuildTarget(name),
321
+ test: goTestTarget(),
322
+ lint: goLintTarget(),
323
+ package: goPackageTarget("go-function-app", name)
324
+ });
325
+ }
326
+ function addGoLib(workspaceRoot, name) {
327
+ prepareGo(workspaceRoot);
328
+ runNx(
329
+ [
330
+ "g",
331
+ "@nx-go/nx-go:library",
332
+ `packages/${name}`,
333
+ `--name=${name}`,
334
+ "--tags=type:go-lib",
335
+ "--no-interactive"
336
+ ],
337
+ workspaceRoot
338
+ );
339
+ addProjectJsonTargets((0, import_node_path3.join)(workspaceRoot, "packages", name, "project.json"), {
340
+ test: goTestTarget(),
341
+ lint: goLintTarget()
342
+ });
343
+ const module2 = goModulePath(workspaceRoot);
344
+ if (module2) {
345
+ logger.step(`Import it as ${module2}/packages/${name}`);
346
+ }
347
+ }
348
+ function addGoInternalLib(workspaceRoot, name) {
349
+ prepareGo(workspaceRoot);
350
+ runNx(
351
+ [
352
+ "g",
353
+ "@nx-go/nx-go:library",
354
+ `libs/${name}`,
355
+ `--name=${name}`,
356
+ "--tags=type:go-internal-lib",
357
+ "--no-interactive"
358
+ ],
359
+ workspaceRoot
360
+ );
361
+ addProjectJsonTargets((0, import_node_path3.join)(workspaceRoot, "libs", name, "project.json"), {
362
+ test: goTestTarget(),
363
+ lint: goLintTarget()
364
+ });
365
+ const module2 = goModulePath(workspaceRoot);
366
+ if (module2) {
367
+ logger.step(`Import it as ${module2}/libs/${name}`);
368
+ }
197
369
  }
198
370
 
199
371
  // src/commands/add/node.ts
372
+ var import_node_path4 = require("path");
200
373
  function ensurePlugin(workspaceRoot, packageName) {
201
374
  if (hasPlugin(workspaceRoot, packageName)) {
202
375
  return;
@@ -206,17 +379,20 @@ function ensurePlugin(workspaceRoot, packageName) {
206
379
  }
207
380
  function runNodeApp(workspaceRoot, name, stack, framework) {
208
381
  ensurePlugin(workspaceRoot, "@nx/node");
209
- runNx([
210
- "g",
211
- "@nx/node:application",
212
- `apps/${name}`,
213
- "--bundler=esbuild",
214
- `--unitTestRunner=${stack.testRunner}`,
215
- `--linter=${stack.linter}`,
216
- "--e2eTestRunner=none",
217
- `--framework=${framework}`,
218
- "--no-interactive"
219
- ], workspaceRoot);
382
+ runNx(
383
+ [
384
+ "g",
385
+ "@nx/node:application",
386
+ `apps/${name}`,
387
+ "--bundler=esbuild",
388
+ `--unitTestRunner=${stack.testRunner}`,
389
+ "--linter=eslint",
390
+ "--e2eTestRunner=none",
391
+ `--framework=${framework}`,
392
+ "--no-interactive"
393
+ ],
394
+ workspaceRoot
395
+ );
220
396
  }
221
397
  function nodeAppPackageTarget(name) {
222
398
  const zip = `dist/drop/node-app-${name}.zip`;
@@ -232,7 +408,9 @@ function nodeAppPackageTarget(name) {
232
408
  function addNodeApp(workspaceRoot, name, stack, framework = "none") {
233
409
  runNodeApp(workspaceRoot, name, stack, framework);
234
410
  ensureAdmZip(workspaceRoot);
235
- addNxTargets((0, import_node_path3.join)(workspaceRoot, "apps", name, "package.json"), { package: nodeAppPackageTarget(name) });
411
+ addNxTargets((0, import_node_path4.join)(workspaceRoot, "apps", name, "package.json"), {
412
+ package: nodeAppPackageTarget(name)
413
+ });
236
414
  }
237
415
  function ensureAzureFunctionsPackage(workspaceRoot) {
238
416
  if (hasPlugin(workspaceRoot, "@azure/functions")) {
@@ -283,15 +461,20 @@ describe('buildGreeting', () => {
283
461
  })
284
462
  `;
285
463
  function repairNodeFunctionAppManifest(nodeFunctionAppRoot, workspaceRoot) {
286
- const manifestPath = (0, import_node_path3.join)(nodeFunctionAppRoot, "package.json");
464
+ const manifestPath = (0, import_node_path4.join)(nodeFunctionAppRoot, "package.json");
287
465
  const manifest = readJson(manifestPath);
288
- const azureFunctionsVersion = readJson((0, import_node_path3.join)(workspaceRoot, "node_modules/@azure/functions/package.json")).version;
466
+ const azureFunctionsVersion = readJson(
467
+ (0, import_node_path4.join)(workspaceRoot, "node_modules/@azure/functions/package.json")
468
+ ).version;
289
469
  const dependencies = manifest.dependencies ?? {};
290
- writeFileEnsured(manifestPath, toJson({
291
- ...manifest,
292
- main: "main.js",
293
- dependencies: { ...dependencies, "@azure/functions": `^${azureFunctionsVersion}` }
294
- }));
470
+ writeFileEnsured(
471
+ manifestPath,
472
+ toJson({
473
+ ...manifest,
474
+ main: "main.js",
475
+ dependencies: { ...dependencies, "@azure/functions": `^${azureFunctionsVersion}` }
476
+ })
477
+ );
295
478
  }
296
479
  function nodeFunctionAppPackageTarget(name) {
297
480
  const zip = `dist/drop/node-function-app-${name}.zip`;
@@ -308,19 +491,27 @@ function nodeFunctionAppPackageTarget(name) {
308
491
  function addNodeFunctionApp(workspaceRoot, name, stack) {
309
492
  runNodeApp(workspaceRoot, name, stack, "none");
310
493
  ensureAzureFunctionsPackage(workspaceRoot);
311
- const nodeFunctionAppRoot = (0, import_node_path3.join)(workspaceRoot, "apps", name);
312
- writeFileEnsured((0, import_node_path3.join)(nodeFunctionAppRoot, "src/main.ts"), NODE_FUNCTION_APP_MAIN);
313
- writeFileEnsured((0, import_node_path3.join)(nodeFunctionAppRoot, "src/functions/hello.ts"), NODE_FUNCTION_APP_HELLO);
314
- writeFileEnsured((0, import_node_path3.join)(nodeFunctionAppRoot, "src/functions/greeting.ts"), NODE_FUNCTION_APP_GREETING);
315
- writeFileEnsured((0, import_node_path3.join)(nodeFunctionAppRoot, "src/functions/greeting.spec.ts"), NODE_FUNCTION_APP_GREETING_SPEC);
316
- writeFileEnsured((0, import_node_path3.join)(nodeFunctionAppRoot, "host.json"), NODE_FUNCTION_APP_HOST_JSON);
494
+ const nodeFunctionAppRoot = (0, import_node_path4.join)(workspaceRoot, "apps", name);
495
+ writeFileEnsured((0, import_node_path4.join)(nodeFunctionAppRoot, "src/main.ts"), NODE_FUNCTION_APP_MAIN);
496
+ writeFileEnsured((0, import_node_path4.join)(nodeFunctionAppRoot, "src/functions/hello.ts"), NODE_FUNCTION_APP_HELLO);
497
+ writeFileEnsured(
498
+ (0, import_node_path4.join)(nodeFunctionAppRoot, "src/functions/greeting.ts"),
499
+ NODE_FUNCTION_APP_GREETING
500
+ );
501
+ writeFileEnsured(
502
+ (0, import_node_path4.join)(nodeFunctionAppRoot, "src/functions/greeting.spec.ts"),
503
+ NODE_FUNCTION_APP_GREETING_SPEC
504
+ );
505
+ writeFileEnsured((0, import_node_path4.join)(nodeFunctionAppRoot, "host.json"), NODE_FUNCTION_APP_HOST_JSON);
317
506
  repairNodeFunctionAppManifest(nodeFunctionAppRoot, workspaceRoot);
318
507
  ensureAdmZip(workspaceRoot);
319
- addNxTargets((0, import_node_path3.join)(nodeFunctionAppRoot, "package.json"), { package: nodeFunctionAppPackageTarget(name) });
508
+ addNxTargets((0, import_node_path4.join)(nodeFunctionAppRoot, "package.json"), {
509
+ package: nodeFunctionAppPackageTarget(name)
510
+ });
320
511
  }
321
512
 
322
513
  // src/commands/add/npmLib.ts
323
- var import_node_path4 = require("path");
514
+ var import_node_path5 = require("path");
324
515
  var NPM_LIB_ESLINT_CONFIG = `import { globSync, readFileSync } from 'node:fs';
325
516
  import { dirname, join } from 'node:path';
326
517
  import { fileURLToPath } from 'node:url';
@@ -363,21 +554,25 @@ export default [
363
554
  `;
364
555
  async function addNpmLib(workspaceRoot, name, options, kindProvided, stack) {
365
556
  const scope = options.scope ?? (kindProvided ? defaultScope(workspaceRoot) : await promptText("npm scope for the published package", defaultScope(workspaceRoot)));
366
- runNx([
367
- "g",
368
- "@nx/js:lib",
369
- `packages/${name}`,
370
- "--publishable",
371
- `--importPath=${scope}/${name}`,
372
- "--bundler=rollup",
373
- `--unitTestRunner=${stack.testRunner}`,
374
- `--linter=${stack.linter}`,
375
- "--no-interactive"
376
- ], workspaceRoot);
377
- markPublic((0, import_node_path4.join)(workspaceRoot, "packages", name, "package.json"));
378
- if (stack.linter === "eslint") {
379
- writeFileEnsured((0, import_node_path4.join)(workspaceRoot, "packages", name, "eslint.config.mjs"), NPM_LIB_ESLINT_CONFIG);
380
- }
557
+ runNx(
558
+ [
559
+ "g",
560
+ "@nx/js:lib",
561
+ `packages/${name}`,
562
+ "--publishable",
563
+ `--importPath=${scope}/${name}`,
564
+ "--bundler=rollup",
565
+ `--unitTestRunner=${stack.testRunner}`,
566
+ "--linter=eslint",
567
+ "--no-interactive"
568
+ ],
569
+ workspaceRoot
570
+ );
571
+ markPublic((0, import_node_path5.join)(workspaceRoot, "packages", name, "package.json"));
572
+ writeFileEnsured(
573
+ (0, import_node_path5.join)(workspaceRoot, "packages", name, "eslint.config.mjs"),
574
+ NPM_LIB_ESLINT_CONFIG
575
+ );
381
576
  }
382
577
  function markPublic(manifestPath) {
383
578
  const manifest = readJson(manifestPath);
@@ -385,11 +580,14 @@ function markPublic(manifestPath) {
385
580
  }
386
581
 
387
582
  // src/commands/add/python.ts
388
- var import_node_path5 = require("path");
583
+ var import_node_fs3 = require("fs");
584
+ var import_node_path6 = require("path");
389
585
  function ensurePython(workspaceRoot) {
390
586
  const hasPython = runShell("python3", ["--version"], workspaceRoot) === 0 || runShell("python", ["--version"], workspaceRoot) === 0;
391
587
  if (!hasPython) {
392
- throw new Error("Python not found. Install Python 3.9+ first: https://www.python.org/downloads/");
588
+ throw new Error(
589
+ "Python not found. Install Python 3.9+ first: https://www.python.org/downloads/"
590
+ );
393
591
  }
394
592
  }
395
593
  function pythonPipPluginSpec() {
@@ -412,7 +610,7 @@ pytest
412
610
  pip-audit
413
611
  `;
414
612
  function ensureRequirementsDev(workspaceRoot) {
415
- const requirementsDevPath = (0, import_node_path5.join)(workspaceRoot, "requirements-dev.txt");
613
+ const requirementsDevPath = (0, import_node_path6.join)(workspaceRoot, "requirements-dev.txt");
416
614
  if (!fileExists(requirementsDevPath)) {
417
615
  writeFileEnsured(requirementsDevPath, PYTHON_REQUIREMENTS_DEV);
418
616
  }
@@ -420,7 +618,7 @@ function ensureRequirementsDev(workspaceRoot) {
420
618
  function pythonModuleDirectory(name) {
421
619
  return name.replaceAll("-", "_");
422
620
  }
423
- function addProjectJsonTargets(projectJsonPath, newTargets) {
621
+ function addProjectJsonTargets2(projectJsonPath, newTargets) {
424
622
  const project = readJson(projectJsonPath);
425
623
  const targets = project.targets ?? {};
426
624
  writeFileEnsured(projectJsonPath, toJson({ ...project, targets: { ...targets, ...newTargets } }));
@@ -452,34 +650,129 @@ function addPythonApp(workspaceRoot, name) {
452
650
  ensurePythonPipPlugin(workspaceRoot);
453
651
  ensureRequirementsDev(workspaceRoot);
454
652
  ensureAdmZip(workspaceRoot);
455
- runNx(["g", "@mnci/nx-python-pip:application", name, `--directory=apps/${name}`, "--no-interactive"], workspaceRoot);
456
- addProjectJsonTargets((0, import_node_path5.join)(workspaceRoot, "apps", name, "project.json"), { package: pythonAppPackageTarget(name) });
653
+ runNx(
654
+ ["g", "@mnci/nx-python-pip:application", name, `--directory=apps/${name}`, "--no-interactive"],
655
+ workspaceRoot
656
+ );
657
+ addProjectJsonTargets2((0, import_node_path6.join)(workspaceRoot, "apps", name, "project.json"), {
658
+ package: pythonAppPackageTarget(name)
659
+ });
457
660
  }
458
661
  function addPythonFunctionApp(workspaceRoot, name) {
459
662
  ensurePython(workspaceRoot);
460
663
  ensurePythonPipPlugin(workspaceRoot);
461
664
  ensureRequirementsDev(workspaceRoot);
462
665
  ensureAdmZip(workspaceRoot);
463
- runNx(["g", "@mnci/nx-python-pip:function-application", name, `--directory=apps/${name}`, "--no-interactive"], workspaceRoot);
666
+ runNx(
667
+ [
668
+ "g",
669
+ "@mnci/nx-python-pip:function-application",
670
+ name,
671
+ `--directory=apps/${name}`,
672
+ "--no-interactive"
673
+ ],
674
+ workspaceRoot
675
+ );
464
676
  const moduleDirectory = pythonModuleDirectory(name);
465
- addProjectJsonTargets((0, import_node_path5.join)(workspaceRoot, "apps", name, "project.json"), { package: pythonFunctionAppPackageTarget(name, moduleDirectory) });
677
+ addProjectJsonTargets2((0, import_node_path6.join)(workspaceRoot, "apps", name, "project.json"), {
678
+ package: pythonFunctionAppPackageTarget(name, moduleDirectory)
679
+ });
466
680
  }
467
681
  function addPythonLib(workspaceRoot, name) {
468
682
  ensurePython(workspaceRoot);
469
683
  ensurePythonPipPlugin(workspaceRoot);
470
684
  ensureRequirementsDev(workspaceRoot);
471
- runNx(["g", "@mnci/nx-python-pip:library", name, `--directory=python-packages/${name}`, "--no-interactive"], workspaceRoot);
685
+ runNx(
686
+ [
687
+ "g",
688
+ "@mnci/nx-python-pip:library",
689
+ name,
690
+ `--directory=python-packages/${name}`,
691
+ "--no-interactive"
692
+ ],
693
+ workspaceRoot
694
+ );
472
695
  }
473
696
  function addPythonInternalLib(workspaceRoot, name) {
474
697
  ensurePython(workspaceRoot);
475
698
  ensurePythonPipPlugin(workspaceRoot);
476
699
  ensureRequirementsDev(workspaceRoot);
477
- runNx(["g", "@mnci/nx-python-pip:internal-library", name, `--directory=libs/${name}`, "--no-interactive"], workspaceRoot);
700
+ runNx(
701
+ [
702
+ "g",
703
+ "@mnci/nx-python-pip:internal-library",
704
+ name,
705
+ `--directory=libs/${name}`,
706
+ "--no-interactive"
707
+ ],
708
+ workspaceRoot
709
+ );
710
+ }
711
+ var PYTHON_PROJECT_DIRS = ["apps", "python-packages", "libs"];
712
+ function findPythonProjectDirectory(workspaceRoot, name) {
713
+ return PYTHON_PROJECT_DIRS.find(
714
+ (directory) => fileExists((0, import_node_path6.join)(workspaceRoot, directory, name, "pyproject.toml"))
715
+ );
716
+ }
717
+ function parseVendorEntries(pyprojectToml) {
718
+ const match = /^\s*vendor\s*=\s*\[([^\]]*)\]/m.exec(pyprojectToml);
719
+ if (!match) {
720
+ return [];
721
+ }
722
+ return match[1].split(",").map((entry) => entry.trim().replaceAll(/^["']|["']$/g, "")).filter(Boolean);
723
+ }
724
+ function addVendorEntry(pyprojectToml, lib) {
725
+ const existing = parseVendorEntries(pyprojectToml);
726
+ if (existing.length > 0) {
727
+ const merged = [...existing, lib].map((name) => `"${name}"`).join(", ");
728
+ return pyprojectToml.replace(
729
+ /^(\s*vendor\s*=\s*)\[([^\]]*)\]/m,
730
+ (_match, prefix) => `${prefix}[${merged}]`
731
+ );
732
+ }
733
+ const table = `[tool.mnci-python-pip]
734
+ vendor = ["${lib}"]
735
+
736
+ `;
737
+ return pyprojectToml.includes("[tool.pytest.ini_options]") ? pyprojectToml.replace("[tool.pytest.ini_options]", () => `${table}[tool.pytest.ini_options]`) : `${pyprojectToml}
738
+ ${table}`;
739
+ }
740
+ async function addPythonVendor(workspaceRoot, consumer, options, kindProvided) {
741
+ const lib = options.lib ?? (kindProvided ? void 0 : await promptText("Internal Python library to vendor (an existing libs/<name>)"));
742
+ if (!lib) {
743
+ throw new Error(
744
+ "`add python-vendor` needs the library to vendor: pass --lib <name> (an existing libs/<name> internal library)."
745
+ );
746
+ }
747
+ const consumerDirectory = findPythonProjectDirectory(workspaceRoot, consumer);
748
+ if (!consumerDirectory) {
749
+ throw new Error(
750
+ `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.`
751
+ );
752
+ }
753
+ if (!fileExists((0, import_node_path6.join)(workspaceRoot, "libs", lib, "pyproject.toml"))) {
754
+ throw new Error(
755
+ `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.`
756
+ );
757
+ }
758
+ if (consumerDirectory === "libs" && consumer === lib) {
759
+ throw new Error(`'${consumer}' cannot vendor itself.`);
760
+ }
761
+ const pyprojectPath = (0, import_node_path6.join)(workspaceRoot, consumerDirectory, consumer, "pyproject.toml");
762
+ const pyprojectToml = (0, import_node_fs3.readFileSync)(pyprojectPath, "utf8");
763
+ if (parseVendorEntries(pyprojectToml).includes(lib)) {
764
+ logger.info(`'${consumer}' already vendors '${lib}' \u2014 nothing to do.`);
765
+ return;
766
+ }
767
+ writeFileEnsured(pyprojectPath, addVendorEntry(pyprojectToml, lib));
768
+ logger.success(
769
+ `'${consumer}' now vendors '${lib}' \u2014 its wheel will include '${lib}''s module on the next build.`
770
+ );
478
771
  }
479
772
 
480
773
  // src/commands/add/reactApp.ts
481
- var import_node_fs2 = require("fs");
482
- var import_node_path6 = require("path");
774
+ var import_node_fs4 = require("fs");
775
+ var import_node_path7 = require("path");
483
776
  function ensurePlugin2(workspaceRoot, packageName) {
484
777
  if (hasPlugin(workspaceRoot, packageName)) {
485
778
  return;
@@ -502,26 +795,35 @@ function reactAppTargets(name) {
502
795
  executor: "nx:run-commands",
503
796
  // eslint-disable-next-line unicorn/no-incorrect-template-string-interpolation -- {workspaceRoot} is an Nx output token
504
797
  outputs: [`{workspaceRoot}/apps/${name}/dist-${environment}`],
505
- options: { command: `vite build --mode ${environment} --outDir dist-${environment}`, cwd: `apps/${name}` }
798
+ options: {
799
+ command: `vite build --mode ${environment} --outDir dist-${environment}`,
800
+ cwd: `apps/${name}`
801
+ }
506
802
  };
507
803
  }
508
- const zipStatements = REACT_ENVIRONMENTS.map((environment) => `z=new A();z.addLocalFolder('apps/${name}/dist-${environment}');z.writeZip('dist/drop/react-app-${name}-${environment}.zip')`).join(";");
804
+ const zipStatements = REACT_ENVIRONMENTS.map(
805
+ (environment) => `z=new A();z.addLocalFolder('apps/${name}/dist-${environment}');z.writeZip('dist/drop/react-app-${name}-${environment}.zip')`
806
+ ).join(";");
509
807
  targets.package = {
510
808
  executor: "nx:run-commands",
511
809
  dependsOn: REACT_ENVIRONMENTS.map((environment) => `build-${environment}`),
512
- // eslint-disable-next-line unicorn/no-incorrect-template-string-interpolation -- {workspaceRoot} tokens, not JS interpolation
513
- outputs: REACT_ENVIRONMENTS.map((environment) => `{workspaceRoot}/dist/drop/react-app-${name}-${environment}.zip`),
514
- options: { command: `node -e "const fs=require('node:fs');fs.mkdirSync('dist/drop',{recursive:true});const A=require('adm-zip');let z;${zipStatements}"` }
810
+ outputs: REACT_ENVIRONMENTS.map(
811
+ // eslint-disable-next-line unicorn/no-incorrect-template-string-interpolation
812
+ (environment) => `{workspaceRoot}/dist/drop/react-app-${name}-${environment}.zip`
813
+ ),
814
+ options: {
815
+ command: `node -e "const fs=require('node:fs');fs.mkdirSync('dist/drop',{recursive:true});const A=require('adm-zip');let z;${zipStatements}"`
816
+ }
515
817
  };
516
818
  return targets;
517
819
  }
518
820
  function allowEnvFiles(workspaceRoot) {
519
- const gitignorePath = (0, import_node_path6.join)(workspaceRoot, ".gitignore");
821
+ const gitignorePath = (0, import_node_path7.join)(workspaceRoot, ".gitignore");
520
822
  if (!fileExists(gitignorePath)) {
521
823
  return;
522
824
  }
523
825
  const marker = "# MoNecromanCI: keep the committed per-environment Vite config (public VITE_ values)";
524
- const current = (0, import_node_fs2.readFileSync)(gitignorePath, "utf8");
826
+ const current = (0, import_node_fs4.readFileSync)(gitignorePath, "utf8");
525
827
  if (current.includes(marker)) {
526
828
  return;
527
829
  }
@@ -533,24 +835,27 @@ ${allow}
533
835
  }
534
836
  function addReactApp(workspaceRoot, name, stack) {
535
837
  ensurePlugin2(workspaceRoot, "@nx/react");
536
- runNx([
537
- "g",
538
- "@nx/react:app",
539
- `apps/${name}`,
540
- "--bundler=vite",
541
- `--unitTestRunner=${stack.testRunner}`,
542
- `--linter=${stack.linter}`,
543
- "--style=css",
544
- "--e2eTestRunner=none",
545
- "--no-interactive"
546
- ], workspaceRoot);
838
+ runNx(
839
+ [
840
+ "g",
841
+ "@nx/react:app",
842
+ `apps/${name}`,
843
+ "--bundler=vite",
844
+ `--unitTestRunner=${stack.testRunner}`,
845
+ "--linter=eslint",
846
+ "--style=css",
847
+ "--e2eTestRunner=none",
848
+ "--no-interactive"
849
+ ],
850
+ workspaceRoot
851
+ );
547
852
  ensureAdmZip(workspaceRoot);
548
- const reactAppRoot = (0, import_node_path6.join)(workspaceRoot, "apps", name);
853
+ const reactAppRoot = (0, import_node_path7.join)(workspaceRoot, "apps", name);
549
854
  for (const environment of REACT_ENVIRONMENTS) {
550
- writeFileEnsured((0, import_node_path6.join)(reactAppRoot, `.env.${environment}`), reactEnvironmentFile(environment));
855
+ writeFileEnsured((0, import_node_path7.join)(reactAppRoot, `.env.${environment}`), reactEnvironmentFile(environment));
551
856
  }
552
857
  allowEnvFiles(workspaceRoot);
553
- addNxTargets((0, import_node_path6.join)(reactAppRoot, "package.json"), reactAppTargets(name));
858
+ addNxTargets((0, import_node_path7.join)(reactAppRoot, "package.json"), reactAppTargets(name));
554
859
  }
555
860
 
556
861
  // src/commands/add.ts
@@ -563,16 +868,21 @@ var PROJECT_KINDS = [
563
868
  "python-app",
564
869
  "python-function-app",
565
870
  "python-lib",
566
- "python-internal-lib"
871
+ "python-internal-lib",
872
+ "python-vendor",
873
+ "go-app",
874
+ "go-function-app",
875
+ "go-lib",
876
+ "go-internal-lib"
567
877
  ];
568
878
  async function runAdd(kind, name, options) {
569
879
  const workspaceRoot = process.cwd();
570
- if (!fileExists((0, import_node_path7.join)(workspaceRoot, "nx.json"))) {
880
+ if (!fileExists((0, import_node_path8.join)(workspaceRoot, "nx.json"))) {
571
881
  throw new Error("No nx.json found here. Run `add` from the workspace root.");
572
882
  }
573
883
  const stack = readWorkspaceStack(workspaceRoot);
574
884
  const kindProvided = kind !== void 0;
575
- const resolvedKind = kind ?? await (0, import_prompts3.select)({
885
+ const resolvedKind = kind ?? await (0, import_prompts4.select)({
576
886
  message: "What kind of project?",
577
887
  choices: PROJECT_KINDS.map((value) => ({ name: value, value }))
578
888
  });
@@ -596,16 +906,19 @@ async function runAdd(kind, name, options) {
596
906
  break;
597
907
  }
598
908
  case "internal-lib": {
599
- runNx([
600
- "g",
601
- "@nx/js:lib",
602
- `libs/${resolvedName}`,
603
- "--bundler=tsc",
604
- `--unitTestRunner=${stack.testRunner}`,
605
- `--linter=${stack.linter}`,
606
- "--no-interactive"
607
- ], workspaceRoot);
608
- markPrivate((0, import_node_path7.join)(workspaceRoot, "libs", resolvedName, "package.json"));
909
+ runNx(
910
+ [
911
+ "g",
912
+ "@nx/js:lib",
913
+ `libs/${resolvedName}`,
914
+ "--bundler=tsc",
915
+ `--unitTestRunner=${stack.testRunner}`,
916
+ "--linter=eslint",
917
+ "--no-interactive"
918
+ ],
919
+ workspaceRoot
920
+ );
921
+ markPrivate((0, import_node_path8.join)(workspaceRoot, "libs", resolvedName, "package.json"));
609
922
  break;
610
923
  }
611
924
  case "python-app": {
@@ -624,9 +937,32 @@ async function runAdd(kind, name, options) {
624
937
  addPythonInternalLib(workspaceRoot, resolvedName);
625
938
  break;
626
939
  }
940
+ case "go-app": {
941
+ addGoApp(workspaceRoot, resolvedName);
942
+ break;
943
+ }
944
+ case "go-function-app": {
945
+ addGoFunctionApp(workspaceRoot, resolvedName);
946
+ break;
947
+ }
948
+ case "go-lib": {
949
+ addGoLib(workspaceRoot, resolvedName);
950
+ break;
951
+ }
952
+ case "go-internal-lib": {
953
+ addGoInternalLib(workspaceRoot, resolvedName);
954
+ break;
955
+ }
956
+ case "python-vendor": {
957
+ await addPythonVendor(workspaceRoot, resolvedName, options, kindProvided);
958
+ syncProjectReferences(workspaceRoot);
959
+ return;
960
+ }
627
961
  default: {
628
962
  const exhaustive = resolvedKind;
629
- throw new Error(`Unknown project kind '${exhaustive}'. Expected one of: ${PROJECT_KINDS.join(", ")}.`);
963
+ throw new Error(
964
+ `Unknown project kind '${exhaustive}'. Expected one of: ${PROJECT_KINDS.join(", ")}.`
965
+ );
630
966
  }
631
967
  }
632
968
  syncProjectReferences(workspaceRoot);
@@ -635,14 +971,17 @@ async function runAdd(kind, name, options) {
635
971
  function syncProjectReferences(workspaceRoot) {
636
972
  logger.step("Syncing TypeScript project references (nx sync)");
637
973
  if (runShell("npx", ["nx", "sync"], workspaceRoot) !== 0) {
638
- logger.warn("nx sync did not complete \u2014 run `npx nx sync` yourself so cross-project imports resolve in your editor.");
974
+ logger.warn(
975
+ "nx sync did not complete \u2014 run `npx nx sync` yourself so cross-project imports resolve in your editor."
976
+ );
639
977
  }
640
978
  }
641
979
  function readWorkspaceStack(workspaceRoot) {
642
- const nxJson = readJson((0, import_node_path7.join)(workspaceRoot, "nx.json"));
980
+ const nxJson = readJson(
981
+ (0, import_node_path8.join)(workspaceRoot, "nx.json")
982
+ );
643
983
  const stack = nxJson.mnci?.stack;
644
984
  return {
645
- linter: stack?.linter === "oxlint" ? "none" : "eslint",
646
985
  testRunner: stack?.testRunner === "vitest" ? "vitest" : "jest"
647
986
  };
648
987
  }
@@ -652,15 +991,16 @@ function markPrivate(manifestPath) {
652
991
  }
653
992
 
654
993
  // src/commands/interactive.ts
655
- var import_node_path10 = require("path");
656
- var import_prompts6 = require("@inquirer/prompts");
994
+ var import_node_path12 = require("path");
995
+ var import_prompts7 = require("@inquirer/prompts");
657
996
 
658
997
  // src/commands/new.ts
659
- var import_node_path9 = require("path");
998
+ var import_node_path10 = require("path");
660
999
 
661
1000
  // src/overlay.ts
662
- var import_node_path8 = require("path");
663
- var DEFAULT_STACK = { linter: "eslint", testRunner: "jest" };
1001
+ var import_node_path9 = require("path");
1002
+ var DEFAULT_STACK = { testRunner: "jest" };
1003
+ var PRETTIER_VERSION = "^3.8.1";
664
1004
  var TS_COMPILER_DEPENDENCIES = {
665
1005
  "@typescript/native": "npm:typescript@^7.0.2",
666
1006
  typescript: "npm:@typescript/typescript6@^6.0.2"
@@ -720,6 +1060,77 @@ var COMMITLINT_CONFIG = `export default { extends: ['@commitlint/config-conventi
720
1060
  `;
721
1061
  var COMMIT_MSG_HOOK = `npx --no -- commitlint --edit "$1"
722
1062
  `;
1063
+ var PRETTIER_CONFIG = `{
1064
+ "semi": false,
1065
+ "singleQuote": true,
1066
+ "trailingComma": "es5",
1067
+ "arrowParens": "avoid",
1068
+ "printWidth": 100,
1069
+ "tabWidth": 2,
1070
+ "useTabs": false
1071
+ }
1072
+ `;
1073
+ var PRETTIER_IGNORE = `node_modules
1074
+ dist
1075
+ dist-dev
1076
+ dist-uat
1077
+ dist-prod
1078
+ coverage
1079
+ .next
1080
+ out
1081
+ .nx
1082
+ tmp
1083
+ package-lock.json
1084
+ *.lock
1085
+ `;
1086
+ function vscodeWorkspace(workspaceName) {
1087
+ return JSON.stringify(
1088
+ {
1089
+ folders: [
1090
+ { path: ".", name: workspaceName },
1091
+ { path: "packages/cli", name: "@mnci/cli" },
1092
+ { path: "packages/nx-python-pip", name: "@mnci/nx-python-pip" },
1093
+ { path: "libs/monecromanci-v2", name: "@mnci/monecromanci" }
1094
+ ],
1095
+ settings: {
1096
+ "eslint.validate": [
1097
+ "javascript",
1098
+ "javascriptreact",
1099
+ "typescript",
1100
+ "typescriptreact",
1101
+ "json",
1102
+ "jsonc",
1103
+ "markdown",
1104
+ "yaml"
1105
+ ],
1106
+ "editor.codeActionsOnSave": {
1107
+ "source.fixAll.eslint": "explicit"
1108
+ },
1109
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
1110
+ "editor.formatOnSave": true,
1111
+ "[json]": {
1112
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
1113
+ },
1114
+ "[jsonc]": {
1115
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
1116
+ },
1117
+ "[yaml]": {
1118
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
1119
+ }
1120
+ },
1121
+ extensions: {
1122
+ recommendations: [
1123
+ "dbaeumer.vscode-eslint",
1124
+ "esbenp.prettier-vscode",
1125
+ "nrwl.angular-console",
1126
+ "firsttris.vscode-jest-runner"
1127
+ ]
1128
+ }
1129
+ },
1130
+ null,
1131
+ 2
1132
+ );
1133
+ }
723
1134
  var ROOT_SCRIPTS = {
724
1135
  build: "nx run-many -t build",
725
1136
  lint: "nx run-many -t lint",
@@ -729,19 +1140,19 @@ var ROOT_SCRIPTS = {
729
1140
  "release:preview": "nx release --dry-run",
730
1141
  prepare: "husky"
731
1142
  };
732
- function rootScripts(stack) {
733
- if (stack.linter === "oxlint") {
734
- return {
735
- ...ROOT_SCRIPTS,
736
- lint: "oxlint",
737
- format: "oxfmt -c oxfmt.config.mts .",
738
- "format:check": "oxfmt -c oxfmt.config.mts --check ."
739
- };
740
- }
741
- return { ...ROOT_SCRIPTS };
1143
+ function rootScripts() {
1144
+ return {
1145
+ ...ROOT_SCRIPTS,
1146
+ format: "prettier --write .",
1147
+ "format:check": "prettier --check .",
1148
+ "python:install": `${PYTHON_INSTALL_GUARD} && ${PYTHON_WORKSPACE_INSTALL_GUARD}`
1149
+ };
742
1150
  }
743
1151
  function generatorDefaults(stack) {
744
- const shared = { linter: stack.linter === "oxlint" ? "none" : "eslint", unitTestRunner: stack.testRunner };
1152
+ const shared = {
1153
+ linter: "eslint",
1154
+ unitTestRunner: stack.testRunner
1155
+ };
745
1156
  return {
746
1157
  "@nx/react:application": shared,
747
1158
  "@nx/react:library": shared,
@@ -755,40 +1166,13 @@ function mnciConfig(options) {
755
1166
  agent: options.agent,
756
1167
  variableGroup: options.variableGroup,
757
1168
  ci: options.ci,
758
- stack: { linter: options.stack.linter, testRunner: options.stack.testRunner }
1169
+ stack: { testRunner: options.stack.testRunner }
759
1170
  };
760
1171
  }
761
1172
  function readMnciConfig(workspaceRoot) {
762
- const nxJson = readJson((0, import_node_path8.join)(workspaceRoot, "nx.json"));
1173
+ const nxJson = readJson((0, import_node_path9.join)(workspaceRoot, "nx.json"));
763
1174
  return nxJson.mnci ?? {};
764
1175
  }
765
- var OXLINT_CONFIG = `import { defineConfig } from 'oxlint'
766
- import standard from 'oxc-standard/.oxlintrc.json' with { type: 'json' }
767
-
768
- // Generated by MoNecromanCI. JavaScript Standard Style linting via the
769
- // oxc-standard preset (unicorn + React + react-perf + TypeScript + oxc).
770
- // Add project-specific rule overrides in the \`rules\` block below.
771
- export default defineConfig({
772
- extends: [standard],
773
- rules: {
774
- // The modern JSX transform (Nx's default) needs no React import in scope.
775
- 'react/react-in-jsx-scope': 'off',
776
- },
777
- })
778
- `;
779
- var OXFMT_CONFIG = `import { defineConfig } from 'oxfmt'
780
-
781
- // Generated by MoNecromanCI. JavaScript Standard Style formatting, matching the
782
- // oxc-standard lint preset: no semicolons, single quotes, 2-space indent.
783
- export default defineConfig({
784
- singleQuote: true,
785
- semi: false,
786
- printWidth: 100,
787
- tabWidth: 2,
788
- trailingComma: 'es5',
789
- arrowParens: 'avoid',
790
- })
791
- `;
792
1176
  function pythonPublishUrl(registry) {
793
1177
  if (registry.kind === "azure-artifacts") {
794
1178
  return `https://pkgs.dev.azure.com/${registry.organization}/${registry.project}/_packaging/${registry.artifactsFeed}/pypi/upload/`;
@@ -798,6 +1182,11 @@ function pythonPublishUrl(registry) {
798
1182
  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)"`;
799
1183
  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)"`;
800
1184
  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)"`;
1185
+ var GO_MODULE_DOWNLOAD_GUARD = `node -e "if(!require('node:fs').existsSync('go.mod')){console.log('No Go projects - skipping.');process.exit(0)}process.exit(require('node:child_process').spawnSync('go',['mod','download'],{stdio:'inherit'}).status ?? 1)"`;
1186
+ var GOLANGCI_LINT_INSTALL_GUARD = `node -e "const fs=require('node:fs'),cp=require('node:child_process');if(!fs.existsSync('go.mod')){console.log('No Go projects - skipping.');process.exit(0)}if(cp.spawnSync('golangci-lint',['--version'],{stdio:'ignore'}).status===0){console.log('golangci-lint already installed - skipping.');process.exit(0)}process.exit(cp.spawnSync('go',['install','github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest'],{stdio:'inherit'}).status ?? 1)"`;
1187
+ var GO_TOOL_PATH_PRELUDE = `const fs=require('node:fs'),cp=require('node:child_process');if(!fs.existsSync('go.mod')){console.log('No Go projects - skipping.');process.exit(0)}const r=cp.spawnSync('go',['env','GOPATH'],{encoding:'utf8'});if(r.status!==0){console.log('Could not resolve GOPATH - skipping.');process.exit(0)}const bin=require('node:path').join(r.stdout.trim(),'bin');`;
1188
+ var GO_TOOL_PATH_AZURE = `node -e "${GO_TOOL_PATH_PRELUDE}console.log('##vso[task.prependpath]'+bin)"`;
1189
+ var GO_TOOL_PATH_GITHUB = `node -e "${GO_TOOL_PATH_PRELUDE}if(!process.env.GITHUB_PATH){console.log('Not running in GitHub Actions - skipping.');process.exit(0)}fs.appendFileSync(process.env.GITHUB_PATH,bin+${String.raw`'\n'`})"`;
801
1190
  var NPM_AUDIT_STEP = "npm audit --audit-level=high || echo npm audit found vulnerabilities, see log above - non-blocking, run npm audit locally to inspect";
802
1191
  var PACK_APPS_GUARD = `node -e "const fs=require('node:fs');fs.mkdirSync('dist/drop',{recursive:true});if(fs.globSync('apps/*/project.json').length===0){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
1192
  function releaseGuard(pythonPublishEnv) {
@@ -897,6 +1286,22 @@ steps:
897
1286
  - script: ${PIP_AUDIT_GUARD}
898
1287
  displayName: pip-audit (non-blocking)
899
1288
 
1289
+ # Go, if the workspace has any: hosted agents ship the toolchain, so only
1290
+ # the module cache and the linter need seeding. Both guards skip cleanly on
1291
+ # a workspace with no root go.mod.
1292
+ - script: ${GO_MODULE_DOWNLOAD_GUARD}
1293
+ displayName: Download Go module dependencies
1294
+
1295
+ # golangci-lint is what the generated Go lint target actually runs (the
1296
+ # plugin's own default is 'go fmt', which only reformats). 'go install'
1297
+ # drops it in GOPATH/bin, which is not on PATH by default on a hosted
1298
+ # agent \u2014 so prepend it for every later step in the job.
1299
+ - script: ${GOLANGCI_LINT_INSTALL_GUARD}
1300
+ displayName: Install golangci-lint
1301
+
1302
+ - script: ${GO_TOOL_PATH_AZURE}
1303
+ displayName: Add Go tool bin to PATH
1304
+
900
1305
  # Fails fast, with an unambiguous message, when a stale TypeScript project
901
1306
  # reference (or another sync generator's drift) was never synced+committed
902
1307
  # locally \u2014 sync.applyChanges (nx.json) only auto-applies interactively, so
@@ -960,7 +1365,10 @@ steps:
960
1365
  }
961
1366
  function githubActionsYaml(agent, pythonPublishUrl2, registryKind = "azure-artifacts") {
962
1367
  const onMain = `github.event_name != 'pull_request' && github.ref_name == 'main'`;
963
- const [npmAuthName, npmAuthValue] = npmAuthEnvVariable(registryKind, (name) => `\${{ secrets.${name} }}`);
1368
+ const [npmAuthName, npmAuthValue] = npmAuthEnvVariable(
1369
+ registryKind,
1370
+ (name) => `\${{ secrets.${name} }}`
1371
+ );
964
1372
  return `name: CI
965
1373
 
966
1374
  # Generated by MoNecromanCI. Deliberately thin: Nx builds, 'nx release'
@@ -1030,6 +1438,22 @@ jobs:
1030
1438
  - run: ${PIP_AUDIT_GUARD}
1031
1439
  name: pip-audit (non-blocking)
1032
1440
 
1441
+ # Go, if the workspace has any: hosted runners ship the toolchain, so
1442
+ # only the module cache and the linter need seeding. Both guards skip
1443
+ # cleanly on a workspace with no root go.mod.
1444
+ - run: ${GO_MODULE_DOWNLOAD_GUARD}
1445
+ name: Download Go module dependencies
1446
+
1447
+ # golangci-lint is what the generated Go lint target actually runs (the
1448
+ # plugin's own default is 'go fmt', which only reformats). 'go install'
1449
+ # drops it in GOPATH/bin, which is not on PATH by default on a hosted
1450
+ # runner \u2014 so publish it for every later step in the job.
1451
+ - run: ${GOLANGCI_LINT_INSTALL_GUARD}
1452
+ name: Install golangci-lint
1453
+
1454
+ - run: ${GO_TOOL_PATH_GITHUB}
1455
+ name: Add Go tool bin to PATH
1456
+
1033
1457
  # Fails fast, with an unambiguous message, when a stale TypeScript project
1034
1458
  # reference (or another sync generator's drift) was never synced+committed
1035
1459
  # locally \u2014 sync.applyChanges (nx.json) only auto-applies interactively, so
@@ -1111,50 +1535,70 @@ updates:
1111
1535
  interval: weekly
1112
1536
  `;
1113
1537
  function applyOverlay(workspaceRoot, options) {
1114
- const nxJsonPath = (0, import_node_path8.join)(workspaceRoot, "nx.json");
1538
+ const nxJsonPath = (0, import_node_path9.join)(workspaceRoot, "nx.json");
1115
1539
  const nxJson = readJson(nxJsonPath);
1116
- const generators = { ...nxJson.generators, ...generatorDefaults(options.stack) };
1540
+ const generators = {
1541
+ ...nxJson.generators,
1542
+ ...generatorDefaults(options.stack)
1543
+ };
1117
1544
  const sync = { ...nxJson.sync, ...SYNC_CONFIG };
1118
1545
  const mnci = { ...nxJson.mnci, ...mnciConfig(options) };
1119
1546
  writeFileEnsured(nxJsonPath, toJson({ ...withReleaseConfig(nxJson), generators, sync, mnci }));
1120
- const manifestPath = (0, import_node_path8.join)(workspaceRoot, "package.json");
1547
+ const manifestPath = (0, import_node_path9.join)(workspaceRoot, "package.json");
1121
1548
  const manifest = readJson(manifestPath);
1122
- const scripts = { ...manifest.scripts, ...rootScripts(options.stack) };
1123
- const devDependencies = { ...manifest.devDependencies, ...TS_COMPILER_DEPENDENCIES };
1124
- writeFileEnsured(manifestPath, toJson({ ...manifest, name: `${options.scope}/source`, scripts, devDependencies }));
1125
- writeFileEnsured((0, import_node_path8.join)(workspaceRoot, ".npmrc"), npmrcContent(options.registry, options.scope));
1126
- writeFileEnsured((0, import_node_path8.join)(workspaceRoot, "commitlint.config.mjs"), COMMITLINT_CONFIG);
1127
- const hookPath = (0, import_node_path8.join)(workspaceRoot, ".husky/commit-msg");
1549
+ const scripts = {
1550
+ ...manifest.scripts,
1551
+ ...rootScripts()
1552
+ };
1553
+ const devDeps = {
1554
+ ...manifest.devDependencies,
1555
+ ...TS_COMPILER_DEPENDENCIES,
1556
+ prettier: PRETTIER_VERSION
1557
+ };
1558
+ writeFileEnsured(
1559
+ manifestPath,
1560
+ toJson({ ...manifest, name: `${options.scope}/source`, scripts, devDependencies: devDeps })
1561
+ );
1562
+ writeFileEnsured((0, import_node_path9.join)(workspaceRoot, ".npmrc"), npmrcContent(options.registry, options.scope));
1563
+ writeFileEnsured((0, import_node_path9.join)(workspaceRoot, "commitlint.config.mjs"), COMMITLINT_CONFIG);
1564
+ const hookPath = (0, import_node_path9.join)(workspaceRoot, ".husky/commit-msg");
1128
1565
  writeFileEnsured(hookPath, COMMIT_MSG_HOOK);
1129
1566
  markExecutable(hookPath);
1130
- if (options.stack.linter === "oxlint") {
1131
- writeFileEnsured((0, import_node_path8.join)(workspaceRoot, "oxlint.config.mts"), OXLINT_CONFIG);
1132
- writeFileEnsured((0, import_node_path8.join)(workspaceRoot, "oxfmt.config.mts"), OXFMT_CONFIG);
1133
- }
1567
+ writeFileEnsured((0, import_node_path9.join)(workspaceRoot, ".prettierrc.json"), PRETTIER_CONFIG);
1568
+ writeFileEnsured((0, import_node_path9.join)(workspaceRoot, ".prettierignore"), PRETTIER_IGNORE);
1569
+ writeFileEnsured(
1570
+ (0, import_node_path9.join)(workspaceRoot, `${options.workspaceName}.code-workspace`),
1571
+ vscodeWorkspace(options.workspaceName)
1572
+ );
1134
1573
  const publishUrl = pythonPublishUrl(options.registry);
1135
1574
  if (options.ci === "azure" || options.ci === "both") {
1136
- writeFileEnsured((0, import_node_path8.join)(workspaceRoot, "azure-pipelines.yml"), azurePipelinesYaml(options.agent, options.variableGroup, publishUrl, options.registry.kind));
1575
+ writeFileEnsured(
1576
+ (0, import_node_path9.join)(workspaceRoot, "azure-pipelines.yml"),
1577
+ azurePipelinesYaml(options.agent, options.variableGroup, publishUrl, options.registry.kind)
1578
+ );
1137
1579
  }
1138
1580
  if (options.ci === "github" || options.ci === "both") {
1139
- writeFileEnsured((0, import_node_path8.join)(workspaceRoot, ".github/workflows/ci.yml"), githubActionsYaml(options.agent, publishUrl, options.registry.kind));
1140
- writeFileEnsured((0, import_node_path8.join)(workspaceRoot, ".github/dependabot.yml"), DEPENDABOT_CONFIG);
1581
+ writeFileEnsured(
1582
+ (0, import_node_path9.join)(workspaceRoot, ".github/workflows/ci.yml"),
1583
+ githubActionsYaml(options.agent, publishUrl, options.registry.kind)
1584
+ );
1585
+ writeFileEnsured((0, import_node_path9.join)(workspaceRoot, ".github/dependabot.yml"), DEPENDABOT_CONFIG);
1141
1586
  }
1142
1587
  }
1143
1588
 
1144
1589
  // src/commands/new.ts
1590
+ function nxCloudProviderValue(ci) {
1591
+ return ci === "azure" ? "azure" : "github";
1592
+ }
1145
1593
  async function resolveStack(options) {
1146
- const fromFlags = { linter: options.linter, testRunner: options.testRunner };
1147
- const complete = Boolean(fromFlags.linter && fromFlags.testRunner);
1148
- if (complete || options.yes) {
1594
+ if (options.testRunner || options.yes) {
1149
1595
  return {
1150
- linter: fromFlags.linter ?? DEFAULT_STACK.linter,
1151
- testRunner: fromFlags.testRunner ?? DEFAULT_STACK.testRunner
1596
+ testRunner: options.testRunner ?? DEFAULT_STACK.testRunner
1152
1597
  };
1153
1598
  }
1154
1599
  const prompted = await promptStack();
1155
1600
  return {
1156
- linter: fromFlags.linter ?? prompted.linter,
1157
- testRunner: fromFlags.testRunner ?? prompted.testRunner
1601
+ testRunner: prompted.testRunner
1158
1602
  };
1159
1603
  }
1160
1604
  var CI_PROVIDERS = /* @__PURE__ */ new Set(["azure", "github", "both"]);
@@ -1187,52 +1631,52 @@ async function runNew(name, options) {
1187
1631
  const scope = options.scope ?? (options.yes ? `@${workspaceName}` : await promptText("npm scope for publishable packages", `@${workspaceName}`));
1188
1632
  const registry = await resolveRegistry(options);
1189
1633
  const ci = await resolveCi(options);
1190
- const agent = options.agent ?? (options.yes ? "ubuntu-latest" : await promptText("CI build agent/runner (vmImage, GitHub Actions runner label, or self-hosted pool name)", "ubuntu-latest"));
1634
+ const agent = options.agent ?? (options.yes ? "ubuntu-latest" : await promptText(
1635
+ "CI build agent/runner (vmImage, GitHub Actions runner label, or self-hosted pool name)",
1636
+ "ubuntu-latest"
1637
+ ));
1191
1638
  const variableGroup = ci === "github" ? options.variableGroup ?? "Build" : options.variableGroup ?? (options.yes ? "Build" : await promptText("Azure DevOps variable group holding the npm PAT", "Build"));
1192
1639
  const stack = await resolveStack(options);
1640
+ const nxCloud = options.nxCloud ?? (options.yes ? false : await promptNxCloud());
1193
1641
  logger.step(`Creating Nx workspace '${workspaceName}' (preset: ts)`);
1194
- runNpx([
1195
- "--yes",
1196
- "create-nx-workspace@latest",
1197
- workspaceName,
1198
- "--preset=ts",
1199
- "--pm=npm",
1200
- "--nxCloud=skip",
1201
- "--no-interactive"
1202
- ], process.cwd());
1203
- const workspaceRoot = (0, import_node_path9.join)(process.cwd(), workspaceName);
1204
- logger.step("Applying MoNecromanCI overlay (release config, .npmrc, commitlint, pipeline, stack)");
1205
- applyOverlay(workspaceRoot, { scope, registry, agent, variableGroup, ci, stack });
1206
- const stackDependencies = stack.linter === "oxlint" ? ["oxc-standard"] : [];
1207
- logger.step(`Installing the toolchain (${stack.linter === "oxlint" ? "oxc-standard" : "eslint"}, commit toolchain)`);
1208
- const installStatus = runShell("npm", ["install", "--save-dev", ...stackDependencies, "husky", "@commitlint/cli", "@commitlint/config-conventional"], workspaceRoot);
1642
+ runNpx(
1643
+ [
1644
+ "--yes",
1645
+ "create-nx-workspace@latest",
1646
+ workspaceName,
1647
+ "--preset=ts",
1648
+ "--pm=npm",
1649
+ nxCloud ? `--nxCloud=${nxCloudProviderValue(ci)}` : "--nxCloud=skip",
1650
+ "--no-interactive"
1651
+ ],
1652
+ process.cwd()
1653
+ );
1654
+ const workspaceRoot = (0, import_node_path10.join)(process.cwd(), workspaceName);
1655
+ logger.step(
1656
+ "Applying MoNecromanCI overlay (VS Code workspace, release config, .npmrc, commitlint, pipeline, stack)"
1657
+ );
1658
+ applyOverlay(workspaceRoot, { workspaceName, scope, registry, agent, variableGroup, ci, stack });
1659
+ logger.step("Installing the toolchain (commitlint + husky)");
1660
+ const installStatus = runShell(
1661
+ "npm",
1662
+ ["install", "--save-dev", "husky", "@commitlint/cli", "@commitlint/config-conventional"],
1663
+ workspaceRoot
1664
+ );
1209
1665
  if (installStatus !== 0) {
1210
1666
  throw new Error(`npm install of the toolchain failed with exit code ${installStatus}`);
1211
1667
  }
1212
1668
  runShell("npx", ["husky"], workspaceRoot);
1213
1669
  logger.success("Done. Next steps:");
1214
1670
  logger.info(` cd ${workspaceName}`);
1215
- logger.info(" mnci add react-app web # or: node-app, node-function-app, npm-lib, internal-lib,");
1216
- logger.info(" # python-app, python-function-app, python-lib, python-internal-lib");
1671
+ logger.info(
1672
+ " mnci add react-app web # or: node-app, node-function-app, npm-lib, internal-lib,"
1673
+ );
1674
+ logger.info(
1675
+ " # python-app, python-function-app, python-lib, python-internal-lib"
1676
+ );
1217
1677
  logger.info(' git add -A && git commit -m "feat: initial workspace"');
1218
1678
  }
1219
1679
 
1220
- // src/commands/interactive.ts
1221
- async function runInteractive() {
1222
- const inWorkspace = fileExists((0, import_node_path10.join)(process.cwd(), "nx.json"));
1223
- const newChoice = { name: "Create a new monorepo", value: "new" };
1224
- const addChoice = { name: "Add a project to this workspace", value: "add" };
1225
- const action = await (0, import_prompts6.select)({
1226
- message: "What would you like to do?",
1227
- choices: inWorkspace ? [addChoice, newChoice] : [newChoice, addChoice]
1228
- });
1229
- if (action === "new") {
1230
- await runNew(void 0, {});
1231
- } else {
1232
- await runAdd(void 0, void 0, {});
1233
- }
1234
- }
1235
-
1236
1680
  // src/commands/upgrade.ts
1237
1681
  var import_node_path11 = require("path");
1238
1682
  function resolveRegistry2(options, persisted) {
@@ -1242,7 +1686,9 @@ function resolveRegistry2(options, persisted) {
1242
1686
  const project = options.project ?? persistedAzure?.project;
1243
1687
  const artifactsFeed = options.artifactsFeed ?? persistedAzure?.artifactsFeed;
1244
1688
  if (!organization || !project || !artifactsFeed) {
1245
- throw new Error("Azure Artifacts registry needs --organization, --project and --artifacts-feed (none found in nx.json's persisted config either).");
1689
+ throw new Error(
1690
+ "Azure Artifacts registry needs --organization, --project and --artifacts-feed (none found in nx.json's persisted config either)."
1691
+ );
1246
1692
  }
1247
1693
  return { kind: "azure-artifacts", organization, project, artifactsFeed };
1248
1694
  }
@@ -1252,7 +1698,9 @@ function resolveRegistry2(options, persisted) {
1252
1698
  if (persisted) {
1253
1699
  return persisted;
1254
1700
  }
1255
- throw new Error("No registry found in nx.json's persisted config. Pass --registry npm or --registry azure-artifacts (with --organization/--project/--artifacts-feed).");
1701
+ throw new Error(
1702
+ "No registry found in nx.json's persisted config. Pass --registry npm or --registry azure-artifacts (with --organization/--project/--artifacts-feed)."
1703
+ );
1256
1704
  }
1257
1705
  function resolveOverlayOptions(options, persisted) {
1258
1706
  const scope = options.scope ?? persisted.scope;
@@ -1262,52 +1710,157 @@ function resolveOverlayOptions(options, persisted) {
1262
1710
  const registry = resolveRegistry2(options, persisted.registry);
1263
1711
  const ci = options.ci ?? persisted.ci;
1264
1712
  if (!ci) {
1265
- throw new Error("No CI provider found in nx.json's persisted config. Pass --ci azure|github|both explicitly.");
1713
+ throw new Error(
1714
+ "No CI provider found in nx.json's persisted config. Pass --ci azure|github|both explicitly."
1715
+ );
1266
1716
  }
1267
1717
  const agent = options.agent ?? persisted.agent;
1268
1718
  if (!agent) {
1269
- throw new Error("No CI build agent found in nx.json's persisted config. Pass --agent explicitly.");
1719
+ throw new Error(
1720
+ "No CI build agent found in nx.json's persisted config. Pass --agent explicitly."
1721
+ );
1270
1722
  }
1271
1723
  const variableGroup = options.variableGroup ?? persisted.variableGroup ?? "Build";
1272
- const linter = options.linter ?? persisted.stack?.linter;
1273
1724
  const testRunner = options.testRunner ?? persisted.stack?.testRunner;
1274
- if (!linter || !testRunner) {
1275
- throw new Error("No stack (linter/test runner) found in nx.json's persisted config. Pass --linter and --test-runner explicitly.");
1725
+ if (!testRunner) {
1726
+ throw new Error(
1727
+ "No testRunner found in nx.json's persisted config. Pass --test-runner explicitly."
1728
+ );
1276
1729
  }
1277
- return { scope, registry, agent, variableGroup, ci, stack: { linter, testRunner } };
1730
+ return { scope, registry, agent, variableGroup, ci, stack: { testRunner } };
1278
1731
  }
1279
1732
  function runUpgrade(workspaceRoot, options) {
1280
1733
  if (!fileExists((0, import_node_path11.join)(workspaceRoot, "nx.json"))) {
1281
- throw new Error(`No nx.json found in ${workspaceRoot} \u2014 this does not look like an Nx workspace. Run 'mnci upgrade' from the workspace root.`);
1734
+ throw new Error(
1735
+ `No nx.json found in ${workspaceRoot} \u2014 this does not look like an Nx workspace. Run 'mnci upgrade' from the workspace root.`
1736
+ );
1282
1737
  }
1283
1738
  const persisted = readMnciConfig(workspaceRoot);
1284
1739
  const resolved = resolveOverlayOptions(options, persisted);
1285
- logger.step("Re-applying the MoNecromanCI overlay (release config, .npmrc, commitlint, pipeline, stack)");
1740
+ logger.step(
1741
+ "Re-applying the MoNecromanCI overlay (release config, .npmrc, commitlint, pipeline, stack)"
1742
+ );
1286
1743
  applyOverlay(workspaceRoot, resolved);
1287
1744
  logger.success("Done. Review the changes with `git diff` before committing.");
1288
1745
  }
1289
1746
 
1290
- // src/cli.ts
1291
- function readVersion() {
1747
+ // src/commands/interactive.ts
1748
+ async function runInteractive() {
1749
+ const inWorkspace = fileExists((0, import_node_path12.join)(process.cwd(), "nx.json"));
1750
+ const newChoice = { name: "Create a new monorepo", value: "new" };
1751
+ const addChoice = { name: "Add a project to this workspace", value: "add" };
1752
+ const upgradeChoice = {
1753
+ name: "Upgrade this workspace (re-apply the latest overlay)",
1754
+ value: "upgrade"
1755
+ };
1756
+ const action = await (0, import_prompts7.select)({
1757
+ message: "What would you like to do?",
1758
+ choices: inWorkspace ? [addChoice, upgradeChoice, newChoice] : [newChoice, addChoice, upgradeChoice]
1759
+ });
1760
+ if (action === "new") {
1761
+ await runNew(void 0, {});
1762
+ } else if (action === "upgrade") {
1763
+ runUpgrade(process.cwd(), {});
1764
+ } else {
1765
+ await runAdd(void 0, void 0, {});
1766
+ }
1767
+ }
1768
+
1769
+ // src/util/versionChecker.ts
1770
+ var import_node_fs5 = require("fs");
1771
+ var import_node_path13 = require("path");
1772
+ var import_cross_spawn2 = __toESM(require("cross-spawn"));
1773
+ var PACKAGE_NAME = "@mnci/cli";
1774
+ var REGISTRY_TIMEOUT_MS = 2e3;
1775
+ function checkForUpdate(currentVersion) {
1776
+ try {
1777
+ if (!process.stdout.isTTY) {
1778
+ return;
1779
+ }
1780
+ setImmediate(() => {
1781
+ try {
1782
+ reportIfOutdated(currentVersion);
1783
+ } catch {
1784
+ }
1785
+ });
1786
+ } catch {
1787
+ }
1788
+ }
1789
+ function reportIfOutdated(currentVersion) {
1790
+ const result = import_cross_spawn2.default.sync("npm", ["view", PACKAGE_NAME, "version"], {
1791
+ encoding: "utf8",
1792
+ timeout: REGISTRY_TIMEOUT_MS,
1793
+ stdio: ["ignore", "pipe", "ignore"]
1794
+ });
1795
+ if (result.error || result.status !== 0) {
1796
+ return;
1797
+ }
1798
+ const latestVersion = (result.stdout ?? "").trim();
1799
+ if (!latestVersion || !isNewerVersion(latestVersion, currentVersion)) {
1800
+ return;
1801
+ }
1802
+ logger.info("");
1803
+ logger.info(`A new version of ${PACKAGE_NAME} is available: ${latestVersion}`);
1804
+ logger.info(` Current version: ${currentVersion}`);
1805
+ logger.info(` Update with: npm install -g ${PACKAGE_NAME}@latest`);
1806
+ logger.info("");
1807
+ }
1808
+ function isNewerVersion(candidate, current) {
1809
+ const candidateParts = releaseTriple(candidate);
1810
+ const currentParts = releaseTriple(current);
1811
+ if (!candidateParts || !currentParts) {
1812
+ return false;
1813
+ }
1814
+ for (const [index, candidatePart] of candidateParts.entries()) {
1815
+ if (candidatePart !== currentParts[index]) {
1816
+ return candidatePart > currentParts[index];
1817
+ }
1818
+ }
1819
+ return false;
1820
+ }
1821
+ function releaseTriple(version) {
1822
+ const match = /^(\d+)\.(\d+)\.(\d+)/.exec(version.trim());
1823
+ return match ? [Number(match[1]), Number(match[2]), Number(match[3])] : void 0;
1824
+ }
1825
+ function readCliVersion(packageDirectory) {
1292
1826
  try {
1293
- const package_ = JSON.parse((0, import_node_fs3.readFileSync)((0, import_node_path12.join)(__dirname, "..", "package.json"), "utf8"));
1294
- return package_.version ?? "0.0.0";
1827
+ const manifestPath = (0, import_node_path13.join)(packageDirectory, "..", "package.json");
1828
+ const manifest = JSON.parse((0, import_node_fs5.readFileSync)(manifestPath, "utf8"));
1829
+ return manifest.version ?? "0.0.0";
1295
1830
  } catch {
1296
1831
  return "0.0.0";
1297
1832
  }
1298
1833
  }
1299
- function buildProgram() {
1834
+
1835
+ // src/cli.ts
1836
+ function buildProgram(cliVersion) {
1300
1837
  const program = new import_commander.Command();
1301
- program.name("mnci").description("MoNecromanCI \u2014 a thin CLI over official Nx plugins").version(readVersion(), "-v, --version", "display the version");
1302
- program.command("new").argument("[name]", "workspace name").description("Create a new monorepo (Nx TS preset + release/pipeline overlay)").option("-y, --yes", "accept defaults for anything not passed as a flag").option("--scope <scope>", "npm scope for publishable packages (e.g. @demo)").option("--registry <kind>", "azure-artifacts | npm").option("--organization <name>", "Azure DevOps organization").option("--project <name>", "Azure DevOps project").option("--artifacts-feed <name>", "Azure Artifacts feed").option("--agent <pool>", "CI build agent: a vmImage (e.g. ubuntu-latest) or self-hosted pool name").option("--variable-group <name>", "Azure DevOps variable group holding the npm PAT").option("--ci <provider>", "CI provider: azure | github | both").option("--linter <linter>", "linter: eslint | oxlint").option("--test-runner <runner>", "unit-test runner: jest | vitest").action(async (name, options) => {
1838
+ program.name("mnci").description("MoNecromanCI \u2014 a thin CLI over official Nx plugins").version(cliVersion, "-v, --version", "display the version");
1839
+ program.command("new").argument("[name]", "workspace name").description("Create a new monorepo (Nx TS preset + release/pipeline overlay)").option("-y, --yes", "accept defaults for anything not passed as a flag").option("--scope <scope>", "npm scope for publishable packages (e.g. @demo)").option("--registry <kind>", "azure-artifacts | npm").option("--organization <name>", "Azure DevOps organization").option("--project <name>", "Azure DevOps project").option("--artifacts-feed <name>", "Azure Artifacts feed").option(
1840
+ "--agent <pool>",
1841
+ "CI build agent: a vmImage (e.g. ubuntu-latest) or self-hosted pool name"
1842
+ ).option("--variable-group <name>", "Azure DevOps variable group holding the npm PAT").option("--ci <provider>", "CI provider: azure | github | both").option("--test-runner <runner>", "unit-test runner: jest | vitest").option("--nx-cloud", "connect the workspace to Nx Cloud (remote caching + CI insights)").action(async (name, options) => {
1303
1843
  await runNew(name, options);
1304
1844
  });
1305
- program.command("upgrade").description("Re-apply the latest MoNecromanCI overlay to this workspace (release config, pipeline, npmrc, commitlint, husky hook, curated scripts)").option("--scope <scope>", "npm scope for publishable packages (overrides the persisted value)").option("--registry <kind>", "azure-artifacts | npm (overrides the persisted value)").option("--organization <name>", "Azure DevOps organization").option("--project <name>", "Azure DevOps project").option("--artifacts-feed <name>", "Azure Artifacts feed").option("--agent <pool>", "CI build agent (overrides the persisted value)").option("--variable-group <name>", "Azure DevOps variable group holding the npm PAT").option("--ci <provider>", "CI provider: azure | github | both (overrides the persisted value)").option("--linter <linter>", "linter: eslint | oxlint (overrides the persisted value)").option("--test-runner <runner>", "unit-test runner: jest | vitest (overrides the persisted value)").action((options) => {
1845
+ program.command("upgrade").description(
1846
+ "Re-apply the latest MoNecromanCI overlay to this workspace (release config, pipeline, npmrc, commitlint, husky hook, curated scripts)"
1847
+ ).option("--scope <scope>", "npm scope for publishable packages (overrides the persisted value)").option("--registry <kind>", "azure-artifacts | npm (overrides the persisted value)").option("--organization <name>", "Azure DevOps organization").option("--project <name>", "Azure DevOps project").option("--artifacts-feed <name>", "Azure Artifacts feed").option("--agent <pool>", "CI build agent (overrides the persisted value)").option("--variable-group <name>", "Azure DevOps variable group holding the npm PAT").option("--ci <provider>", "CI provider: azure | github | both (overrides the persisted value)").option(
1848
+ "--test-runner <runner>",
1849
+ "unit-test runner: jest | vitest (overrides the persisted value)"
1850
+ ).action((options) => {
1306
1851
  runUpgrade(process.cwd(), options);
1307
1852
  });
1308
- 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("--framework <framework>", "node-app only: express | fastify | koa | nest | none (default: none)").action(async (kind, name, options) => {
1309
- await runAdd(kind, name, options);
1310
- });
1853
+ 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(
1854
+ "--framework <framework>",
1855
+ "node-app only: express | fastify | koa | nest | none (default: none)"
1856
+ ).option(
1857
+ "--lib <name>",
1858
+ "python-vendor only: the internal Python library (libs/<name>) to vendor into <name>"
1859
+ ).action(
1860
+ async (kind, name, options) => {
1861
+ await runAdd(kind, name, options);
1862
+ }
1863
+ );
1311
1864
  program.action(async () => {
1312
1865
  await runInteractive();
1313
1866
  });
@@ -1315,7 +1868,15 @@ function buildProgram() {
1315
1868
  }
1316
1869
  async function main() {
1317
1870
  try {
1318
- await buildProgram().parseAsync(process.argv);
1871
+ const cliVersion = readCliVersion(__dirname);
1872
+ const program = buildProgram(cliVersion);
1873
+ checkForUpdate(cliVersion);
1874
+ const arguments_ = new Set(process.argv.slice(2));
1875
+ const requestedVersionFlag = arguments_.has("-v") || arguments_.has("--version");
1876
+ if (process.stdout.isTTY && !requestedVersionFlag) {
1877
+ logger.info(`mnci v${cliVersion}`);
1878
+ }
1879
+ await program.parseAsync(process.argv);
1319
1880
  } catch (error) {
1320
1881
  logger.error(error instanceof Error ? error.message : String(error));
1321
1882
  process.exitCode = 1;