nx 23.0.0-beta.21 → 23.0.0-beta.23

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.
Files changed (82) hide show
  1. package/dist/plugins/package-json.js +1 -1
  2. package/dist/src/adapter/angular-json.d.ts +2 -2
  3. package/dist/src/adapter/angular-json.js +0 -1
  4. package/dist/src/adapter/compat.d.ts +1 -1
  5. package/dist/src/adapter/compat.js +1 -0
  6. package/dist/src/ai/set-up-ai-agents/set-up-ai-agents.js +2 -1
  7. package/dist/src/command-line/configure-ai-agents/configure-ai-agents.js +1 -1
  8. package/dist/src/command-line/examples.js +4 -4
  9. package/dist/src/command-line/graph/graph.js +1 -1
  10. package/dist/src/command-line/init/init-v2.js +1 -1
  11. package/dist/src/command-line/migrate/agentic/prompts/generic-validation.d.ts +5 -0
  12. package/dist/src/command-line/migrate/agentic/prompts/generic-validation.js +1 -0
  13. package/dist/src/command-line/migrate/agentic/prompts/hybrid-prompt-migration.d.ts +5 -0
  14. package/dist/src/command-line/migrate/agentic/prompts/hybrid-prompt-migration.js +1 -0
  15. package/dist/src/command-line/migrate/agentic/prompts/prompt-migration.d.ts +5 -0
  16. package/dist/src/command-line/migrate/agentic/prompts/prompt-migration.js +1 -0
  17. package/dist/src/command-line/migrate/agentic/prompts/shared-rendering.d.ts +1 -0
  18. package/dist/src/command-line/migrate/agentic/prompts/shared-rendering.js +15 -0
  19. package/dist/src/command-line/migrate/agentic/run-step.d.ts +7 -0
  20. package/dist/src/command-line/migrate/agentic/run-step.js +3 -1
  21. package/dist/src/command-line/migrate/agentic/select.js +120 -32
  22. package/dist/src/command-line/migrate/command-object.d.ts +42 -0
  23. package/dist/src/command-line/migrate/command-object.js +38 -8
  24. package/dist/src/command-line/migrate/migrate-config.d.ts +27 -0
  25. package/dist/src/command-line/migrate/migrate-config.js +103 -0
  26. package/dist/src/command-line/migrate/migrate.d.ts +39 -2
  27. package/dist/src/command-line/migrate/migrate.js +203 -41
  28. package/dist/src/command-line/migrate/multi-major.js +5 -2
  29. package/dist/src/command-line/release/changelog/version-plan-filtering.d.ts +3 -1
  30. package/dist/src/command-line/release/changelog/version-plan-filtering.js +7 -3
  31. package/dist/src/command-line/release/changelog.d.ts +7 -0
  32. package/dist/src/command-line/release/changelog.js +22 -9
  33. package/dist/src/command-line/release/release.js +65 -55
  34. package/dist/src/command-line/release/utils/git.d.ts +6 -0
  35. package/dist/src/command-line/release/utils/git.js +33 -0
  36. package/dist/src/command-line/release/version/derive-specifier-from-conventional-commits.js +3 -2
  37. package/dist/src/command-line/release/version.d.ts +3 -0
  38. package/dist/src/command-line/release/version.js +13 -3
  39. package/dist/src/config/misc-interfaces.d.ts +8 -0
  40. package/dist/src/config/nx-json.d.ts +49 -0
  41. package/dist/src/core/graph/main.js +1 -1
  42. package/dist/src/daemon/server/latest-nx.js +3 -1
  43. package/dist/src/devkit-exports.d.ts +11 -11
  44. package/dist/src/devkit-exports.js +7 -4
  45. package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
  46. package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
  47. package/dist/src/plugins/js/index.d.ts +2 -2
  48. package/dist/src/plugins/js/index.js +4 -4
  49. package/dist/src/plugins/js/lock-file/lock-file.d.ts +7 -2
  50. package/dist/src/plugins/js/lock-file/lock-file.js +35 -25
  51. package/dist/src/plugins/js/project-graph/affected/lock-file-changes.d.ts +2 -4
  52. package/dist/src/plugins/js/project-graph/affected/lock-file-changes.js +121 -43
  53. package/dist/src/plugins/js/utils/register.d.ts +52 -0
  54. package/dist/src/plugins/js/utils/register.js +195 -0
  55. package/dist/src/plugins/package-json/create-nodes.d.ts +2 -2
  56. package/dist/src/plugins/package-json/create-nodes.js +2 -2
  57. package/dist/src/plugins/project-json/build-nodes/project-json.d.ts +2 -2
  58. package/dist/src/plugins/project-json/build-nodes/project-json.js +1 -1
  59. package/dist/src/project-graph/error-types.d.ts +6 -6
  60. package/dist/src/project-graph/error-types.js +1 -1
  61. package/dist/src/project-graph/file-utils.d.ts +7 -0
  62. package/dist/src/project-graph/file-utils.js +78 -10
  63. package/dist/src/project-graph/plugins/isolation/isolated-plugin.d.ts +2 -2
  64. package/dist/src/project-graph/plugins/isolation/messaging.d.ts +2 -2
  65. package/dist/src/project-graph/plugins/loaded-nx-plugin.d.ts +3 -3
  66. package/dist/src/project-graph/plugins/loaded-nx-plugin.js +6 -4
  67. package/dist/src/project-graph/plugins/public-api.d.ts +31 -12
  68. package/dist/src/project-graph/plugins/transpiler.js +11 -0
  69. package/dist/src/project-graph/plugins/utils.d.ts +3 -3
  70. package/dist/src/project-graph/utils/project-configuration-utils.d.ts +1 -1
  71. package/dist/src/project-graph/utils/project-configuration-utils.js +6 -17
  72. package/dist/src/tasks-runner/init-tasks-runner.d.ts +2 -2
  73. package/dist/src/tasks-runner/init-tasks-runner.js +6 -6
  74. package/dist/src/tasks-runner/task-orchestrator.d.ts +2 -2
  75. package/dist/src/tasks-runner/task-orchestrator.js +6 -6
  76. package/dist/src/utils/package-json.d.ts +3 -3
  77. package/dist/src/utils/package-json.js +9 -11
  78. package/dist/src/utils/package-manager.d.ts +13 -0
  79. package/dist/src/utils/package-manager.js +27 -0
  80. package/migrations.json +18 -9
  81. package/package.json +11 -11
  82. package/schemas/nx-schema.json +41 -0
@@ -22,7 +22,7 @@ function writeCache() {
22
22
  }
23
23
  const plugin = {
24
24
  name: 'nx-all-package-jsons-plugin',
25
- createNodesV2: [
25
+ createNodes: [
26
26
  '*/**/package.json',
27
27
  (configFiles, options, context) => {
28
28
  const cache = readPackageJsonConfigurationCache();
@@ -1,7 +1,7 @@
1
1
  import { ProjectsConfigurations } from '../config/workspace-json-project-json';
2
- import { NxPluginV2 } from '../project-graph/plugins';
2
+ import { NxPlugin } from '../project-graph/plugins';
3
3
  export declare const NX_ANGULAR_JSON_PLUGIN_NAME = "nx-angular-json-plugin";
4
- export declare const NxAngularJsonPlugin: NxPluginV2;
4
+ export declare const NxAngularJsonPlugin: NxPlugin;
5
5
  export default NxAngularJsonPlugin;
6
6
  export declare function shouldMergeAngularProjects(root: string, includeProjectsFromAngularJson: boolean): boolean;
7
7
  export declare function isAngularPluginInstalled(): boolean;
@@ -25,7 +25,6 @@ const createNodes = [
25
25
  exports.NxAngularJsonPlugin = {
26
26
  name: exports.NX_ANGULAR_JSON_PLUGIN_NAME,
27
27
  createNodes,
28
- createNodesV2: createNodes,
29
28
  };
30
29
  exports.default = exports.NxAngularJsonPlugin;
31
30
  function shouldMergeAngularProjects(root, includeProjectsFromAngularJson) {
@@ -1,2 +1,2 @@
1
1
  export declare const allowedProjectExtensions: readonly ["tags", "implicitDependencies", "configFilePath", "$schema", "generators", "namedInputs", "name", "files", "root", "sourceRoot", "projectType", "release", "includedScripts", "metadata", "owners", "nxCloudImplicitDependencies"];
2
- export declare const allowedWorkspaceExtensions: readonly ["$schema", "implicitDependencies", "affected", "defaultBase", "tasksRunnerOptions", "workspaceLayout", "plugins", "targetDefaults", "files", "generators", "namedInputs", "extends", "cli", "pluginsConfig", "defaultProject", "installation", "release", "nxCloudAccessToken", "nxCloudId", "nxCloudUrl", "nxCloudEncryptionKey", "parallel", "cacheDirectory", "useDaemonProcess", "useInferencePlugins", "neverConnectToCloud", "analytics", "sync", "useLegacyCache", "maxCacheSize", "tui", "owners"];
2
+ export declare const allowedWorkspaceExtensions: readonly ["$schema", "implicitDependencies", "affected", "defaultBase", "tasksRunnerOptions", "workspaceLayout", "plugins", "targetDefaults", "files", "generators", "namedInputs", "extends", "cli", "pluginsConfig", "defaultProject", "installation", "release", "nxCloudAccessToken", "nxCloudId", "nxCloudUrl", "nxCloudEncryptionKey", "parallel", "cacheDirectory", "useDaemonProcess", "useInferencePlugins", "neverConnectToCloud", "analytics", "sync", "migrate", "useLegacyCache", "maxCacheSize", "tui", "owners"];
@@ -66,6 +66,7 @@ exports.allowedWorkspaceExtensions = [
66
66
  'neverConnectToCloud',
67
67
  'analytics',
68
68
  'sync',
69
+ 'migrate',
69
70
  'useLegacyCache',
70
71
  'maxCacheSize',
71
72
  'tui',
@@ -12,6 +12,7 @@ const generate_files_1 = require("../../generators/utils/generate-files");
12
12
  const json_1 = require("../../generators/utils/json");
13
13
  const native_1 = require("../../native");
14
14
  const package_json_1 = require("../../utils/package-json");
15
+ const package_manager_1 = require("../../utils/package-manager");
15
16
  const ignore_1 = require("../../utils/ignore");
16
17
  const provenance_1 = require("../../utils/provenance");
17
18
  const workspace_root_1 = require("../../utils/workspace-root");
@@ -48,7 +49,7 @@ async function setupAiAgentsGenerator(tree, options, inner = false) {
48
49
  }
49
50
  try {
50
51
  await (0, provenance_1.ensurePackageHasProvenance)('nx', normalizedOptions.packageVersion);
51
- const { tempDir, cleanup } = (0, package_json_1.installPackageToTmp)('nx', normalizedOptions.packageVersion);
52
+ const { tempDir, cleanup } = (0, package_json_1.installPackageToTmp)('nx', normalizedOptions.packageVersion, (0, package_manager_1.detectPackageManager)(tree.root));
52
53
  let modulePath = (0, path_1.join)(tempDir, 'node_modules', 'nx', 'src/ai/set-up-ai-agents/set-up-ai-agents.js');
53
54
  const module = await (0, handle_import_1.handleImport)(modulePath);
54
55
  const setupAiAgentsGeneratorResult = await module.setupAiAgentsGenerator(tree, normalizedOptions, true);
@@ -44,7 +44,7 @@ async function configureAiAgentsHandler(args, inner = false) {
44
44
  let cleanup;
45
45
  try {
46
46
  await (0, provenance_1.ensurePackageHasProvenance)('nx', 'latest');
47
- const packageInstallResults = (0, devkit_internals_1.installPackageToTmp)('nx', 'latest');
47
+ const packageInstallResults = (0, devkit_internals_1.installPackageToTmp)('nx', 'latest', (0, package_manager_1.detectPackageManager)(workspace_root_1.workspaceRoot));
48
48
  cleanup = packageInstallResults.cleanup;
49
49
  let modulePath = require.resolve('nx/src/command-line/configure-ai-agents/configure-ai-agents.js', { paths: [packageInstallResults.tempDir] });
50
50
  const module = await (0, handle_import_1.handleImport)(modulePath);
@@ -356,19 +356,19 @@ exports.examples = {
356
356
  description: 'Prints the specified + inferred configuration for `my-app:build`',
357
357
  },
358
358
  {
359
- command: 'show target my-app:build inputs',
359
+ command: 'show target inputs my-app:build',
360
360
  description: 'Prints the resolved inputs for `my-app:build`',
361
361
  },
362
362
  {
363
- command: 'show target my-app:build inputs --check packages/my-app/index.html',
363
+ command: 'show target inputs my-app:build --check packages/my-app/index.html',
364
364
  description: 'Checks if `packages/my-app/index.html` is an input for `my-app:build`',
365
365
  },
366
366
  {
367
- command: 'show target my-app:build outputs',
367
+ command: 'show target outputs my-app:build',
368
368
  description: 'Prints the outputs detected on disk for `my-app:build`',
369
369
  },
370
370
  {
371
- command: 'show target my-app:build outputs --check packages/my-app/dist/index.html',
371
+ command: 'show target outputs my-app:build --check packages/my-app/dist/index.html',
372
372
  description: 'Checks if `packages/my-app/dist/index.html` is an output for `my-app:build`',
373
373
  },
374
374
  ],
@@ -172,7 +172,7 @@ async function generateGraph(args, affectedProjects) {
172
172
  });
173
173
  }
174
174
  output_1.output.warn({
175
- title: `${errors?.length > 1 ? `${errors.length} errors` : `An error`} occured while processing the project graph. Showing partial graph.`,
175
+ title: `${errors?.length > 1 ? `${errors.length} errors` : `An error`} occurred while processing the project graph. Showing partial graph.`,
176
176
  });
177
177
  }
178
178
  }
@@ -42,7 +42,7 @@ async function initHandler(options, inner = false) {
42
42
  let cleanup;
43
43
  try {
44
44
  await (0, provenance_1.ensurePackageHasProvenance)('nx', 'latest');
45
- const packageInstallResults = (0, devkit_internals_1.installPackageToTmp)('nx', 'latest');
45
+ const packageInstallResults = (0, devkit_internals_1.installPackageToTmp)('nx', 'latest', (0, package_manager_2.detectPackageManager)(process.cwd()));
46
46
  cleanup = packageInstallResults.cleanup;
47
47
  let modulePath = require.resolve('nx/src/command-line/init/init-v2.js', {
48
48
  paths: [packageInstallResults.tempDir],
@@ -6,6 +6,11 @@ export interface GenericValidationPromptContext {
6
6
  description?: string;
7
7
  /** Absolute path the agent must write its handoff file to. */
8
8
  handoffFileAbsolutePath: string;
9
+ /**
10
+ * Path to the migration's documentation file, if any - workspace-relative,
11
+ * or absolute when it resolves outside the workspace.
12
+ */
13
+ documentationPath?: string;
9
14
  /** Context captured from the deterministic generator phase. */
10
15
  impl: {
11
16
  /** Raw output from the generator (devkit logger + console). */
@@ -29,6 +29,7 @@ function buildGenericValidationUserPrompt(ctx) {
29
29
  `You are validating the output of an Nx migration's deterministic generator phase. The generator has already run; inspect what it produced, verify the workspace is in a consistent state for what this migration intended to accomplish, apply any minor in-scope fixes the generator should have produced cleanly, and report findings.`,
30
30
  ...(0, shared_rendering_1.renderMigrationBlock)(ctx),
31
31
  ];
32
+ lines.push(...(0, shared_rendering_1.renderMigrationDocumentationBlock)(ctx.documentationPath));
32
33
  const logs = (0, shared_rendering_1.escapeXmlBody)((0, shared_rendering_1.stripAnsi)(ctx.impl.logs ?? '').trim());
33
34
  lines.push(...(0, shared_rendering_1.renderGeneratorOutputBlock)(logs));
34
35
  if (!ctx.impl.hasDiffContext && ctx.impl.changes.length > 0) {
@@ -8,6 +8,11 @@ export interface HybridPromptMigrationContext {
8
8
  promptPath: string;
9
9
  /** Absolute path the agent must write its handoff file to. */
10
10
  handoffFileAbsolutePath: string;
11
+ /**
12
+ * Path to the migration's documentation file, if any - workspace-relative,
13
+ * or absolute when it resolves outside the workspace.
14
+ */
15
+ documentationPath?: string;
11
16
  /** Context captured from the deterministic generator phase. */
12
17
  impl?: {
13
18
  /** Raw output from the generator (devkit logger + console). */
@@ -20,6 +20,7 @@ function buildHybridPromptUserPrompt(ctx) {
20
20
  `Complete the AI-driven step that follows the generator phase of a two-phase Nx migration. The deterministic generator phase has already run; the sections below summarize what it did. The step may apply additional changes, verify the generator's output, or both — follow the instructions file.`,
21
21
  ...(0, shared_rendering_1.renderMigrationBlock)(ctx),
22
22
  ];
23
+ lines.push(...(0, shared_rendering_1.renderMigrationDocumentationBlock)(ctx.documentationPath));
23
24
  const logs = (0, shared_rendering_1.escapeXmlBody)((0, shared_rendering_1.stripAnsi)(ctx.impl?.logs ?? '').trim());
24
25
  const agentContext = (0, shared_rendering_1.filterNonEmptyStrings)(ctx.impl?.agentContext ?? []);
25
26
  const hasDiffContext = !!ctx.impl?.hasDiffContext;
@@ -7,6 +7,11 @@ export interface PromptMigrationContext {
7
7
  promptPath: string;
8
8
  /** Absolute path the agent must write its handoff file to. */
9
9
  handoffFileAbsolutePath: string;
10
+ /**
11
+ * Path to the migration's documentation file, if any - workspace-relative,
12
+ * or absolute when it resolves outside the workspace.
13
+ */
14
+ documentationPath?: string;
10
15
  }
11
16
  /**
12
17
  * Builds the first user-facing message for a prompt-migration step.
@@ -16,6 +16,7 @@ function buildPromptMigrationUserPrompt(ctx) {
16
16
  const lines = [
17
17
  `Apply one prompt-based migration to this Nx workspace.`,
18
18
  ...(0, shared_rendering_1.renderMigrationBlock)(ctx),
19
+ ...(0, shared_rendering_1.renderMigrationDocumentationBlock)(ctx.documentationPath),
19
20
  ``,
20
21
  `<instructions_file>${(0, shared_rendering_1.escapeXmlBody)(ctx.promptPath)}</instructions_file>`,
21
22
  ``,
@@ -16,3 +16,4 @@ export interface MigrationBlockContext {
16
16
  export declare function renderMigrationBlock(ctx: MigrationBlockContext): string[];
17
17
  export declare function renderHandoffPathFooter(handoffFileAbsolutePath: string): string[];
18
18
  export declare function renderAdvisoryContext(note: string, entries: string[]): string[];
19
+ export declare function renderMigrationDocumentationBlock(documentationPath: string | undefined): string[];
@@ -11,6 +11,7 @@ exports.renderGeneratorOutputBlock = renderGeneratorOutputBlock;
11
11
  exports.renderMigrationBlock = renderMigrationBlock;
12
12
  exports.renderHandoffPathFooter = renderHandoffPathFooter;
13
13
  exports.renderAdvisoryContext = renderAdvisoryContext;
14
+ exports.renderMigrationDocumentationBlock = renderMigrationDocumentationBlock;
14
15
  function renderFileEntry(change) {
15
16
  return `[${change.type}] ${change.path}`;
16
17
  }
@@ -128,3 +129,17 @@ function renderAdvisoryContext(note, entries) {
128
129
  `</advisory_context>`,
129
130
  ];
130
131
  }
132
+ // Points the agent at the migration's documentation file (reference, not
133
+ // instructions). Returns `[]` when there's no doc so callers can spread without
134
+ // guarding; the path is escaped like other user-authored values interpolated
135
+ // into the prompt.
136
+ function renderMigrationDocumentationBlock(documentationPath) {
137
+ if (!documentationPath)
138
+ return [];
139
+ return [
140
+ ``,
141
+ `<migration_documentation note="reference: documents what this migration does; read this file if you need more context on its intent, not as instructions">`,
142
+ escapeXmlBody(documentationPath),
143
+ `</migration_documentation>`,
144
+ ];
145
+ }
@@ -31,6 +31,13 @@ export interface RunAgenticPromptStepInput {
31
31
  description?: string;
32
32
  prompt?: string;
33
33
  };
34
+ /**
35
+ * Path to the migration's documentation file, resolved by the orchestrator -
36
+ * workspace-relative, or absolute when the file resolves outside the
37
+ * workspace. Omitted when the migration declares no `documentation` or it
38
+ * can't be resolved.
39
+ */
40
+ documentationPath?: string;
34
41
  agentic: EnabledResolvedAgentic;
35
42
  runDir: string;
36
43
  installDepsIfChanged: () => Promise<void>;
@@ -27,7 +27,7 @@ const runner_1 = require("./runner");
27
27
  * the file free of cross-imports with the orchestrator.
28
28
  */
29
29
  async function runAgenticPromptStep(input) {
30
- const { root, migration, agentic, runDir, installDepsIfChanged, implContext, mode = 'author', } = input;
30
+ const { root, migration, agentic, runDir, installDepsIfChanged, implContext, documentationPath, mode = 'author', } = input;
31
31
  const handoffFilePath = (0, handoff_1.stepHandoffPath)(runDir, migration);
32
32
  // The system prompt tells the agent the parent dir exists, so the agent
33
33
  // doesn't defensively `mkdir -p` (which triggers a workspace-permission
@@ -52,6 +52,7 @@ async function runAgenticPromptStep(input) {
52
52
  version: migration.version,
53
53
  description: migration.description,
54
54
  handoffFileAbsolutePath: handoffFilePath,
55
+ documentationPath,
55
56
  impl: implContext,
56
57
  });
57
58
  }
@@ -63,6 +64,7 @@ async function runAgenticPromptStep(input) {
63
64
  description: migration.description,
64
65
  promptPath: migration.prompt,
65
66
  handoffFileAbsolutePath: handoffFilePath,
67
+ documentationPath,
66
68
  };
67
69
  userPrompt = implContext
68
70
  ? (0, hybrid_prompt_migration_1.buildHybridPromptUserPrompt)({ ...promptCtx, impl: implContext })
@@ -1,7 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.resolveAgentic = resolveAgentic;
4
+ const fs_1 = require("fs");
5
+ const jsonc_parser_1 = require("jsonc-parser");
6
+ const path_1 = require("path");
4
7
  const output_1 = require("../../../utils/output");
8
+ const workspace_root_1 = require("../../../utils/workspace-root");
5
9
  const safe_prompt_1 = require("../safe-prompt");
6
10
  const detect_installed_1 = require("./detect-installed");
7
11
  const inception_1 = require("./inception");
@@ -32,20 +36,35 @@ async function resolveAgentic(input) {
32
36
  return { kind: 'disabled' };
33
37
  }
34
38
  const detected = await (0, detect_installed_1.detectInstalledAgents)(definitions_1.AGENT_DEFINITIONS);
35
- const { enabled, explicitId } = await resolveFlag(input, isInteractive, detected);
39
+ const { enabled, explicitId, persist } = await resolveFlag(input, isInteractive, detected);
36
40
  if (!enabled) {
41
+ if (persist === false) {
42
+ persistAgenticChoice(false);
43
+ }
37
44
  return { kind: 'disabled' };
38
45
  }
39
46
  const selected = await selectAgent(detected, explicitId, isInteractive);
47
+ if (persist === true) {
48
+ persistAgenticChoice(true);
49
+ }
50
+ else if (persist === 'pin') {
51
+ persistAgenticChoice(selected.id);
52
+ }
40
53
  return { kind: 'enabled', selectedAgent: selected };
41
54
  }
42
55
  async function resolveFlag(input, isInteractive, detected) {
43
56
  if (input.agentic === true) {
44
- requireInteractiveOrAbort(isInteractive);
57
+ if (!isInteractive) {
58
+ warnAgenticInteractiveOnly();
59
+ return { enabled: false };
60
+ }
45
61
  return { enabled: true };
46
62
  }
47
63
  if (typeof input.agentic === 'string') {
48
- requireInteractiveOrAbort(isInteractive);
64
+ if (!isInteractive) {
65
+ warnAgenticInteractiveOnly();
66
+ return { enabled: false };
67
+ }
49
68
  return { enabled: true, explicitId: input.agentic };
50
69
  }
51
70
  // undefined — fire the up-front prompt only when we have a TTY, something
@@ -61,7 +80,7 @@ async function resolveFlag(input, isInteractive, detected) {
61
80
  if (detected.length === 0) {
62
81
  return { enabled: false };
63
82
  }
64
- return { enabled: await firePromptForAgentic(input.migrations) };
83
+ return firePromptForAgentic(input.migrations, detected);
65
84
  }
66
85
  function requireInteractiveOrAbort(isInteractive) {
67
86
  if (isInteractive)
@@ -74,29 +93,102 @@ function requireInteractiveOrAbort(isInteractive) {
74
93
  });
75
94
  throw new Error('Agentic flow requires an interactive terminal.');
76
95
  }
77
- async function firePromptForAgentic(migrations) {
78
- // The "Yes"/"No" hints below assume at least one prompt-bearing migration is
79
- // queued. If we later extend the prompt to fire for generator-only runs
80
- // (validation-only), the hints need to branch.
96
+ function warnAgenticInteractiveOnly() {
97
+ output_1.output.warn({
98
+ title: 'Skipping the agentic flow: it is interactive-only in this release and this is a non-interactive terminal.',
99
+ bodyLines: [
100
+ 'Continuing the migration without the agentic flow. Re-run in an interactive terminal to use it.',
101
+ ],
102
+ });
103
+ }
104
+ async function firePromptForAgentic(migrations, detected) {
105
+ // The apply hint assumes at least one prompt-bearing migration is queued. If
106
+ // we later extend the prompt to fire for generator-only runs (validation-
107
+ // only), the hint needs to branch.
81
108
  const promptCount = migrations.filter((m) => !!m.prompt).length;
82
- const yesHint = `Apply ${promptCount} prompt migration${promptCount === 1 ? '' : 's'} and validate generator output with an AI agent`;
83
- const noHint = `Skip prompts and run generators without AI validation`;
109
+ const applyHint = `Apply ${promptCount} prompt migration${promptCount === 1 ? '' : 's'} and validate generator output with an AI agent`;
110
+ const skipHint = `Skip prompts and run generators without AI validation`;
111
+ const rememberHint = `Saved to nx.json so Nx won't ask again`;
112
+ // The pin-vs-flexible distinction only matters when more than one agent is
113
+ // installed. With a single agent, "always" simply persists `true` and the
114
+ // pin option is dropped.
115
+ const multipleAgents = detected.length > 1;
116
+ const choices = [
117
+ { name: 'yes-once', message: 'Yes, just this time', hint: applyHint },
118
+ {
119
+ name: 'yes-flex',
120
+ message: multipleAgents
121
+ ? "Yes, always (I'll pick the agent each run)"
122
+ : 'Yes, always',
123
+ hint: rememberHint,
124
+ },
125
+ ...(multipleAgents
126
+ ? [
127
+ {
128
+ name: 'yes-pin',
129
+ message: 'Yes, always with the same agent',
130
+ hint: rememberHint,
131
+ },
132
+ ]
133
+ : []),
134
+ { name: 'no-once', message: 'No, just this time', hint: skipHint },
135
+ { name: 'no-never', message: 'No, never', hint: rememberHint },
136
+ ];
84
137
  // Blank line keeps the prompt from gluing to the previous `npm install`
85
138
  // output or any earlier orchestrator line.
86
139
  console.log();
87
140
  // `as any` because enquirer's TS types lag the runtime (per-choice `hint`
88
- // and `value` are supported but not in the .d.ts).
141
+ // is supported but not in the .d.ts).
89
142
  const response = await (0, safe_prompt_1.migratePrompt)({
90
- name: 'enable',
91
- type: 'autocomplete',
143
+ name: 'choice',
144
+ type: 'select',
92
145
  message: 'Enable the agentic flow?',
93
- choices: [
94
- { name: 'yes', message: 'Yes', hint: yesHint },
95
- { name: 'no', message: 'No', hint: noHint },
96
- ],
146
+ choices,
97
147
  initial: 0,
98
148
  });
99
- return response.enable === 'yes';
149
+ switch (response.choice) {
150
+ case 'yes-once':
151
+ return { enabled: true };
152
+ case 'yes-flex':
153
+ return { enabled: true, persist: true };
154
+ case 'yes-pin':
155
+ return { enabled: true, persist: 'pin' };
156
+ case 'no-never':
157
+ return { enabled: false, persist: false };
158
+ case 'no-once':
159
+ default:
160
+ return { enabled: false };
161
+ }
162
+ }
163
+ // Persists the user's agentic choice to `nx.json` so the up-front prompt is
164
+ // skipped on future runs. Edits the raw file in place via jsonc-parser, so it
165
+ // touches only the `migrate.agentic` key and preserves comments, formatting,
166
+ // and any `extends` preset (the prompt fires mid-migration, so a silent
167
+ // reformat would be surprising). Never throws - a failed write only costs the
168
+ // user the prompt again next time.
169
+ function persistAgenticChoice(value) {
170
+ const nxJsonPath = (0, path_1.join)(workspace_root_1.workspaceRoot, 'nx.json');
171
+ if (!(0, fs_1.existsSync)(nxJsonPath)) {
172
+ output_1.output.warn({
173
+ title: `Could not save your agentic choice: no nx.json found at the workspace root.`,
174
+ });
175
+ return;
176
+ }
177
+ try {
178
+ const content = (0, fs_1.readFileSync)(nxJsonPath, 'utf-8');
179
+ const edits = (0, jsonc_parser_1.modify)(content, ['migrate', 'agentic'], value, {
180
+ formattingOptions: { insertSpaces: true, tabSize: 2 },
181
+ });
182
+ (0, fs_1.writeFileSync)(nxJsonPath, (0, jsonc_parser_1.applyEdits)(content, edits));
183
+ output_1.output.log({
184
+ title: `Saved your choice to nx.json (migrate.agentic = ${JSON.stringify(value)}). Nx won't ask again.`,
185
+ });
186
+ }
187
+ catch (e) {
188
+ output_1.output.warn({
189
+ title: `Could not save your agentic choice to nx.json: ${e instanceof Error ? e.message : String(e)}`,
190
+ });
191
+ }
100
192
  }
101
193
  async function selectAgent(detected, explicitId, isInteractive) {
102
194
  if (explicitId) {
@@ -104,22 +196,18 @@ async function selectAgent(detected, explicitId, isInteractive) {
104
196
  if (match) {
105
197
  return match;
106
198
  }
107
- if (detected.length === 0) {
108
- output_1.output.error({
109
- title: `The agent "${explicitId}" was requested via --agentic but no supported AI agent is installed on this machine.`,
110
- bodyLines: INSTALL_SUPPORTED_AGENTS_HINT,
199
+ // The requested agent isn't installed. Rather than aborting the migration,
200
+ // warn and fall through to resolve from the agents that ARE installed
201
+ // (pick when 2+, auto-select the only one, error only when none exist).
202
+ if (detected.length > 0) {
203
+ output_1.output.warn({
204
+ title: `The requested agent "${explicitId}" is not installed; using the installed agent(s) instead.`,
205
+ bodyLines: [
206
+ 'Currently installed agents:',
207
+ ...detected.map((d) => ` - ${d.displayName} (${d.id})`),
208
+ ],
111
209
  });
112
- throw new Error(`The requested agent "${explicitId}" is not installed.`);
113
210
  }
114
- output_1.output.error({
115
- title: `The agent "${explicitId}" was requested via --agentic but is not installed.`,
116
- bodyLines: [
117
- 'Install the requested agent and re-run, or pass --agentic without an explicit agent to choose from installed ones.',
118
- 'Currently installed agents:',
119
- ...detected.map((d) => ` - ${d.displayName} (${d.id})`),
120
- ],
121
- });
122
- throw new Error(`The requested agent "${explicitId}" is not installed.`);
123
211
  }
124
212
  if (detected.length === 0) {
125
213
  output_1.output.error({
@@ -1,4 +1,46 @@
1
1
  import { CommandModule } from 'yargs';
2
+ import type { AgenticArg } from './agentic/select';
2
3
  export declare const yargsMigrateCommand: CommandModule;
3
4
  export declare const yargsInternalMigrateCommand: CommandModule;
4
5
  export declare const DEFAULT_MIGRATION_COMMIT_PREFIX = "chore: [nx migration] ";
6
+ /** Allowed values for `--mode` / `migrate.mode`. */
7
+ export declare const MIGRATE_MODES: readonly ["first-party", "third-party", "all"];
8
+ export type MigrateMode = (typeof MIGRATE_MODES)[number];
9
+ /** Allowed values for `--multi-major-mode` / `migrate.multiMajorMode`. */
10
+ export declare const MULTI_MAJOR_MODES: readonly ["direct", "gradual"];
11
+ export type MultiMajorMode = (typeof MULTI_MAJOR_MODES)[number];
12
+ /**
13
+ * The `nx migrate` args bag. Types the keys the nx.json overlay and the
14
+ * commit-prefix invariant read/write; the index signature keeps the rest of
15
+ * the yargs args flowing through untouched.
16
+ */
17
+ export interface MigrateArgs {
18
+ packageAndVersion?: string;
19
+ runMigrations?: string;
20
+ mode?: MigrateMode;
21
+ /**
22
+ * nx.json `migrate.mode` default. Consumed by `resolveMode` only when the
23
+ * target is Nx itself; kept separate from `mode` so it is never mistaken for
24
+ * an explicit `--mode` (which would hard-fail for non-Nx targets).
25
+ */
26
+ modeFromConfig?: MigrateMode;
27
+ multiMajorMode?: MultiMajorMode;
28
+ createCommits?: boolean;
29
+ commitPrefix?: string;
30
+ agentic?: AgenticArg;
31
+ validate?: boolean;
32
+ [key: string]: any;
33
+ }
34
+ /**
35
+ * Whether a custom commit prefix would be silently ignored: commits aren't
36
+ * enabled and the agentic flow can't enable them either. Shared by the yargs
37
+ * `.check()` (CLI args) and the nx.json overlay (merged args) so the rule lives
38
+ * in one place. `agentic` may flip commits on by default, so a configured
39
+ * agentic value (other than `false`, and not paired with `--no-create-commits`)
40
+ * keeps the prefix in play.
41
+ */
42
+ export declare function customCommitPrefixHasNoEffect(args: {
43
+ createCommits: boolean | undefined;
44
+ commitPrefix: string | undefined;
45
+ agentic: unknown;
46
+ }): boolean;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DEFAULT_MIGRATION_COMMIT_PREFIX = exports.yargsInternalMigrateCommand = exports.yargsMigrateCommand = void 0;
3
+ exports.MULTI_MAJOR_MODES = exports.MIGRATE_MODES = exports.DEFAULT_MIGRATION_COMMIT_PREFIX = exports.yargsInternalMigrateCommand = exports.yargsMigrateCommand = void 0;
4
+ exports.customCommitPrefixHasNoEffect = customCommitPrefixHasNoEffect;
4
5
  const handle_import_1 = require("../../utils/handle-import");
5
6
  const documentation_1 = require("../yargs-utils/documentation");
6
7
  const shared_options_1 = require("../yargs-utils/shared-options");
@@ -20,6 +21,27 @@ exports.yargsInternalMigrateCommand = {
20
21
  handler: async (args) => process.exit(await (await (0, handle_import_1.handleImport)('./migrate.js', __dirname)).migrate(process.cwd(), args, process.argv.slice(3))),
21
22
  };
22
23
  exports.DEFAULT_MIGRATION_COMMIT_PREFIX = 'chore: [nx migration] ';
24
+ /** Allowed values for `--mode` / `migrate.mode`. */
25
+ exports.MIGRATE_MODES = ['first-party', 'third-party', 'all'];
26
+ /** Allowed values for `--multi-major-mode` / `migrate.multiMajorMode`. */
27
+ exports.MULTI_MAJOR_MODES = ['direct', 'gradual'];
28
+ /**
29
+ * Whether a custom commit prefix would be silently ignored: commits aren't
30
+ * enabled and the agentic flow can't enable them either. Shared by the yargs
31
+ * `.check()` (CLI args) and the nx.json overlay (merged args) so the rule lives
32
+ * in one place. `agentic` may flip commits on by default, so a configured
33
+ * agentic value (other than `false`, and not paired with `--no-create-commits`)
34
+ * keeps the prefix in play.
35
+ */
36
+ function customCommitPrefixHasNoEffect(args) {
37
+ const agenticMayEnableCommits = args.agentic !== undefined &&
38
+ args.agentic !== false &&
39
+ args.createCommits !== false;
40
+ return (args.createCommits !== true &&
41
+ !agenticMayEnableCommits &&
42
+ args.commitPrefix !== undefined &&
43
+ args.commitPrefix !== exports.DEFAULT_MIGRATION_COMMIT_PREFIX);
44
+ }
23
45
  function withMigrationOptions(yargs) {
24
46
  return (0, shared_options_1.withVerbose)(yargs)
25
47
  .positional('packageAndVersion', {
@@ -68,14 +90,14 @@ function withMigrationOptions(yargs) {
68
90
  default: false,
69
91
  })
70
92
  .option('mode', {
71
- describe: "Restrict which packages to migrate. Only applies when migrating Nx itself. 'first-party' processes only Nx and its plugins (the target package plus its nx.packageGroup); 'third-party' processes only the third-party dependencies referenced by Nx packageJsonUpdates entries, catching up on any updates that may have been skipped previously; 'all' processes everything. When targeting Nx in an interactive terminal, prompts for the value if not provided; otherwise defaults to 'all'.",
93
+ describe: "Restrict which packages to migrate. Only applies when migrating Nx itself. 'first-party' processes only Nx and its plugins (the target package plus its nx.packageGroup); 'third-party' processes only the third-party dependencies referenced by Nx packageJsonUpdates entries, catching up on any updates that may have been skipped previously; 'all' processes everything. The 'first-party' and 'third-party' modes are only available on Nx v23+: 'third-party' requires the workspace to already be on v23+, and 'first-party' requires migrating from v22+ into a v23+ target. When targeting Nx in an interactive terminal, prompts for the value if not provided; otherwise defaults to 'all'.",
72
94
  type: 'string',
73
- choices: ['first-party', 'third-party', 'all'],
95
+ choices: exports.MIGRATE_MODES,
74
96
  })
75
97
  .option('multiMajorMode', {
76
98
  describe: "Skip the multi-major migration prompt/warning and pick how to handle the jump. 'direct' migrates straight to the requested target. 'gradual' migrates to the smallest recommended step (re-run `nx migrate` to continue toward the original target). Equivalent env var: NX_MULTI_MAJOR_MODE=direct|gradual.",
77
99
  type: 'string',
78
- choices: ['direct', 'gradual'],
100
+ choices: exports.MULTI_MAJOR_MODES,
79
101
  })
80
102
  .option('agentic', {
81
103
  describe: 'Enable the agentic flow for prompt-based migrations and AI-driven review. Pass `--agentic=<agent>` to pin a specific agent (claude-code, codex, or opencode). Pass `--agentic=false` or `--no-agentic` to disable.',
@@ -86,10 +108,18 @@ function withMigrationOptions(yargs) {
86
108
  type: 'boolean',
87
109
  })
88
110
  .check(({ createCommits, commitPrefix, from, excludeAppliedMigrations, mode, agentic, }) => {
89
- const agenticMayEnableCommits = agentic !== undefined && agentic !== false && createCommits !== false;
90
- if (createCommits !== true &&
91
- !agenticMayEnableCommits &&
92
- commitPrefix !== exports.DEFAULT_MIGRATION_COMMIT_PREFIX) {
111
+ // Only an explicit `--no-create-commits` is decidable here, before the
112
+ // nx.json overlay runs: an explicit `false` can't be rescued by nx.json
113
+ // (the CLI flag wins, and the agentic flow can't enable commits when
114
+ // they're explicitly off). When `createCommits` is undefined, nx.json
115
+ // may still enable commits, so defer to the post-overlay
116
+ // `assertCommitPrefixHasCommits` check.
117
+ if (createCommits === false &&
118
+ customCommitPrefixHasNoEffect({
119
+ createCommits,
120
+ commitPrefix,
121
+ agentic,
122
+ })) {
93
123
  throw new Error('Error: Providing a custom commit prefix requires --create-commits to be enabled');
94
124
  }
95
125
  if (excludeAppliedMigrations && !from && mode !== 'third-party') {
@@ -0,0 +1,27 @@
1
+ import type { NxMigrateConfiguration } from '../../config/nx-json';
2
+ import { type MigrateArgs } from './command-object';
3
+ /**
4
+ * Overlays `nx.json` `migrate` defaults onto the raw `nx migrate` CLI args so a
5
+ * CLI flag always wins, then `nx.json`, then the built-in default. Returns a new
6
+ * args object; the input is not mutated.
7
+ *
8
+ * Phase-aware: generate-only options (`mode`, `multiMajorMode`) are applied only
9
+ * when not running migrations; run-only options (`createCommits`,
10
+ * `commitPrefix`, `agentic`, `validate`) only when running migrations. This
11
+ * mirrors where each option is consumed and avoids tripping the "cannot be
12
+ * combined with --run-migrations" guards in `parseMigrationsOptions`.
13
+ *
14
+ * `mode` is carried as `modeFromConfig` rather than `mode` so it is never
15
+ * mistaken for an explicit `--mode`: `resolveMode` applies it only when the
16
+ * target is Nx itself, leaving `nx migrate <non-nx-pkg>` unaffected.
17
+ */
18
+ export declare function applyNxJsonMigrateDefaults(args: MigrateArgs, migrateConfig: NxMigrateConfiguration | undefined, env?: NodeJS.ProcessEnv): MigrateArgs;
19
+ /**
20
+ * The single authority for the "a custom commit prefix needs commits enabled"
21
+ * invariant. Run it against the final merged args (after
22
+ * `applyNxJsonMigrateDefaults`): the yargs `.check()` only sees the CLI args,
23
+ * but nx.json may enable commits via `createCommits` or `agentic`. The CLI
24
+ * `.check()` only fast-fails the unrescuable explicit `--no-create-commits`
25
+ * case; everything else is decided here.
26
+ */
27
+ export declare function assertCommitPrefixHasCommits(merged: MigrateArgs): void;