nx 23.0.0-beta.2 → 23.0.0-beta.21

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 (274) hide show
  1. package/dist/bin/init-local.js +11 -20
  2. package/dist/bin/nx.d.ts +1 -0
  3. package/dist/bin/nx.js +28 -2
  4. package/dist/plugins/package-json.js +4 -2
  5. package/dist/src/adapter/ngcli-adapter.d.ts +2 -1
  6. package/dist/src/adapter/ngcli-adapter.js +25 -2
  7. package/dist/src/ai/clone-ai-config-repo.js +20 -3
  8. package/dist/src/analytics/analytics.js +10 -1
  9. package/dist/src/command-line/add/completion.d.ts +1 -0
  10. package/dist/src/command-line/add/completion.js +15 -0
  11. package/dist/src/command-line/affected/completion.d.ts +1 -0
  12. package/dist/src/command-line/affected/completion.js +15 -0
  13. package/dist/src/command-line/completion/argv-layout.d.ts +6 -0
  14. package/dist/src/command-line/completion/argv-layout.js +19 -0
  15. package/dist/src/command-line/completion/command-completions.d.ts +19 -0
  16. package/dist/src/command-line/completion/command-completions.js +120 -0
  17. package/dist/src/command-line/completion/command-handlers.d.ts +30 -0
  18. package/dist/src/command-line/completion/command-handlers.js +69 -0
  19. package/dist/src/command-line/completion/command-object.d.ts +10 -0
  20. package/dist/src/command-line/completion/command-object.js +95 -0
  21. package/dist/src/command-line/completion/completion-providers.d.ts +21 -0
  22. package/dist/src/command-line/completion/completion-providers.js +194 -0
  23. package/dist/src/command-line/completion/infix-targets.d.ts +1 -0
  24. package/dist/src/command-line/completion/infix-targets.js +48 -0
  25. package/dist/src/command-line/completion/metadata.d.ts +22 -0
  26. package/dist/src/command-line/completion/metadata.js +71 -0
  27. package/dist/src/command-line/completion/registrations.d.ts +9 -0
  28. package/dist/src/command-line/completion/registrations.js +16 -0
  29. package/dist/src/command-line/completion/scripts/bash.sh +51 -0
  30. package/dist/src/command-line/completion/scripts/fish.fish +47 -0
  31. package/dist/src/command-line/completion/scripts/powershell.ps1 +52 -0
  32. package/dist/src/command-line/completion/scripts/zsh.zsh +69 -0
  33. package/dist/src/command-line/completion/scripts.d.ts +18 -0
  34. package/dist/src/command-line/completion/scripts.js +140 -0
  35. package/dist/src/command-line/completion/trigger.d.ts +3 -0
  36. package/dist/src/command-line/completion/trigger.js +21 -0
  37. package/dist/src/command-line/completion/value-completions.d.ts +3 -0
  38. package/dist/src/command-line/completion/value-completions.js +21 -0
  39. package/dist/src/command-line/examples.js +1 -1
  40. package/dist/src/command-line/format/format.js +15 -5
  41. package/dist/src/command-line/generate/completion.d.ts +1 -0
  42. package/dist/src/command-line/generate/completion.js +9 -0
  43. package/dist/src/command-line/graph/completion.d.ts +1 -0
  44. package/dist/src/command-line/graph/completion.js +13 -0
  45. package/dist/src/command-line/init/implementation/angular/standalone-workspace.js +14 -18
  46. package/dist/src/command-line/init/implementation/dot-nx/add-nx-scripts.js +1 -0
  47. package/dist/src/command-line/init/implementation/utils.d.ts +7 -1
  48. package/dist/src/command-line/init/implementation/utils.js +51 -14
  49. package/dist/src/command-line/migrate/agentic/capture-generator-output.d.ts +22 -0
  50. package/dist/src/command-line/migrate/agentic/capture-generator-output.js +100 -0
  51. package/dist/src/command-line/migrate/agentic/cli-args.d.ts +12 -0
  52. package/dist/src/command-line/migrate/agentic/cli-args.js +38 -0
  53. package/dist/src/command-line/migrate/agentic/definitions.d.ts +6 -0
  54. package/dist/src/command-line/migrate/agentic/definitions.js +98 -0
  55. package/dist/src/command-line/migrate/agentic/detect-installed.d.ts +10 -0
  56. package/dist/src/command-line/migrate/agentic/detect-installed.js +68 -0
  57. package/dist/src/command-line/migrate/agentic/handoff-gitignore.d.ts +46 -0
  58. package/dist/src/command-line/migrate/agentic/handoff-gitignore.js +87 -0
  59. package/dist/src/command-line/migrate/agentic/handoff.d.ts +63 -0
  60. package/dist/src/command-line/migrate/agentic/handoff.js +183 -0
  61. package/dist/src/command-line/migrate/agentic/inception.d.ts +9 -0
  62. package/dist/src/command-line/migrate/agentic/inception.js +15 -0
  63. package/dist/src/command-line/migrate/agentic/print-dropped-agent-context.d.ts +22 -0
  64. package/dist/src/command-line/migrate/agentic/print-dropped-agent-context.js +50 -0
  65. package/dist/src/command-line/migrate/agentic/prompts/generic-validation.d.ts +51 -0
  66. package/dist/src/command-line/migrate/agentic/prompts/generic-validation.js +65 -0
  67. package/dist/src/command-line/migrate/agentic/prompts/hybrid-prompt-migration.d.ts +44 -0
  68. package/dist/src/command-line/migrate/agentic/prompts/hybrid-prompt-migration.js +52 -0
  69. package/dist/src/command-line/migrate/agentic/prompts/prompt-migration.d.ts +21 -0
  70. package/dist/src/command-line/migrate/agentic/prompts/prompt-migration.js +26 -0
  71. package/dist/src/command-line/migrate/agentic/prompts/shared-rendering.d.ts +18 -0
  72. package/dist/src/command-line/migrate/agentic/prompts/shared-rendering.js +130 -0
  73. package/dist/src/command-line/migrate/agentic/prompts/system-prompt.d.ts +46 -0
  74. package/dist/src/command-line/migrate/agentic/prompts/system-prompt.js +88 -0
  75. package/dist/src/command-line/migrate/agentic/run-step.d.ts +51 -0
  76. package/dist/src/command-line/migrate/agentic/run-step.js +121 -0
  77. package/dist/src/command-line/migrate/agentic/runner.d.ts +33 -0
  78. package/dist/src/command-line/migrate/agentic/runner.js +442 -0
  79. package/dist/src/command-line/migrate/agentic/select.d.ts +14 -0
  80. package/dist/src/command-line/migrate/agentic/select.js +150 -0
  81. package/dist/src/command-line/migrate/agentic/types.d.ts +102 -0
  82. package/dist/src/command-line/migrate/agentic/types.js +2 -0
  83. package/dist/src/command-line/migrate/command-object.d.ts +1 -0
  84. package/dist/src/command-line/migrate/command-object.js +32 -7
  85. package/dist/src/command-line/migrate/migrate-commits.d.ts +50 -0
  86. package/dist/src/command-line/migrate/migrate-commits.js +102 -0
  87. package/dist/src/command-line/migrate/migrate-output.d.ts +180 -0
  88. package/dist/src/command-line/migrate/migrate-output.js +258 -0
  89. package/dist/src/command-line/migrate/migrate.d.ts +122 -12
  90. package/dist/src/command-line/migrate/migrate.js +1036 -217
  91. package/dist/src/command-line/migrate/migration-shape.d.ts +8 -0
  92. package/dist/src/command-line/migrate/migration-shape.js +13 -0
  93. package/dist/src/command-line/migrate/multi-major.d.ts +30 -0
  94. package/dist/src/command-line/migrate/multi-major.js +185 -0
  95. package/dist/src/command-line/migrate/prompt-files.d.ts +31 -0
  96. package/dist/src/command-line/migrate/prompt-files.js +141 -0
  97. package/dist/src/command-line/migrate/run-migration-process.js +28 -6
  98. package/dist/src/command-line/migrate/safe-prompt.d.ts +28 -0
  99. package/dist/src/command-line/migrate/safe-prompt.js +49 -0
  100. package/dist/src/command-line/migrate/update-filters.d.ts +11 -0
  101. package/dist/src/command-line/migrate/update-filters.js +44 -0
  102. package/dist/src/command-line/migrate/version-utils.d.ts +6 -0
  103. package/dist/src/command-line/migrate/version-utils.js +59 -0
  104. package/dist/src/command-line/nx-commands.js +9 -0
  105. package/dist/src/command-line/release/config/config.d.ts +3 -6
  106. package/dist/src/command-line/release/config/config.js +77 -45
  107. package/dist/src/command-line/release/config/use-legacy-versioning.d.ts +2 -0
  108. package/dist/src/command-line/release/config/use-legacy-versioning.js +8 -0
  109. package/dist/src/command-line/release/utils/release-graph.js +2 -3
  110. package/dist/src/command-line/release/utils/repository-git-tags.js +1 -1
  111. package/dist/src/command-line/release/utils/resolve-changelog-renderer.js +7 -19
  112. package/dist/src/command-line/release/version/resolve-current-version.js +1 -1
  113. package/dist/src/command-line/release/version/version-actions.js +3 -7
  114. package/dist/src/command-line/report/report.js +2 -2
  115. package/dist/src/command-line/run/completion.d.ts +1 -0
  116. package/dist/src/command-line/run/completion.js +7 -0
  117. package/dist/src/command-line/run-many/completion.d.ts +1 -0
  118. package/dist/src/command-line/run-many/completion.js +13 -0
  119. package/dist/src/command-line/show/completion.d.ts +1 -0
  120. package/dist/src/command-line/show/completion.js +27 -0
  121. package/dist/src/command-line/show/show-target/info.d.ts +1 -0
  122. package/dist/src/command-line/show/show-target/info.js +100 -19
  123. package/dist/src/command-line/watch/command-object.js +24 -4
  124. package/dist/src/command-line/watch/completion.d.ts +1 -0
  125. package/dist/src/command-line/watch/completion.js +10 -0
  126. package/dist/src/command-line/watch/watch.d.ts +7 -0
  127. package/dist/src/command-line/watch/watch.js +1 -1
  128. package/dist/src/config/misc-interfaces.d.ts +25 -2
  129. package/dist/src/config/nx-json.d.ts +43 -56
  130. package/dist/src/config/schema-utils.d.ts +21 -0
  131. package/dist/src/config/schema-utils.js +92 -18
  132. package/dist/src/config/task-graph.d.ts +4 -107
  133. package/dist/src/core/graph/main.js +1 -1
  134. package/dist/src/core/graph/styles.css +2 -3
  135. package/dist/src/core/graph/styles.js +1 -1
  136. package/dist/src/daemon/client/client.d.ts +1 -1
  137. package/dist/src/daemon/server/file-watching/file-watcher-sockets.d.ts +1 -1
  138. package/dist/src/daemon/server/file-watching/file-watcher-sockets.js +1 -1
  139. package/dist/src/daemon/server/file-watching/route-workspace-changes.d.ts +9 -0
  140. package/dist/src/daemon/server/file-watching/route-workspace-changes.js +76 -0
  141. package/dist/src/daemon/server/project-graph-incremental-recomputation.js +45 -11
  142. package/dist/src/daemon/server/server.js +4 -43
  143. package/dist/src/daemon/server/start.d.ts +1 -1
  144. package/dist/src/daemon/server/start.js +2 -0
  145. package/dist/src/devkit-exports.d.ts +2 -2
  146. package/dist/src/devkit-internals.d.ts +5 -1
  147. package/dist/src/devkit-internals.js +17 -1
  148. package/dist/src/executors/run-commands/running-tasks.d.ts +7 -0
  149. package/dist/src/executors/run-commands/running-tasks.js +178 -105
  150. package/dist/src/executors/run-script/run-script.impl.js +3 -10
  151. package/dist/src/executors/utils/convert-nx-executor.js +1 -1
  152. package/dist/src/hasher/hash-plan-inspector.d.ts +1 -1
  153. package/dist/src/hasher/task-hasher.js +6 -4
  154. package/dist/src/index.d.ts +1 -1
  155. package/dist/src/index.js +0 -3
  156. package/dist/src/migrations/update-16-2-0/remove-run-commands-output-path.js +6 -2
  157. package/dist/src/migrations/update-17-0-0/move-cache-directory.md +31 -0
  158. package/dist/src/migrations/update-17-0-0/use-minimal-config-for-tasks-runner-options.js +20 -4
  159. package/dist/src/migrations/update-20-0-0/move-use-daemon-process.md +27 -0
  160. package/dist/src/migrations/update-20-0-1/use-legacy-cache.md +24 -0
  161. package/dist/src/migrations/update-21-0-0/release-changelog-config-changes.md +49 -0
  162. package/dist/src/migrations/update-21-0-0/release-version-config-changes.md +54 -0
  163. package/dist/src/migrations/update-21-0-0/remove-custom-tasks-runner.md +28 -0
  164. package/dist/src/migrations/update-21-0-0/remove-legacy-cache.md +22 -0
  165. package/dist/src/migrations/update-22-2-0/add-self-healing-to-gitignore.md +11 -0
  166. package/dist/src/migrations/update-23-0-0/add-migrate-runs-to-git-ignore.d.ts +2 -0
  167. package/dist/src/migrations/update-23-0-0/add-migrate-runs-to-git-ignore.js +16 -0
  168. package/dist/src/migrations/update-23-0-0/consolidate-release-tag-config.d.ts +9 -0
  169. package/dist/src/migrations/update-23-0-0/consolidate-release-tag-config.js +18 -0
  170. package/dist/src/migrations/update-23-0-0/convert-target-defaults-to-array.d.ts +35 -0
  171. package/dist/src/migrations/update-23-0-0/convert-target-defaults-to-array.js +139 -0
  172. package/dist/src/migrations/update-23-0-0/convert-target-defaults-to-array.md +66 -0
  173. package/dist/src/native/index.d.ts +79 -2
  174. package/dist/src/native/native-bindings.js +3 -0
  175. package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
  176. package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
  177. package/dist/src/plugins/js/lock-file/npm-parser.js +37 -19
  178. package/dist/src/plugins/js/lock-file/pnpm-parser.js +51 -4
  179. package/dist/src/plugins/js/lock-file/project-graph-pruning.js +12 -4
  180. package/dist/src/plugins/js/utils/packages.js +1 -1
  181. package/dist/src/plugins/js/utils/register.d.ts +103 -14
  182. package/dist/src/plugins/js/utils/register.js +434 -39
  183. package/dist/src/plugins/js/utils/typescript.d.ts +7 -0
  184. package/dist/src/plugins/js/utils/typescript.js +39 -0
  185. package/dist/src/plugins/package-json/create-nodes.d.ts +3 -2
  186. package/dist/src/plugins/package-json/create-nodes.js +7 -5
  187. package/dist/src/project-graph/affected/locators/project-glob-changes.js +2 -1
  188. package/dist/src/project-graph/build-project-graph.d.ts +5 -0
  189. package/dist/src/project-graph/build-project-graph.js +6 -2
  190. package/dist/src/project-graph/file-map-utils.d.ts +5 -0
  191. package/dist/src/project-graph/file-map-utils.js +10 -1
  192. package/dist/src/project-graph/plugins/get-plugins.d.ts +7 -2
  193. package/dist/src/project-graph/plugins/get-plugins.js +8 -5
  194. package/dist/src/project-graph/plugins/isolation/plugin-worker.d.ts +1 -0
  195. package/dist/src/project-graph/plugins/isolation/plugin-worker.js +2 -0
  196. package/dist/src/project-graph/plugins/resolve-plugin.d.ts +7 -4
  197. package/dist/src/project-graph/plugins/resolve-plugin.js +152 -33
  198. package/dist/src/project-graph/plugins/tasks-execution-hooks.js +4 -2
  199. package/dist/src/project-graph/plugins/transpiler.d.ts +12 -0
  200. package/dist/src/project-graph/plugins/transpiler.js +37 -0
  201. package/dist/src/project-graph/plugins/utils.js +13 -7
  202. package/dist/src/project-graph/project-graph.js +1 -1
  203. package/dist/src/project-graph/utils/project-configuration/target-defaults.d.ts +95 -4
  204. package/dist/src/project-graph/utils/project-configuration/target-defaults.js +515 -68
  205. package/dist/src/project-graph/utils/project-configuration-utils.d.ts +13 -5
  206. package/dist/src/project-graph/utils/project-configuration-utils.js +14 -6
  207. package/dist/src/project-graph/utils/retrieve-workspace-files.js +1 -1
  208. package/dist/src/tasks-runner/create-task-graph.d.ts +4 -4
  209. package/dist/src/tasks-runner/create-task-graph.js +1 -1
  210. package/dist/src/tasks-runner/default-tasks-runner.d.ts +0 -2
  211. package/dist/src/tasks-runner/forked-process-task-runner.d.ts +1 -1
  212. package/dist/src/tasks-runner/forked-process-task-runner.js +11 -6
  213. package/dist/src/tasks-runner/init-tasks-runner.d.ts +0 -15
  214. package/dist/src/tasks-runner/init-tasks-runner.js +0 -63
  215. package/dist/src/tasks-runner/legacy-depends-on-warning.d.ts +18 -0
  216. package/dist/src/tasks-runner/legacy-depends-on-warning.js +109 -0
  217. package/dist/src/tasks-runner/life-cycle.d.ts +7 -8
  218. package/dist/src/tasks-runner/life-cycles/invoke-runner-terminal-output-life-cycle.js +6 -6
  219. package/dist/src/tasks-runner/life-cycles/task-history-life-cycle-old.js +13 -2
  220. package/dist/src/tasks-runner/life-cycles/task-history-life-cycle.js +16 -5
  221. package/dist/src/tasks-runner/life-cycles/tui-summary-life-cycle.js +11 -2
  222. package/dist/src/tasks-runner/pseudo-terminal.d.ts +1 -1
  223. package/dist/src/tasks-runner/pseudo-terminal.js +22 -10
  224. package/dist/src/tasks-runner/run-command.js +8 -11
  225. package/dist/src/tasks-runner/running-tasks/batch-process.d.ts +1 -1
  226. package/dist/src/tasks-runner/running-tasks/batch-process.js +3 -5
  227. package/dist/src/tasks-runner/running-tasks/node-child-process.d.ts +2 -2
  228. package/dist/src/tasks-runner/running-tasks/node-child-process.js +5 -7
  229. package/dist/src/tasks-runner/task-env.d.ts +1 -1
  230. package/dist/src/tasks-runner/task-env.js +6 -1
  231. package/dist/src/tasks-runner/task-orchestrator.d.ts +11 -1
  232. package/dist/src/tasks-runner/task-orchestrator.js +112 -38
  233. package/dist/src/tasks-runner/tasks-schedule.js +3 -3
  234. package/dist/src/tasks-runner/utils.d.ts +7 -8
  235. package/dist/src/tasks-runner/utils.js +23 -27
  236. package/dist/src/utils/child-process.js +2 -2
  237. package/dist/src/utils/compile-cache.d.ts +24 -0
  238. package/dist/src/utils/compile-cache.js +49 -0
  239. package/dist/src/utils/enable-compile-cache.d.ts +1 -0
  240. package/dist/src/utils/enable-compile-cache.js +7 -0
  241. package/dist/src/utils/fileutils.d.ts +0 -8
  242. package/dist/src/utils/fileutils.js +0 -40
  243. package/dist/src/utils/git-utils.d.ts +15 -0
  244. package/dist/src/utils/git-utils.js +138 -0
  245. package/dist/src/utils/handle-import.d.ts +4 -1
  246. package/dist/src/utils/handle-import.js +56 -2
  247. package/dist/src/utils/has-nx-js-plugin.d.ts +9 -0
  248. package/dist/src/utils/has-nx-js-plugin.js +24 -0
  249. package/dist/src/utils/installed-nx-version.d.ts +14 -4
  250. package/dist/src/utils/installed-nx-version.js +54 -7
  251. package/dist/src/utils/logger.d.ts +12 -1
  252. package/dist/src/utils/logger.js +57 -36
  253. package/dist/src/utils/nx-key.d.ts +0 -1
  254. package/dist/src/utils/nx-key.js +20 -23
  255. package/dist/src/utils/nx-package-group.d.ts +8 -0
  256. package/dist/src/utils/nx-package-group.js +15 -0
  257. package/dist/src/utils/output.d.ts +3 -2
  258. package/dist/src/utils/output.js +29 -28
  259. package/dist/src/utils/package-json.d.ts +14 -1
  260. package/dist/src/utils/package-json.js +20 -21
  261. package/dist/src/utils/perf-logging.js +3 -1
  262. package/dist/src/utils/plugin-cache-utils.d.ts +13 -4
  263. package/dist/src/utils/plugin-cache-utils.js +23 -13
  264. package/dist/src/utils/plugins/local-plugins.d.ts +18 -0
  265. package/dist/src/utils/plugins/local-plugins.js +30 -0
  266. package/dist/src/utils/tar.d.ts +8 -0
  267. package/dist/src/utils/tar.js +44 -0
  268. package/migrations.json +16 -0
  269. package/package.json +28 -28
  270. package/schemas/nx-schema.json +114 -80
  271. package/dist/src/plugins/js/project-graph/build-dependencies/strip-source-code.d.ts +0 -7
  272. package/dist/src/plugins/js/project-graph/build-dependencies/strip-source-code.js +0 -155
  273. package/dist/src/plugins/js/project-graph/build-dependencies/typescript-import-locator.d.ts +0 -16
  274. package/dist/src/plugins/js/project-graph/build-dependencies/typescript-import-locator.js +0 -121
@@ -1,12 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Migrator = void 0;
3
+ exports.ChangedDepInstaller = exports.isHybridMigration = exports.isPromptOnlyMigration = exports.filterDowngradedUpdates = exports.Migrator = exports.normalizeVersion = void 0;
4
4
  exports.formatCommandFailure = formatCommandFailure;
5
- exports.normalizeVersion = normalizeVersion;
5
+ exports.resolveCanonicalNxPackage = resolveCanonicalNxPackage;
6
+ exports.resolveMode = resolveMode;
6
7
  exports.parseMigrationsOptions = parseMigrationsOptions;
7
8
  exports.isNpmPeerDepsError = isNpmPeerDepsError;
9
+ exports.resolveAgenticRunId = resolveAgenticRunId;
10
+ exports.formatSkippedPromptsNextStep = formatSkippedPromptsNextStep;
11
+ exports.resolveCreateCommits = resolveCreateCommits;
12
+ exports.resolveShouldRunValidation = resolveShouldRunValidation;
8
13
  exports.executeMigrations = executeMigrations;
9
14
  exports.runNxOrAngularMigration = runNxOrAngularMigration;
15
+ exports.parseMigrationReturn = parseMigrationReturn;
10
16
  exports.migrate = migrate;
11
17
  exports.runMigration = runMigration;
12
18
  exports.readMigrationCollection = readMigrationCollection;
@@ -15,7 +21,7 @@ exports.nxCliPath = nxCliPath;
15
21
  const tslib_1 = require("tslib");
16
22
  const pc = tslib_1.__importStar(require("picocolors"));
17
23
  const child_process_1 = require("child_process");
18
- const enquirer_1 = require("enquirer");
24
+ const safe_prompt_1 = require("./safe-prompt");
19
25
  const handle_import_1 = require("../../utils/handle-import");
20
26
  const path_1 = require("path");
21
27
  const module_1 = require("module");
@@ -25,6 +31,7 @@ const node_url_1 = require("node:url");
25
31
  const util_1 = require("util");
26
32
  const tree_1 = require("../../generators/tree");
27
33
  const fileutils_1 = require("../../utils/fileutils");
34
+ const tar_1 = require("../../utils/tar");
28
35
  const write_formatted_json_file_1 = require("../../utils/write-formatted-json-file");
29
36
  const logger_1 = require("../../utils/logger");
30
37
  const git_utils_1 = require("../../utils/git-utils");
@@ -37,6 +44,7 @@ const fs_1 = require("fs");
37
44
  const workspace_root_1 = require("../../utils/workspace-root");
38
45
  const is_ci_1 = require("../../utils/is-ci");
39
46
  const installation_directory_1 = require("../../utils/installation-directory");
47
+ const installed_nx_version_1 = require("../../utils/installed-nx-version");
40
48
  const configuration_1 = require("../../config/configuration");
41
49
  const child_process_2 = require("../../utils/child-process");
42
50
  const client_1 = require("../../daemon/client/client");
@@ -45,6 +53,19 @@ const project_graph_1 = require("../../project-graph/project-graph");
45
53
  const format_changed_files_with_prettier_if_available_1 = require("../../generators/internal-utils/format-changed-files-with-prettier-if-available");
46
54
  const provenance_1 = require("../../utils/provenance");
47
55
  const catalog_1 = require("../../utils/catalog");
56
+ const multi_major_1 = require("./multi-major");
57
+ const prompt_files_1 = require("./prompt-files");
58
+ const command_object_1 = require("./command-object");
59
+ const handoff_gitignore_1 = require("./agentic/handoff-gitignore");
60
+ const migrate_commits_1 = require("./migrate-commits");
61
+ const migrate_output_1 = require("./migrate-output");
62
+ const migration_shape_1 = require("./migration-shape");
63
+ Object.defineProperty(exports, "isHybridMigration", { enumerable: true, get: function () { return migration_shape_1.isHybridMigration; } });
64
+ Object.defineProperty(exports, "isPromptOnlyMigration", { enumerable: true, get: function () { return migration_shape_1.isPromptOnlyMigration; } });
65
+ const update_filters_1 = require("./update-filters");
66
+ Object.defineProperty(exports, "filterDowngradedUpdates", { enumerable: true, get: function () { return update_filters_1.filterDowngradedUpdates; } });
67
+ const version_utils_1 = require("./version-utils");
68
+ Object.defineProperty(exports, "normalizeVersion", { enumerable: true, get: function () { return version_utils_1.normalizeVersion; } });
48
69
  const execAsync = (0, util_1.promisify)(child_process_1.exec);
49
70
  function formatCommandFailure(command, error) {
50
71
  const normalizeCommandOutput = (output) => {
@@ -76,33 +97,6 @@ function runOrReturnExitCode(run) {
76
97
  throw e;
77
98
  }
78
99
  }
79
- function normalizeVersion(version) {
80
- const [semver, ...prereleaseTagParts] = version.split('-');
81
- // Handle versions like 1.0.0-beta-next.2
82
- const prereleaseTag = prereleaseTagParts.join('-');
83
- const [major, minor, patch] = semver.split('.');
84
- const newSemver = `${major || 0}.${minor || 0}.${patch || 0}`;
85
- const newVersion = prereleaseTag
86
- ? `${newSemver}-${prereleaseTag}`
87
- : newSemver;
88
- const withoutPatch = `${major || 0}.${minor || 0}.0`;
89
- const withoutPatchAndMinor = `${major || 0}.0.0`;
90
- const variationsToCheck = [
91
- newVersion,
92
- newSemver,
93
- withoutPatch,
94
- withoutPatchAndMinor,
95
- ];
96
- for (const variation of variationsToCheck) {
97
- try {
98
- if ((0, semver_1.gt)(variation, '0.0.0')) {
99
- return variation;
100
- }
101
- }
102
- catch { }
103
- }
104
- return '0.0.0';
105
- }
106
100
  function cleanSemver(version) {
107
101
  return (0, semver_1.clean)(version) ?? (0, semver_1.coerce)(version);
108
102
  }
@@ -114,6 +108,10 @@ class Migrator {
114
108
  this.packageUpdates = {};
115
109
  this.collectedVersions = {};
116
110
  this.promptAnswers = {};
111
+ if ((opts.mode === 'first-party' || opts.mode === 'third-party') &&
112
+ !opts.firstPartyPackages) {
113
+ throw new Error(`Error: 'firstPartyPackages' is required when 'mode' is '${opts.mode}'.`);
114
+ }
117
115
  this.packageJson = opts.packageJson;
118
116
  this.nxInstallation = opts.nxInstallation;
119
117
  this.getInstalledPackageVersion = opts.getInstalledPackageVersion;
@@ -122,6 +120,8 @@ class Migrator {
122
120
  this.to = opts.to;
123
121
  this.interactive = opts.interactive;
124
122
  this.excludeAppliedMigrations = opts.excludeAppliedMigrations;
123
+ this.mode = opts.mode;
124
+ this.firstPartyPackages = opts.firstPartyPackages;
125
125
  }
126
126
  async fetchMigrationConfig(packageName, packageVersion) {
127
127
  const migrationConfig = await this.fetch(packageName, packageVersion);
@@ -135,14 +135,17 @@ class Migrator {
135
135
  version: targetVersion,
136
136
  addToPackageJson: false,
137
137
  });
138
- const migrations = await this.createMigrateJson();
138
+ this.applyModeFilter();
139
+ const { migrations, promptContents } = await this.createMigrateJson();
139
140
  return {
140
141
  packageUpdates: this.packageUpdates,
141
142
  migrations,
143
+ ...(Object.keys(promptContents).length > 0 ? { promptContents } : {}),
142
144
  minVersionWithSkippedUpdates: this.minVersionWithSkippedUpdates,
143
145
  };
144
146
  }
145
147
  async createMigrateJson() {
148
+ const promptContents = {};
146
149
  const migrations = await Promise.all(Object.keys(this.packageUpdates).map(async (packageName) => {
147
150
  if (this.packageUpdates[packageName].ignoreMigrations) {
148
151
  return [];
@@ -151,10 +154,15 @@ class Migrator {
151
154
  if (currentVersion === null)
152
155
  return [];
153
156
  const { version } = this.packageUpdates[packageName];
154
- const { generators } = await this.fetchMigrationConfig(packageName, version);
155
- if (!generators)
157
+ const { generators: migrationEntries, resolvedPromptFiles } = await this.fetchMigrationConfig(packageName, version);
158
+ if (!migrationEntries)
156
159
  return [];
157
- return Object.entries(generators)
160
+ if (resolvedPromptFiles) {
161
+ for (const [promptPath, content] of Object.entries(resolvedPromptFiles)) {
162
+ promptContents[(0, prompt_files_1.promptContentKey)(packageName, promptPath)] = content;
163
+ }
164
+ }
165
+ return Object.entries(migrationEntries)
158
166
  .filter(([, migration]) => migration.version &&
159
167
  this.gt(migration.version, currentVersion) &&
160
168
  this.lte(migration.version, version) &&
@@ -165,7 +173,7 @@ class Migrator {
165
173
  name: migrationName,
166
174
  }));
167
175
  }));
168
- return migrations.flat();
176
+ return { migrations: migrations.flat(), promptContents };
169
177
  }
170
178
  async buildPackageJsonUpdates(targetPackage, target) {
171
179
  const packagesToCheck = await this.populatePackageJsonUpdatesAndGetPackagesToCheck(targetPackage, target);
@@ -263,7 +271,7 @@ class Migrator {
263
271
  if (ignorePackageGroup) {
264
272
  return [];
265
273
  }
266
- const packageGroup = packageName === '@nrwl/workspace' && (0, semver_1.lt)(targetVersion, '14.0.0-beta.0')
274
+ const packageGroup = packageName === '@nrwl/workspace' && (0, version_utils_1.isLegacyEra)(targetVersion)
267
275
  ? LEGACY_NRWL_PACKAGE_GROUP
268
276
  : (migrationConfig.packageGroup ?? []);
269
277
  let packageGroupOrder = [];
@@ -307,6 +315,9 @@ class Migrator {
307
315
  };
308
316
  const filtered = {};
309
317
  for (const [packageName, packageUpdate] of Object.entries(packageJsonUpdate.packages)) {
318
+ if (this.shouldExcludePackage(packageName)) {
319
+ continue;
320
+ }
310
321
  if (this.shouldApplyPackageUpdate(packageUpdate, packageName, dependencies)) {
311
322
  filtered[packageName] = {
312
323
  version: packageUpdate.version,
@@ -331,6 +342,29 @@ class Migrator {
331
342
  }
332
343
  return filteredPackageJsonUpdates;
333
344
  }
345
+ shouldExcludePackage(packageName) {
346
+ if (!this.firstPartyPackages) {
347
+ return false;
348
+ }
349
+ if (this.mode === 'first-party') {
350
+ return !this.firstPartyPackages.has(packageName);
351
+ }
352
+ return false;
353
+ }
354
+ applyModeFilter() {
355
+ if (this.mode !== 'third-party') {
356
+ return;
357
+ }
358
+ // Cascade walks through first-party packages so cross-plugin third-party
359
+ // deps (e.g. typescript managed by @nx/js but used by @nx/angular) get
360
+ // surfaced. Drop the first-party set from the final result here so only
361
+ // third-party updates land in package.json.
362
+ for (const name of Object.keys(this.packageUpdates)) {
363
+ if (this.firstPartyPackages.has(name)) {
364
+ delete this.packageUpdates[name];
365
+ }
366
+ }
367
+ }
334
368
  shouldApplyPackageUpdate(packageUpdate, packageName, dependencies) {
335
369
  return ((!packageUpdate.ifPackageInstalled ||
336
370
  this.getPkgVersion(packageUpdate.ifPackageInstalled)) &&
@@ -423,7 +457,7 @@ class Migrator {
423
457
  // @ts-expect-error -- enquirer types aren't correct, footer does exist
424
458
  promptConfig.footer = () => pc.dim(` View migration details at https://nx.dev/nx-api/${packageName.replace('@nx/', '')}#${packageUpdateKey.replace(/[-\.]/g, '')}packageupdates`);
425
459
  }
426
- return await (0, enquirer_1.prompt)([promptConfig]).then(({ shouldApply }) => {
460
+ return await (0, safe_prompt_1.migratePrompt)([promptConfig]).then(({ shouldApply }) => {
427
461
  this.promptAnswers[promptKey] = shouldApply;
428
462
  if (!shouldApply &&
429
463
  (!this.minVersionWithSkippedUpdates ||
@@ -442,13 +476,13 @@ class Migrator {
442
476
  return this.getInstalledPackageVersion(pkg, this.installedPkgVersionOverrides);
443
477
  }
444
478
  gt(v1, v2) {
445
- return (0, semver_1.gt)(normalizeVersion(v1), normalizeVersion(v2));
479
+ return (0, semver_1.gt)((0, version_utils_1.normalizeVersion)(v1), (0, version_utils_1.normalizeVersion)(v2));
446
480
  }
447
481
  lt(v1, v2) {
448
- return (0, semver_1.lt)(normalizeVersion(v1), normalizeVersion(v2));
482
+ return (0, semver_1.lt)((0, version_utils_1.normalizeVersion)(v1), (0, version_utils_1.normalizeVersion)(v2));
449
483
  }
450
484
  lte(v1, v2) {
451
- return (0, semver_1.lte)(normalizeVersion(v1), normalizeVersion(v2));
485
+ return (0, semver_1.lte)((0, version_utils_1.normalizeVersion)(v1), (0, version_utils_1.normalizeVersion)(v2));
452
486
  }
453
487
  }
454
488
  exports.Migrator = Migrator;
@@ -475,17 +509,59 @@ const LEGACY_NRWL_PACKAGE_GROUP = [
475
509
  { package: '@nrwl/expo', version: '*' },
476
510
  { package: '@nrwl/tao', version: '*' },
477
511
  ];
478
- async function normalizeVersionWithTagCheck(pkg, version) {
479
- // This doesn't seem like a valid version, lets check if its a tag on the registry.
480
- if (version && !(0, semver_1.parse)(version)) {
481
- try {
482
- return (0, package_manager_1.resolvePackageVersionUsingRegistry)(pkg, version);
483
- }
484
- catch {
485
- // fall through to old logic
486
- }
512
+ function resolveFirstPartyPackages(targetPackage, packageGroup) {
513
+ const set = new Set([targetPackage]);
514
+ for (const { package: name } of packageGroup ?? []) {
515
+ set.add(name);
516
+ }
517
+ return set;
518
+ }
519
+ /**
520
+ * The canonical Nx package for a given target version: `@nrwl/workspace` for
521
+ * legacy (`< 14.0.0-beta.0`), `nx` otherwise. Non-semver inputs (e.g. the
522
+ * literal `'latest'` sentinel before tag resolution) resolve to modern era.
523
+ * Used by `--mode=third-party` to silently swap `@nx/workspace` → `nx` when
524
+ * walking the cascade.
525
+ */
526
+ function resolveCanonicalNxPackage(targetVersion) {
527
+ return (0, version_utils_1.isLegacyEra)(targetVersion) ? '@nrwl/workspace' : 'nx';
528
+ }
529
+ async function resolveMode(mode, targetPackage, targetVersion, context = {
530
+ hasFrom: false,
531
+ hasExcludeAppliedMigrations: false,
532
+ }) {
533
+ if (mode) {
534
+ return mode;
535
+ }
536
+ if (!(0, version_utils_1.isNxEquivalentTarget)(targetPackage, targetVersion)) {
537
+ return 'all';
538
+ }
539
+ if (!process.stdin.isTTY || (0, is_ci_1.isCI)()) {
540
+ return 'all';
541
+ }
542
+ const choices = [
543
+ {
544
+ name: 'first-party',
545
+ message: 'First-party only (Nx and its official packages)',
546
+ },
547
+ ];
548
+ if (!context.hasFrom && !context.hasExcludeAppliedMigrations) {
549
+ choices.push({
550
+ name: 'third-party',
551
+ message: 'Third-party only (deps managed by Nx)',
552
+ });
487
553
  }
488
- return normalizeVersion(version);
554
+ choices.push({
555
+ name: 'all',
556
+ message: 'All (first-party and third-party)',
557
+ });
558
+ const { mode: selected } = await (0, safe_prompt_1.migratePrompt)({
559
+ type: 'select',
560
+ name: 'mode',
561
+ message: 'Which packages would you like to migrate?',
562
+ choices,
563
+ });
564
+ return selected;
489
565
  }
490
566
  async function versionOverrides(overrides, param) {
491
567
  const res = {};
@@ -499,7 +575,7 @@ async function versionOverrides(overrides, param) {
499
575
  if (!selectedPackage || !selectedVersion) {
500
576
  throw new Error(`Incorrect '${param}' section. Use --${param}="package@version"`);
501
577
  }
502
- return normalizeVersionWithTagCheck(selectedPackage, selectedVersion).then((version) => {
578
+ return (0, version_utils_1.normalizeVersionWithTagCheck)(selectedPackage, selectedVersion).then((version) => {
503
579
  res[normalizeSlashes(selectedPackage)] = version;
504
580
  });
505
581
  });
@@ -513,78 +589,219 @@ async function parseTargetPackageAndVersion(args) {
513
589
  if (args.indexOf('@') > -1) {
514
590
  const i = args.lastIndexOf('@');
515
591
  if (i === 0) {
516
- const targetPackage = args.trim();
517
- const targetVersion = 'latest';
518
- return { targetPackage, targetVersion };
519
- }
520
- else {
521
- const targetPackage = args.substring(0, i);
522
- const maybeVersion = args.substring(i + 1);
523
- if (!targetPackage || !maybeVersion) {
524
- throw new Error(`Provide the correct package name and version. E.g., my-package@9.0.0.`);
525
- }
526
- const targetVersion = await normalizeVersionWithTagCheck(targetPackage, maybeVersion);
527
- return { targetPackage, targetVersion };
528
- }
529
- }
530
- else {
531
- if (args === 'latest' ||
532
- args === 'next' ||
533
- args === 'canary' ||
534
- (0, semver_1.valid)(args) ||
535
- args.match(/^\d+(?:\.\d+)?(?:\.\d+)?$/)) {
536
- // Passing `nx` here may seem wrong, but nx and @nrwl/workspace are synced in version.
537
- // We could duplicate the ternary below, but its not necessary since they are equivalent
538
- // on the registry
539
- const targetVersion = await normalizeVersionWithTagCheck('nx', args);
540
- const targetPackage = !['latest', 'next', 'canary'].includes(args) &&
541
- (0, semver_1.lt)(targetVersion, '14.0.0-beta.0')
542
- ? '@nrwl/workspace'
543
- : 'nx';
544
- return {
545
- targetPackage,
546
- targetVersion,
547
- };
548
- }
549
- else {
550
- return {
551
- targetPackage: args,
552
- targetVersion: 'latest',
553
- };
554
- }
555
- }
592
+ return { targetPackage: args.trim(), targetVersion: 'latest' };
593
+ }
594
+ const targetPackage = args.substring(0, i);
595
+ const maybeVersion = args.substring(i + 1);
596
+ if (!targetPackage || !maybeVersion) {
597
+ throw new Error(`Provide the correct package name and version. E.g., my-package@9.0.0.`);
598
+ }
599
+ const targetVersion = await (0, version_utils_1.normalizeVersionWithTagCheck)(targetPackage, maybeVersion);
600
+ return { targetPackage, targetVersion };
601
+ }
602
+ if (version_utils_1.DIST_TAGS.includes(args) ||
603
+ (0, semver_1.valid)(args) ||
604
+ args.match(/^\d+(?:\.\d+)?(?:\.\d+)?$/)) {
605
+ // Passing `nx` here may seem wrong, but nx and @nrwl/workspace are synced in version.
606
+ // We could duplicate the ternary below, but its not necessary since they are equivalent
607
+ // on the registry
608
+ const targetVersion = await (0, version_utils_1.normalizeVersionWithTagCheck)('nx', args);
609
+ const isDistTag = version_utils_1.DIST_TAGS.includes(args);
610
+ const targetPackage = isDistTag
611
+ ? 'nx'
612
+ : resolveCanonicalNxPackage(targetVersion);
613
+ return { targetPackage, targetVersion };
614
+ }
615
+ return { targetPackage: args, targetVersion: 'latest' };
556
616
  }
557
617
  async function parseMigrationsOptions(options) {
558
618
  if (options.runMigrations === '') {
559
619
  options.runMigrations = 'migrations.json';
560
620
  }
561
- if (!options.runMigrations) {
562
- const [from, to] = await Promise.all([
563
- options.from
564
- ? versionOverrides(options.from, 'from')
565
- : Promise.resolve({}),
566
- options.to
567
- ? await versionOverrides(options.to, 'to')
568
- : Promise.resolve({}),
569
- ]);
570
- const { targetPackage, targetVersion } = await parseTargetPackageAndVersion(options['packageAndVersion']);
571
- return {
572
- type: 'generateMigrations',
573
- targetPackage: normalizeSlashes(targetPackage),
574
- targetVersion,
575
- from,
576
- to,
577
- interactive: options.interactive,
578
- excludeAppliedMigrations: options.excludeAppliedMigrations,
579
- };
621
+ if (options.mode && options.runMigrations) {
622
+ throw new Error(`Error: '--mode' cannot be combined with '--run-migrations'.`);
580
623
  }
581
- else {
624
+ if (options.multiMajorMode && options.runMigrations) {
625
+ throw new Error(`Error: '--multi-major-mode' cannot be combined with '--run-migrations'.`);
626
+ }
627
+ if (options.runMigrations) {
582
628
  return {
583
629
  type: 'runMigrations',
584
630
  runMigrations: options.runMigrations,
585
631
  ifExists: options.ifExists,
632
+ agentic: options.agentic,
633
+ validate: options.validate,
586
634
  };
587
635
  }
636
+ assertThirdPartyModeFlagCompatibility(options);
637
+ const [from, to] = await Promise.all([
638
+ options.from
639
+ ? versionOverrides(options.from, 'from')
640
+ : Promise.resolve({}),
641
+ options.to
642
+ ? await versionOverrides(options.to, 'to')
643
+ : Promise.resolve({}),
644
+ ]);
645
+ const positional = options['packageAndVersion'];
646
+ const resolved = await resolveTargetAndMode({ positional, from, options });
647
+ const { mode, installedNxVersion } = resolved;
648
+ let { targetPackage, targetVersion } = resolved;
649
+ // Crossing more than one major can silently skip migrations: each
650
+ // major's metadata may have pruned entries from much-older versions.
651
+ const multiMajorResult = await (0, multi_major_1.maybePromptOrWarnMultiMajorMigration)({
652
+ mode,
653
+ options,
654
+ targetPackage,
655
+ targetVersion,
656
+ });
657
+ targetVersion = multiMajorResult.chosen;
658
+ if (mode === 'third-party') {
659
+ assertThirdPartyTargetBounds({
660
+ targetPackage,
661
+ targetVersion,
662
+ to,
663
+ installedNxVersion,
664
+ });
665
+ }
666
+ return {
667
+ type: 'generateMigrations',
668
+ targetPackage,
669
+ targetVersion,
670
+ from,
671
+ to,
672
+ interactive: options.interactive,
673
+ excludeAppliedMigrations: options.excludeAppliedMigrations,
674
+ mode,
675
+ originalTargetVersion: multiMajorResult.originalTarget,
676
+ multiMajorMode: multiMajorResult.gradual ? 'gradual' : undefined,
677
+ };
678
+ }
679
+ function assertThirdPartyModeFlagCompatibility(options) {
680
+ if (options.mode !== 'third-party')
681
+ return;
682
+ if (options.from) {
683
+ throw new Error(`Error: '--mode=third-party' cannot be combined with '--from'.`);
684
+ }
685
+ if (options.excludeAppliedMigrations === true) {
686
+ throw new Error(`Error: '--mode=third-party' cannot be combined with '--exclude-applied-migrations'.`);
687
+ }
688
+ }
689
+ // Defaults target package/version mode-aware (third-party → installed
690
+ // canonical, otherwise nx@latest) and enforces the era gate when --mode
691
+ // is explicit.
692
+ async function resolveTargetAndMode(args) {
693
+ const { positional, from, options } = args;
694
+ let targetPackage;
695
+ let targetVersion;
696
+ if (positional) {
697
+ const parsed = await parseTargetPackageAndVersion(positional);
698
+ targetPackage = normalizeSlashes(parsed.targetPackage);
699
+ targetVersion = parsed.targetVersion;
700
+ }
701
+ // Resolve mode before defaulting target so the default can depend on the
702
+ // resolved mode (third-party defaults to nx@<installed>; otherwise nx@latest).
703
+ // For bare invocation, `targetPackage='nx'` and `targetVersion='latest'` are
704
+ // safe sentinels: `isNxEquivalentTarget` treats the literal `'latest'` as
705
+ // modern era (semver `lt('latest', '14.0.0-beta.0')` is false).
706
+ const mode = await resolveMode(options.mode, targetPackage ?? 'nx', targetVersion ?? 'latest', {
707
+ hasFrom: Object.keys(from).length > 0,
708
+ hasExcludeAppliedMigrations: options.excludeAppliedMigrations === true,
709
+ });
710
+ let installedNxVersion;
711
+ // For third-party, anchor `targetPackage`/`targetVersion` to the installed
712
+ // canonical when the positional was either omitted or a bare package name
713
+ // (no semver). This keeps the era gate accepting legacy workspaces, the
714
+ // upper-bound gate meaningful, and downstream semver comparisons safe from
715
+ // the literal `'latest'` that `parseTargetPackageAndVersion` emits for bare
716
+ // package names.
717
+ if (mode === 'third-party' && (!positional || !(0, semver_1.valid)(targetVersion))) {
718
+ const installed = resolveInstalledCanonical();
719
+ if (!installed) {
720
+ throw new Error(`Error: '--mode=third-party' requires 'nx' (or '@nrwl/workspace' on Nx <14) to be installed in your workspace. Install dependencies first, then re-run.`);
721
+ }
722
+ installedNxVersion = installed.version;
723
+ targetPackage = installed.canonical;
724
+ targetVersion = installed.version;
725
+ }
726
+ else if (!positional) {
727
+ // Bare invocation: default to `nx@latest` as a literal sentinel rather
728
+ // than resolving via the registry here. Multi-major resolves the dist-tag
729
+ // when needed (and bails gracefully on registry failure), and the cascade
730
+ // resolves it for the walk (honouring `NX_MIGRATE_SKIP_REGISTRY_FETCH`).
731
+ // This matches the resilience of `nx migrate nx`.
732
+ targetPackage = 'nx';
733
+ targetVersion = 'latest';
734
+ }
735
+ if (options.mode && !(0, version_utils_1.isNxEquivalentTarget)(targetPackage, targetVersion)) {
736
+ const isLegacy = (0, version_utils_1.isLegacyEra)(targetVersion);
737
+ const validTargets = isLegacy
738
+ ? `'@nrwl/workspace'`
739
+ : `'nx' or '@nx/workspace'`;
740
+ const eraNote = isLegacy ? ' for Nx <14.0.0' : '';
741
+ throw new Error(`Error: '--mode' requires the target to be ${validTargets}${eraNote}. Got '${targetPackage}@${targetVersion}'.`);
742
+ }
743
+ return {
744
+ targetPackage: targetPackage,
745
+ targetVersion: targetVersion,
746
+ mode,
747
+ installedNxVersion,
748
+ };
749
+ }
750
+ // `--mode=third-party` upper-bound gate. The third-party walk follows nx's
751
+ // `packageGroup`; a target or `--to` above the installed version would
752
+ // expand the walk past it and surface third-party bumps that only exist in
753
+ // the newer plugin's history. The first-party set is sourced from the
754
+ // installed nx package's declared `packageGroup` (authoritative for the
755
+ // user's current Nx universe). Legacy era falls back to the hardcoded
756
+ // `LEGACY_NRWL_PACKAGE_GROUP`.
757
+ function assertThirdPartyTargetBounds(args) {
758
+ const { targetPackage, targetVersion, to, installedNxVersion } = args;
759
+ const canonical = resolveCanonicalNxPackage(targetVersion);
760
+ const isLegacy = canonical === '@nrwl/workspace';
761
+ // Reuse the resolved installed version from `resolveTargetAndMode` when
762
+ // present (it's already era-aware via `resolveInstalledCanonical`).
763
+ // Otherwise fall back to the era-specific reader.
764
+ const installed = installedNxVersion ??
765
+ (isLegacy
766
+ ? (0, installed_nx_version_1.getInstalledLegacyNrwlWorkspaceVersion)()
767
+ : (0, installed_nx_version_1.getInstalledNxVersion)());
768
+ if (!installed) {
769
+ throw new Error(`Error: '--mode=third-party' requires '${canonical}' to be installed in your workspace. Install dependencies first, then re-run.`);
770
+ }
771
+ if ((0, semver_1.gt)(targetVersion, installed)) {
772
+ throw new Error(`Error: '--mode=third-party' cannot migrate to a version higher than what is currently installed (got '${targetPackage}@${targetVersion}', installed '${canonical}@${installed}'). Either drop '--mode=third-party' or lower the target.`);
773
+ }
774
+ const firstPartySet = isLegacy
775
+ ? new Set([
776
+ '@nrwl/workspace',
777
+ ...LEGACY_NRWL_PACKAGE_GROUP.map((p) => p.package),
778
+ ])
779
+ : (0, installed_nx_version_1.getInstalledNxPackageGroup)();
780
+ for (const [pkg, version] of Object.entries(to)) {
781
+ if (firstPartySet.has(pkg) && (0, semver_1.gt)(version, installed)) {
782
+ throw new Error(`Error: '--mode=third-party' cannot migrate to a version higher than what is currently installed (got '--to ${pkg}@${version}', installed '${canonical}@${installed}'). Either drop '--mode=third-party' or lower the '--to' value.`);
783
+ }
784
+ }
785
+ }
786
+ /**
787
+ * Pick the canonical Nx package + version for `--mode=third-party` when the
788
+ * user didn't supply an explicit version. Returns `'nx'` for modern era,
789
+ * falls back to `'@nrwl/workspace'` (legacy era) when only that is installed
790
+ * or when the installed `nx` itself is `<14`.
791
+ */
792
+ function resolveInstalledCanonical() {
793
+ const installedNx = (0, installed_nx_version_1.getInstalledNxVersion)();
794
+ if (installedNx) {
795
+ return {
796
+ canonical: resolveCanonicalNxPackage(installedNx),
797
+ version: installedNx,
798
+ };
799
+ }
800
+ const installedLegacy = (0, installed_nx_version_1.getInstalledLegacyNrwlWorkspaceVersion)();
801
+ if (installedLegacy) {
802
+ return { canonical: '@nrwl/workspace', version: installedLegacy };
803
+ }
804
+ return null;
588
805
  }
589
806
  function createInstalledPackageVersionsResolver(root) {
590
807
  const cache = {};
@@ -731,11 +948,22 @@ async function downloadPackageMigrationsFromRegistry(packageName, packageVersion
731
948
  let result;
732
949
  try {
733
950
  const { tarballPath } = await (0, package_manager_1.packageRegistryPack)(dir, packageName, packageVersion);
734
- const migrations = await (0, fileutils_1.extractFileFromTarball)((0, path_1.join)(dir, tarballPath), (0, path_2.joinPathFragments)('package', migrationsFilePath), (0, path_1.join)(dir, migrationsFilePath)).then((path) => (0, fileutils_1.readJsonFile)(path));
735
- result = { ...migrations, packageGroup, version: packageVersion };
736
- }
737
- catch {
738
- throw new Error(`Failed to find migrations file "${migrationsFilePath}" in package "${packageName}@${packageVersion}".`);
951
+ const fullTarballPath = (0, path_1.join)(dir, tarballPath);
952
+ let migrations;
953
+ try {
954
+ migrations = await (0, tar_1.extractFileFromTarball)(fullTarballPath, (0, path_2.joinPathFragments)('package', migrationsFilePath), (0, path_1.join)(dir, migrationsFilePath)).then((path) => (0, fileutils_1.readJsonFile)(path));
955
+ }
956
+ catch {
957
+ throw new Error(`Failed to find migrations file "${migrationsFilePath}" in package "${packageName}@${packageVersion}".`);
958
+ }
959
+ (0, prompt_files_1.validateMigrationEntries)(packageName, packageVersion, migrations);
960
+ const resolvedPromptFiles = await (0, prompt_files_1.extractPromptFilesFromTarball)(packageName, packageVersion, migrations, migrationsFilePath, fullTarballPath, dir);
961
+ result = {
962
+ ...migrations,
963
+ packageGroup,
964
+ version: packageVersion,
965
+ ...(resolvedPromptFiles ? { resolvedPromptFiles } : {}),
966
+ };
739
967
  }
740
968
  finally {
741
969
  await cleanup();
@@ -789,10 +1017,18 @@ async function getPackageMigrationsUsingInstallImpl(packageName, packageVersion)
789
1017
  });
790
1018
  const { migrations: migrationsFilePath, packageGroup, packageJson, } = readPackageMigrationConfig(packageName, dir);
791
1019
  let migrations = undefined;
1020
+ let resolvedPromptFiles;
792
1021
  if (migrationsFilePath) {
793
1022
  migrations = (0, fileutils_1.readJsonFile)(migrationsFilePath);
794
- }
795
- result = { ...migrations, packageGroup, version: packageJson.version };
1023
+ (0, prompt_files_1.validateMigrationEntries)(packageName, packageVersion, migrations);
1024
+ resolvedPromptFiles = await (0, prompt_files_1.readPromptFilesFromInstall)(packageName, packageVersion, migrations, migrationsFilePath);
1025
+ }
1026
+ result = {
1027
+ ...migrations,
1028
+ packageGroup,
1029
+ version: packageJson.version,
1030
+ ...(resolvedPromptFiles ? { resolvedPromptFiles } : {}),
1031
+ };
796
1032
  }
797
1033
  catch (e) {
798
1034
  const pmc = (0, package_manager_1.getPackageManagerCommand)((0, package_manager_1.detectPackageManager)(dir), dir);
@@ -836,12 +1072,13 @@ async function createMigrationsFile(root, migrations) {
836
1072
  async function updatePackageJson(root, updatedPackages) {
837
1073
  const packageJsonPath = (0, path_1.join)(root, 'package.json');
838
1074
  if (!(0, fs_1.existsSync)(packageJsonPath)) {
839
- return;
1075
+ return false;
840
1076
  }
841
1077
  const parseOptions = {};
842
1078
  const json = (0, fileutils_1.readJsonFile)(packageJsonPath, parseOptions);
843
1079
  const manager = (0, catalog_1.getCatalogManager)(root);
844
1080
  const catalogUpdates = [];
1081
+ let modified = false;
845
1082
  Object.keys(updatedPackages).forEach((p) => {
846
1083
  const existingVersion = json.dependencies?.[p] ?? json.devDependencies?.[p];
847
1084
  if (existingVersion && manager?.isCatalogReference(existingVersion)) {
@@ -856,28 +1093,40 @@ async function updatePackageJson(root, updatedPackages) {
856
1093
  }
857
1094
  // Update non-catalog packages in package.json
858
1095
  if (json.devDependencies?.[p]) {
859
- json.devDependencies[p] = updatedPackages[p].version;
1096
+ if (json.devDependencies[p] !== updatedPackages[p].version) {
1097
+ json.devDependencies[p] = updatedPackages[p].version;
1098
+ modified = true;
1099
+ }
860
1100
  return;
861
1101
  }
862
1102
  if (json.dependencies?.[p]) {
863
- json.dependencies[p] = updatedPackages[p].version;
1103
+ if (json.dependencies[p] !== updatedPackages[p].version) {
1104
+ json.dependencies[p] = updatedPackages[p].version;
1105
+ modified = true;
1106
+ }
864
1107
  return;
865
1108
  }
866
1109
  const dependencyType = updatedPackages[p].addToPackageJson;
867
1110
  if (typeof dependencyType === 'string') {
868
1111
  json[dependencyType] ??= {};
869
- json[dependencyType][p] = updatedPackages[p].version;
1112
+ if (json[dependencyType][p] !== updatedPackages[p].version) {
1113
+ json[dependencyType][p] = updatedPackages[p].version;
1114
+ modified = true;
1115
+ }
870
1116
  }
871
1117
  });
872
- await (0, write_formatted_json_file_1.writeFormattedJsonFile)(packageJsonPath, json, {
873
- appendNewLine: parseOptions.endsWithNewline,
874
- });
1118
+ if (modified) {
1119
+ await (0, write_formatted_json_file_1.writeFormattedJsonFile)(packageJsonPath, json, {
1120
+ appendNewLine: parseOptions.endsWithNewline,
1121
+ });
1122
+ }
875
1123
  // Update catalog definitions
876
1124
  if (catalogUpdates.length) {
877
1125
  // manager is guaranteed to be defined when there are catalog updates
878
1126
  manager.updateCatalogVersions(root, catalogUpdates);
879
1127
  await formatCatalogDefinitionFiles(manager, root);
880
1128
  }
1129
+ return modified || catalogUpdates.length > 0;
881
1130
  }
882
1131
  async function formatCatalogDefinitionFiles(manager, root) {
883
1132
  const catalogDefinitionFilePaths = manager.getCatalogDefinitionFilePaths();
@@ -899,29 +1148,38 @@ async function updateInstallationDetails(root, updatedPackages) {
899
1148
  const parseOptions = {};
900
1149
  const nxJson = (0, fileutils_1.readJsonFile)(nxJsonPath, parseOptions);
901
1150
  if (!nxJson.installation) {
902
- return;
1151
+ return false;
903
1152
  }
1153
+ let modified = false;
904
1154
  const nxVersion = updatedPackages.nx?.version;
905
- if (nxVersion) {
1155
+ if (nxVersion && nxJson.installation.version !== nxVersion) {
906
1156
  nxJson.installation.version = nxVersion;
1157
+ modified = true;
907
1158
  }
908
1159
  if (nxJson.installation.plugins) {
909
1160
  for (const dep in nxJson.installation.plugins) {
910
1161
  const update = updatedPackages[dep];
911
1162
  if (update) {
912
- nxJson.installation.plugins[dep] = (0, semver_1.valid)(update.version)
1163
+ const newVersion = (0, semver_1.valid)(update.version)
913
1164
  ? update.version
914
1165
  : await (0, package_manager_1.resolvePackageVersionUsingRegistry)(dep, update.version);
1166
+ if (nxJson.installation.plugins[dep] !== newVersion) {
1167
+ nxJson.installation.plugins[dep] = newVersion;
1168
+ modified = true;
1169
+ }
915
1170
  }
916
1171
  }
917
1172
  }
918
- await (0, write_formatted_json_file_1.writeFormattedJsonFile)(nxJsonPath, nxJson, {
919
- appendNewLine: parseOptions.endsWithNewline,
920
- });
1173
+ if (modified) {
1174
+ await (0, write_formatted_json_file_1.writeFormattedJsonFile)(nxJsonPath, nxJson, {
1175
+ appendNewLine: parseOptions.endsWithNewline,
1176
+ });
1177
+ }
1178
+ return modified;
921
1179
  }
922
1180
  async function isMigratingToNewMajor(from, to) {
923
- from = normalizeVersion(from);
924
- to = ['latest', 'next', 'canary'].includes(to) ? to : normalizeVersion(to);
1181
+ from = (0, version_utils_1.normalizeVersion)(from);
1182
+ to = ['latest', 'next', 'canary'].includes(to) ? to : (0, version_utils_1.normalizeVersion)(to);
925
1183
  if (!(0, semver_1.valid)(from)) {
926
1184
  from = await (0, package_manager_1.resolvePackageVersionUsingRegistry)('nx', from);
927
1185
  }
@@ -945,34 +1203,107 @@ async function generateMigrationsJsonAndUpdatePackageJson(root, opts) {
945
1203
  const originalNxJson = (0, configuration_1.readNxJson)();
946
1204
  const from = originalNxJson.installation?.version ??
947
1205
  readNxVersion(originalPackageJson, root);
1206
+ const mode = opts.mode;
1207
+ let walkedTargetPackage = opts.targetPackage;
1208
+ let fromOverrides = opts.from;
1209
+ let excludeApplied = opts.excludeAppliedMigrations;
1210
+ if (mode === 'third-party') {
1211
+ // For third-party, walk the canonical Nx target so cross-plugin
1212
+ // third-party dependencies (e.g. typescript managed by @nx/js but
1213
+ // used by @nx/angular) stay consistent.
1214
+ const canonical = resolveCanonicalNxPackage(opts.targetVersion);
1215
+ walkedTargetPackage = canonical;
1216
+ fromOverrides = { [canonical]: '0.0.0' };
1217
+ excludeApplied = true;
1218
+ }
948
1219
  logger_1.logger.info(`Fetching meta data about packages.`);
949
1220
  logger_1.logger.info(`It may take a few minutes.`);
1221
+ const fetch = createFetcher();
1222
+ let firstPartyPackages;
1223
+ if (mode === 'first-party' || mode === 'third-party') {
1224
+ // `@nx/workspace` is version-synced with `nx` and declares an
1225
+ // intentionally narrow `packageGroup` ({ nx, nx-cloud }) via its
1226
+ // `ng-update` field, whereas `nx` declares the full @nx/* plugin
1227
+ // fan-out. Their transitive first-party closures are equivalent.
1228
+ const sourcePackage = walkedTargetPackage === '@nx/workspace' ? 'nx' : walkedTargetPackage;
1229
+ const rootMetadata = await fetch(sourcePackage, opts.targetVersion);
1230
+ // Legacy `@nrwl/workspace<14` doesn't ship a complete `packageGroup`
1231
+ // in its metadata; the Migrator's cascade injects
1232
+ // `LEGACY_NRWL_PACKAGE_GROUP` for that case, and the post-build
1233
+ // third-party filter must mirror that set or first-party `@nrwl/*`
1234
+ // plugins slip past it.
1235
+ const packageGroup = sourcePackage === '@nrwl/workspace' && (0, version_utils_1.isLegacyEra)(opts.targetVersion)
1236
+ ? LEGACY_NRWL_PACKAGE_GROUP
1237
+ : rootMetadata.packageGroup;
1238
+ firstPartyPackages = resolveFirstPartyPackages(sourcePackage, packageGroup);
1239
+ }
1240
+ const installedPackageVersions = createInstalledPackageVersionsResolver(root);
950
1241
  const migrator = new Migrator({
951
1242
  packageJson: originalPackageJson,
952
1243
  nxInstallation: originalNxJson.installation,
953
- getInstalledPackageVersion: createInstalledPackageVersionsResolver(root),
954
- fetch: createFetcher(),
955
- from: opts.from,
1244
+ getInstalledPackageVersion: installedPackageVersions,
1245
+ fetch,
1246
+ from: fromOverrides,
956
1247
  to: opts.to,
957
1248
  interactive: opts.interactive && !(0, is_ci_1.isCI)(),
958
- excludeAppliedMigrations: opts.excludeAppliedMigrations,
1249
+ excludeAppliedMigrations: excludeApplied,
1250
+ mode,
1251
+ firstPartyPackages,
959
1252
  });
960
- const { migrations, packageUpdates, minVersionWithSkippedUpdates } = await migrator.migrate(opts.targetPackage, opts.targetVersion);
961
- await updatePackageJson(root, packageUpdates);
962
- await updateInstallationDetails(root, packageUpdates);
1253
+ const { migrations, packageUpdates, promptContents, minVersionWithSkippedUpdates, } = await migrator.migrate(walkedTargetPackage, opts.targetVersion);
1254
+ // The cascade collects packageJsonUpdates entries against the cascade
1255
+ // root's installed version, but inner per-package pins are only gated
1256
+ // against the in-flight cascade tally — not against each inner package's
1257
+ // installed version. A from-zero walk (e.g. `--mode=third-party`) can
1258
+ // surface a stale historical pin that would write a lower version than
1259
+ // the user already has. Drop those before writing; nx migrate is
1260
+ // forward-only, never a downgrade.
1261
+ const writableUpdates = (0, update_filters_1.filterDowngradedUpdates)(packageUpdates, originalPackageJson, installedPackageVersions);
1262
+ const wrotePackageJson = await updatePackageJson(root, writableUpdates);
1263
+ const wroteNxJsonInstallation = await updateInstallationDetails(root, writableUpdates);
1264
+ const promptMigrationFiles = (0, prompt_files_1.writePromptMigrationFiles)(root, migrations, promptContents ?? {}, packageUpdates[walkedTargetPackage].version);
963
1265
  if (migrations.length > 0) {
964
1266
  await createMigrationsFile(root, [
965
- ...addSplitConfigurationMigrationIfAvailable(from, packageUpdates),
1267
+ ...addSplitConfigurationMigrationIfAvailable(from, writableUpdates),
966
1268
  ...migrations,
967
1269
  ]);
968
1270
  }
1271
+ const modeLine = mode === 'first-party'
1272
+ ? `- Processed Nx first-party packages only (skipped third-party dependency bumps).`
1273
+ : mode === 'third-party'
1274
+ ? `- Processed third-party dependencies only (skipped Nx first-party package updates).`
1275
+ : null;
1276
+ const noChanges = !wrotePackageJson && !wroteNxJsonInstallation && migrations.length === 0;
1277
+ if (noChanges) {
1278
+ output_1.output.success({
1279
+ title: `No updates were applied.`,
1280
+ bodyLines: [
1281
+ ...(modeLine ? [modeLine] : []),
1282
+ mode === 'third-party'
1283
+ ? `- No third-party dependency bumps were found for the installed Nx version. Either your dependencies are already up to date, or this workspace doesn't manage them in a place 'nx migrate' writes to (e.g. non-JS workspaces only track Nx and its plugins).`
1284
+ : `- No package updates or migrations were found.`,
1285
+ ],
1286
+ });
1287
+ // Nothing was applied; skip the "Next steps" guidance below — it would
1288
+ // tell the user to inspect package.json changes that don't exist.
1289
+ return;
1290
+ }
969
1291
  output_1.output.success({
970
1292
  title: `The migrate command has run successfully.`,
971
1293
  bodyLines: [
972
- `- package.json has been updated.`,
1294
+ ...(modeLine ? [modeLine] : []),
1295
+ ...(wrotePackageJson ? [`- package.json has been updated.`] : []),
1296
+ ...(wroteNxJsonInstallation
1297
+ ? [`- nx.json (installation) has been updated.`]
1298
+ : []),
973
1299
  migrations.length > 0
974
1300
  ? `- migrations.json has been generated.`
975
1301
  : `- There are no migrations to run, so migrations.json has not been created.`,
1302
+ ...(promptMigrationFiles.length > 0
1303
+ ? [
1304
+ `- ${promptMigrationFiles.length} AI migration prompt(s) have been written to ${prompt_files_1.AI_MIGRATIONS_DIR}/.`,
1305
+ ]
1306
+ : []),
976
1307
  ],
977
1308
  });
978
1309
  try {
@@ -1006,9 +1337,21 @@ async function generateMigrationsJsonAndUpdatePackageJson(root, opts) {
1006
1337
  ]
1007
1338
  : [
1008
1339
  `- Make sure package.json changes make sense and then run '${pmc.install}',`,
1340
+ ...(promptMigrationFiles.length > 0
1341
+ ? [
1342
+ `- Review and tweak the AI migration prompts in ${prompt_files_1.AI_MIGRATIONS_DIR}/ as needed.`,
1343
+ ]
1344
+ : []),
1009
1345
  ...(migrations.length > 0
1010
1346
  ? [`- Run '${pmc.exec} nx migrate --run-migrations'`]
1011
1347
  : []),
1348
+ ...(opts.originalTargetVersion
1349
+ ? [
1350
+ `- After applying these migrations, run '${pmc.exec} nx migrate ${opts.targetPackage}@${opts.originalTargetVersion} --mode=${opts.mode}${opts.multiMajorMode === 'gradual'
1351
+ ? ` ${multi_major_1.MULTI_MAJOR_MODE_FLAG}=gradual`
1352
+ : ''}' to continue toward your original target.`,
1353
+ ]
1354
+ : []),
1012
1355
  ...(opts.interactive && minVersionWithSkippedUpdates
1013
1356
  ? [
1014
1357
  `- You opted out of some migrations for now. Write the following command down somewhere to apply these migrations later:`,
@@ -1040,7 +1383,7 @@ function addSplitConfigurationMigrationIfAvailable(from, packageJson) {
1040
1383
  if (!packageJson['@nrwl/workspace'])
1041
1384
  return [];
1042
1385
  if ((0, semver_1.gte)(packageJson['@nrwl/workspace'].version, '15.7.0-beta.0') &&
1043
- (0, semver_1.lt)(normalizeVersion(from), '15.7.0-beta.0')) {
1386
+ (0, semver_1.lt)((0, version_utils_1.normalizeVersion)(from), '15.7.0-beta.0')) {
1044
1387
  return [
1045
1388
  {
1046
1389
  version: '15.7.0-beta.0',
@@ -1177,10 +1520,90 @@ function logNpmPeerDepsError(phase) {
1177
1520
  });
1178
1521
  }
1179
1522
  }
1180
- async function executeMigrations(root, migrations, isVerbose, shouldCreateCommits, commitPrefix, shouldSkipInstall = false) {
1523
+ function resolveAgenticRunId(migrations) {
1524
+ return (0, semver_1.rsort)(migrations.map((m) => (0, version_utils_1.normalizeVersion)(m.version)))[0];
1525
+ }
1526
+ function formatSkippedPromptsNextStep(skipped) {
1527
+ return [
1528
+ 'Some prompt migrations were skipped. Review and apply each of the following prompt files to the workspace, in the listed order:',
1529
+ ...skipped.map((m) => ` - ${m.prompt}`),
1530
+ ].join('\n');
1531
+ }
1532
+ /**
1533
+ * Resolves the effective `--create-commits` state once the agentic flow has
1534
+ * been resolved. The agent's outer prompt only embeds the impl-phase file list
1535
+ * when per-migration commits isolate each migration's diff, so the diff-context
1536
+ * flag returned here gates that section.
1537
+ */
1538
+ function resolveCreateCommits(args) {
1539
+ const { createCommits, agenticKind, isGitRepo, commitPrefixIsCustom } = args;
1540
+ // Explicit `--create-commits` without git is a hard error — the user asked
1541
+ // for something we cannot deliver.
1542
+ if (createCommits === true && !isGitRepo) {
1543
+ return {
1544
+ effective: false,
1545
+ agenticHasDiffContext: false,
1546
+ error: '`--create-commits` requires a git repository. Run `git init` first, or omit the flag.',
1547
+ };
1548
+ }
1549
+ if (agenticKind === 'enabled') {
1550
+ if (createCommits === false) {
1551
+ return {
1552
+ effective: false,
1553
+ agenticHasDiffContext: false,
1554
+ warning: "--no-create-commits was passed alongside --agentic. Without per-migration commits, the agent can't isolate the current migration's changes from earlier migrations in this run. Drop --no-create-commits for accurate per-migration review." +
1555
+ (commitPrefixIsCustom
1556
+ ? ' Note: the custom --commit-prefix value will have no effect because commits are disabled.'
1557
+ : ''),
1558
+ };
1559
+ }
1560
+ // Without git we cannot soft-force commits the user didn't explicitly
1561
+ // opt into. Degrade rather than error: continue the agentic run, but
1562
+ // without per-file diff context (which depends on per-migration commits).
1563
+ if (!isGitRepo) {
1564
+ return {
1565
+ effective: false,
1566
+ agenticHasDiffContext: false,
1567
+ warning: '`--agentic` enables per-migration commits by default, but the workspace is not a git repository. Continuing without commits — the agent will not receive per-file diff context. Run `git init` to enable.' +
1568
+ (commitPrefixIsCustom
1569
+ ? ' The custom --commit-prefix value will have no effect.'
1570
+ : ''),
1571
+ };
1572
+ }
1573
+ return { effective: true, agenticHasDiffContext: true };
1574
+ }
1575
+ return {
1576
+ effective: createCommits === true,
1577
+ agenticHasDiffContext: false,
1578
+ };
1579
+ }
1580
+ /**
1581
+ * Resolves whether the framework-owned generic-validation agent step should run
1582
+ * after generator-only migrations.
1583
+ *
1584
+ * Default-on when the agentic flow resolved to `enabled`; silently ignored
1585
+ * otherwise (no warning emitted) — `--validate` requires an active agent
1586
+ * session by definition. An explicit `--no-validate` (`validate === false`)
1587
+ * opts out even when agentic is enabled.
1588
+ */
1589
+ function resolveShouldRunValidation(args) {
1590
+ return args.validate !== false && args.agenticKind === 'enabled';
1591
+ }
1592
+ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommits, commitPrefix, shouldSkipInstall = false, agentic, agenticHasDiffContext = false, shouldRunValidation = false) {
1181
1593
  const changedDepInstaller = new ChangedDepInstaller(root, shouldSkipInstall);
1182
1594
  const migrationsWithNoChanges = [];
1183
1595
  const sortedMigrations = migrations.sort((a, b) => {
1596
+ // Under `--agentic`, hoist the v23 migration that ignores
1597
+ // `.nx/migrate-runs` to position 0 so its .gitignore update lands
1598
+ // before any per-migration commit absorbs the run's handoff scratch.
1599
+ // See `agentic/handoff-gitignore.ts` for the full rationale and the
1600
+ // inline-fallback path that covers intra-pre-v23 agentic runs.
1601
+ if (agentic?.kind === 'enabled') {
1602
+ if ((0, handoff_gitignore_1.isHandoffGitignoreMigration)(a))
1603
+ return -1;
1604
+ if ((0, handoff_gitignore_1.isHandoffGitignoreMigration)(b))
1605
+ return 1;
1606
+ }
1184
1607
  // special case for the split configuration migration to run first
1185
1608
  if (a.name === '15-7-0-split-configuration-into-project-json-files') {
1186
1609
  return -1;
@@ -1188,40 +1611,319 @@ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommit
1188
1611
  if (b.name === '15-7-0-split-configuration-into-project-json-files') {
1189
1612
  return 1;
1190
1613
  }
1191
- return (0, semver_1.lt)(normalizeVersion(a.version), normalizeVersion(b.version))
1614
+ return (0, semver_1.lt)((0, version_utils_1.normalizeVersion)(a.version), (0, version_utils_1.normalizeVersion)(b.version))
1192
1615
  ? -1
1193
1616
  : 1;
1194
1617
  });
1618
+ // Lazy-load the agentic chain so non-agentic runs don't pay its startup cost.
1619
+ let agenticRun;
1620
+ if (agentic?.kind === 'enabled' && sortedMigrations.length > 0) {
1621
+ const { initRunDir } = require('./agentic/handoff');
1622
+ const { runAgenticPromptStep } = require('./agentic/run-step');
1623
+ agenticRun = {
1624
+ agentic,
1625
+ runDir: initRunDir(root, resolveAgenticRunId(sortedMigrations)),
1626
+ runStep: runAgenticPromptStep,
1627
+ };
1628
+ }
1629
+ const printDroppedAgentContext = agentic?.kind === 'inside-agent'
1630
+ ? require('./agentic/print-dropped-agent-context').printDroppedAgentContextForOuterAgent
1631
+ : undefined;
1195
1632
  logger_1.logger.info(`Running the following migrations:`);
1196
- sortedMigrations.forEach((m) => logger_1.logger.info(`- ${m.package}: ${m.name} (${m.description})`));
1197
- logger_1.logger.info(`---------------------------------------------------------\n`);
1198
- const allNextSteps = [];
1633
+ sortedMigrations.forEach((m) => logger_1.logger.info(m.description
1634
+ ? `- ${m.package}: ${m.name} — ${m.description}`
1635
+ : `- ${m.package}: ${m.name}`));
1636
+ logger_1.logger.info('');
1637
+ // Tracked separately from `skippedPrompts` so the end-of-run logic can
1638
+ // render them distinctly per resolution mode.
1639
+ const migrationEmittedNextSteps = [];
1640
+ const skippedPrompts = [];
1641
+ // One record per migration the loop touched. `status: 'completed'` records
1642
+ // are pushed at the end of each successful iteration; `status: 'aborted'`
1643
+ // is pushed by the catch block when a migration throws mid-iteration, so
1644
+ // `outcomes` is the single source of truth for the recap and tally — no
1645
+ // parallel "pending" list. `outcomes.length` always equals `migrationIndex`
1646
+ // after the loop body runs.
1647
+ const outcomes = [];
1648
+ // Prompt-only migrations whose agent never ran. Hybrid migrations with a
1649
+ // skipped prompt are NOT counted here — their deterministic half still ran.
1650
+ let notRunMigrationsCount = 0;
1651
+ const skipReason = agentic?.kind === 'inside-agent'
1652
+ ? 'deferred to the AI agent driving this run'
1653
+ : 'agentic flow disabled';
1654
+ const installDepsIfChanged = () => changedDepInstaller.installDepsIfChanged();
1655
+ // Returns the migrations whose own commits failed and whose diffs are
1656
+ // still sitting in the working tree — derived live from `outcomes`. The
1657
+ // next successful commit absorbs them via `git add -A`; its commit body
1658
+ // lists them so a reader of `git log -p` can see which prior migrations'
1659
+ // diffs got pulled in.
1660
+ const pendingForCommitBody = () => outcomes
1661
+ .filter((o) => o.commit.kind === 'failed')
1662
+ .map((o) => ({ package: o.migration.package, name: o.migration.name }));
1663
+ // True while at least one prior migration's commit has failed and its
1664
+ // diff hasn't been absorbed yet. While true, the working tree carries
1665
+ // prior-migration state, so the `hasDiffContext` flag in the hybrid-
1666
+ // agentic and validation-agentic prompt branches is suppressed (the
1667
+ // prompt-only-with-agentic branch doesn't use `hasDiffContext`).
1668
+ const hasPendingCommitDebt = () => outcomes.some((o) => o.commit.kind === 'failed');
1669
+ // Single funnel for per-migration commit attempts. Returns the
1670
+ // `CommitState` to record on the migration's outcome. On `committed`,
1671
+ // back-annotates any prior failed-commit outcomes to `kind: 'absorbed'`
1672
+ // (their diffs were just rolled into this commit via `git add -A`).
1673
+ async function attemptMigrationCommit(m) {
1674
+ const pending = pendingForCommitBody();
1675
+ const result = await (0, migrate_commits_1.commitMigrationIfRequested)(root, m, shouldCreateCommits, commitPrefix, installDepsIfChanged, pending);
1676
+ if (result.status === 'committed') {
1677
+ // This commit absorbed every pending failed-commit migration's diff.
1678
+ // Transition their `commit.kind: 'failed'` records to `'absorbed'` so
1679
+ // the failure recap (if a later migration throws) can anchor them
1680
+ // and the retained-state filter no longer counts them as
1681
+ // uncommitted.
1682
+ //
1683
+ // The key is `package:name`; matching on `name` alone would conflate
1684
+ // across packages. Guard the kind check so a subsequent absorption-
1685
+ // of-same-name cannot overwrite an earlier annotation.
1686
+ if (pending.length > 0) {
1687
+ const absorbedKeys = new Set(pending.map((p) => `${p.package}:${p.name}`));
1688
+ for (const o of outcomes) {
1689
+ const key = `${o.migration.package}:${o.migration.name}`;
1690
+ if (absorbedKeys.has(key) && o.commit.kind === 'failed') {
1691
+ o.commit = {
1692
+ kind: 'absorbed',
1693
+ into: { name: m.name, sha: result.sha },
1694
+ };
1695
+ }
1696
+ }
1697
+ }
1698
+ return { kind: 'landed', sha: result.sha };
1699
+ }
1700
+ if (result.status === 'failed') {
1701
+ // Diff is still in WT. Subsequent prompts cannot claim git-isolation
1702
+ // until a later commit absorbs the backlog.
1703
+ return { kind: 'failed' };
1704
+ }
1705
+ // `no-changes` and `disabled` — no commit attempted, nothing to record
1706
+ // as a commit failure.
1707
+ return { kind: 'none' };
1708
+ }
1709
+ const totalMigrations = sortedMigrations.length;
1710
+ let migrationIndex = 0;
1199
1711
  for (const m of sortedMigrations) {
1200
- logger_1.logger.info(`Running migration ${m.package}: ${m.name}`);
1712
+ migrationIndex++;
1713
+ (0, migrate_output_1.logMigrationBoundary)(migrationIndex, totalMigrations, m.package, m.name);
1714
+ // Snapshot the WT for before/after comparison in the catch block.
1715
+ // Content-sensitive so a dirty→dirty case (this migration mutating an
1716
+ // already-dirty shared file like `package.json`) doesn't collapse.
1717
+ const baselineWorkingTreeSnapshot = (0, git_utils_1.getUncommittedChangesSnapshot)(root);
1201
1718
  try {
1202
- const { changes, nextSteps } = await runNxOrAngularMigration(root, m, isVerbose, shouldCreateCommits, commitPrefix, () => changedDepInstaller.installDepsIfChanged());
1203
- allNextSteps.push(...nextSteps);
1204
- if (changes.length === 0) {
1205
- migrationsWithNoChanges.push(m);
1719
+ let outcome;
1720
+ let commit = { kind: 'none' };
1721
+ if ((0, migration_shape_1.isPromptOnlyMigration)(m)) {
1722
+ if (agenticRun) {
1723
+ const stepResult = await agenticRun.runStep({
1724
+ root,
1725
+ migration: m,
1726
+ agentic: agenticRun.agentic,
1727
+ runDir: agenticRun.runDir,
1728
+ installDepsIfChanged,
1729
+ });
1730
+ commit = await attemptMigrationCommit(m);
1731
+ (0, migrate_output_1.logAgenticSuccessOutcome)(stepResult.ambiguous ? 'Marked complete by user' : 'Applied', commit.kind === 'landed' ? commit.sha : null, stepResult.summary);
1732
+ outcome = 'applied';
1733
+ }
1734
+ else {
1735
+ logger_1.logger.info(pc.dim(`↷ Skipped — ${skipReason}. Listed in next steps.`));
1736
+ skippedPrompts.push(m);
1737
+ notRunMigrationsCount++;
1738
+ outcome = 'deferred';
1739
+ }
1740
+ }
1741
+ else if ((0, migration_shape_1.isHybridMigration)(m)) {
1742
+ const { changes, nextSteps, agentContext, logs, madeChanges } = await runNxOrAngularMigration(root, m, isVerbose,
1743
+ /* captureGeneratorOutput: */ !!agenticRun);
1744
+ migrationEmittedNextSteps.push(...nextSteps);
1745
+ if (agenticRun) {
1746
+ // Install any deps the deterministic phase added/bumped before the
1747
+ // agent runs — the prompt half may depend on them being present in
1748
+ // node_modules.
1749
+ await installDepsIfChanged();
1750
+ const stepResult = await agenticRun.runStep({
1751
+ root,
1752
+ migration: m,
1753
+ agentic: agenticRun.agentic,
1754
+ runDir: agenticRun.runDir,
1755
+ installDepsIfChanged,
1756
+ implContext: {
1757
+ logs,
1758
+ changes,
1759
+ agentContext,
1760
+ // When prior commits failed, the working tree carries their
1761
+ // diff. The git-inspect path of the prompt would mislead the
1762
+ // agent in that case; fall back to embedded `<files_changed>`.
1763
+ hasDiffContext: agenticHasDiffContext && !hasPendingCommitDebt(),
1764
+ },
1765
+ });
1766
+ commit = await attemptMigrationCommit(m);
1767
+ (0, migrate_output_1.logAgenticSuccessOutcome)(stepResult.ambiguous ? 'Marked complete by user' : 'Applied', commit.kind === 'landed' ? commit.sha : null, stepResult.summary);
1768
+ outcome = 'applied';
1769
+ }
1770
+ else {
1771
+ // The inner prompt step doesn't run here (agentic disabled, or
1772
+ // running inside an outer agent). Under `inside-agent`, surface the
1773
+ // generator-emitted `agentContext` to stdout so the outer driving
1774
+ // agent can ingest it. Under `disabled` the run is human-driven;
1775
+ // agent-targeted context would only add noise — drop.
1776
+ logger_1.logger.info(pc.dim(`↷ Prompt phase skipped — ${skipReason}. Listed in next steps.`));
1777
+ if (printDroppedAgentContext && agentContext.length > 0) {
1778
+ printDroppedAgentContext({ migration: m, agentContext });
1779
+ }
1780
+ skippedPrompts.push(m);
1781
+ if (!madeChanges) {
1782
+ migrationsWithNoChanges.push(m);
1783
+ }
1784
+ // Only attempt a commit when this migration's deterministic
1785
+ // phase actually produced changes. Otherwise the absorbing
1786
+ // `git add -A` would build a commit subject naming this no-op
1787
+ // migration even though its content is entirely prior pending
1788
+ // diffs — confusing `git log` / `git blame` attribution. Pending
1789
+ // stays pending and the next change-producing migration absorbs.
1790
+ if (madeChanges) {
1791
+ commit = await attemptMigrationCommit(m);
1792
+ }
1793
+ if (commit.kind === 'landed' && commit.sha) {
1794
+ logger_1.logger.info(pc.dim(`Committed as ${commit.sha}`));
1795
+ }
1796
+ outcome = 'deferred';
1797
+ }
1206
1798
  }
1207
- logger_1.logger.info(`---------------------------------------------------------`);
1799
+ else {
1800
+ // Defer commit until validation succeeds; failed validation leaves
1801
+ // changes uncommitted in the working tree for the user to review.
1802
+ const validationRun = agenticRun && shouldRunValidation ? agenticRun : undefined;
1803
+ const { changes, nextSteps, agentContext, logs, madeChanges } = await runNxOrAngularMigration(root, m, isVerbose,
1804
+ /* captureGeneratorOutput: */ !!validationRun);
1805
+ migrationEmittedNextSteps.push(...nextSteps);
1806
+ const canRunValidation = !!validationRun && changes.length > 0;
1807
+ if (canRunValidation) {
1808
+ // Install any deps the deterministic phase added/bumped before the
1809
+ // validation agent runs — the agent may run tasks that need them.
1810
+ await installDepsIfChanged();
1811
+ const stepResult = await validationRun.runStep({
1812
+ root,
1813
+ migration: m,
1814
+ agentic: validationRun.agentic,
1815
+ runDir: validationRun.runDir,
1816
+ installDepsIfChanged,
1817
+ implContext: {
1818
+ logs,
1819
+ changes,
1820
+ agentContext,
1821
+ // See the hybrid agentic branch above for the rationale on
1822
+ // why pending commit debt gates git-inspect context.
1823
+ hasDiffContext: agenticHasDiffContext && !hasPendingCommitDebt(),
1824
+ },
1825
+ mode: 'generic-validation',
1826
+ });
1827
+ commit = await attemptMigrationCommit(m);
1828
+ (0, migrate_output_1.logAgenticSuccessOutcome)(stepResult.ambiguous
1829
+ ? 'Marked complete by user'
1830
+ : 'Validation passed', commit.kind === 'landed' ? commit.sha : null, stepResult.summary);
1831
+ outcome = 'applied';
1832
+ }
1833
+ else {
1834
+ // Inner validation step didn't run. Surface `agentContext` under
1835
+ // `inside-agent` so the outer driving agent can ingest it.
1836
+ if (printDroppedAgentContext && agentContext.length > 0) {
1837
+ printDroppedAgentContext({ migration: m, agentContext });
1838
+ }
1839
+ if (!madeChanges) {
1840
+ migrationsWithNoChanges.push(m);
1841
+ outcome = 'no-changes';
1842
+ }
1843
+ else {
1844
+ commit = await attemptMigrationCommit(m);
1845
+ if (commit.kind === 'landed' && commit.sha) {
1846
+ logger_1.logger.info(pc.dim(`Committed as ${commit.sha}`));
1847
+ }
1848
+ outcome = 'applied';
1849
+ }
1850
+ }
1851
+ }
1852
+ outcomes.push({
1853
+ migration: { package: m.package, name: m.name },
1854
+ status: 'completed',
1855
+ kind: outcome,
1856
+ commit,
1857
+ });
1858
+ logger_1.logger.info('');
1208
1859
  }
1209
1860
  catch (e) {
1861
+ // Record the in-flight migration as `aborted` so the recap and tally
1862
+ // see it. `commit: 'failed'` requires both: (1) commits were
1863
+ // requested — otherwise the "could not be created" recap line is
1864
+ // false; (2) the WT snapshot diverged from the iteration baseline —
1865
+ // net-new state, not the pre-existing pending diff. Else `'none'`.
1866
+ const leftNewDiff = (0, git_utils_1.getUncommittedChangesSnapshot)(root) !== baselineWorkingTreeSnapshot;
1867
+ outcomes.push({
1868
+ migration: { package: m.package, name: m.name },
1869
+ status: 'aborted',
1870
+ commit: shouldCreateCommits && leftNewDiff
1871
+ ? { kind: 'failed' }
1872
+ : { kind: 'none' },
1873
+ });
1210
1874
  if (!(e instanceof NpmPeerDepsInstallError)) {
1875
+ // `withGeneratorOutputCapture` attaches the generator's `console.*`
1876
+ // output as `capturedLogs` (best-effort; may be absent). Surface it
1877
+ // so the user sees what the generator printed before it crashed.
1878
+ const capturedLogs = e?.capturedLogs;
1879
+ const bodyLines = typeof capturedLogs === 'string' && capturedLogs.length > 0
1880
+ ? [
1881
+ 'Output from the generator before it failed:',
1882
+ '',
1883
+ ...capturedLogs.split('\n'),
1884
+ ]
1885
+ : undefined;
1211
1886
  output_1.output.error({
1212
1887
  title: `Failed to run ${m.name} from ${m.package}. This workspace is NOT up to date!`,
1888
+ bodyLines,
1889
+ });
1890
+ (0, migrate_output_1.logFailureRecap)({
1891
+ migrationIndex,
1892
+ totalMigrations,
1893
+ outcomes,
1894
+ migrationEmittedNextSteps,
1895
+ insideAgent: agentic?.kind === 'inside-agent',
1213
1896
  });
1214
1897
  }
1215
1898
  throw e;
1216
1899
  }
1217
1900
  }
1218
1901
  if (!shouldCreateCommits) {
1219
- await changedDepInstaller.installDepsIfChanged();
1902
+ await installDepsIfChanged();
1220
1903
  }
1221
1904
  if (changedDepInstaller.skippedInstall) {
1222
1905
  logSkippedPostMigrationInstall(root);
1223
1906
  }
1224
- return { migrationsWithNoChanges, nextSteps: allNextSteps };
1907
+ // Combined-view next-steps array kept for back-compat with repair.ts, which
1908
+ // consumes the single `nextSteps` field.
1909
+ const combinedNextSteps = [...migrationEmittedNextSteps];
1910
+ if (skippedPrompts.length > 0) {
1911
+ combinedNextSteps.push(formatSkippedPromptsNextStep(skippedPrompts));
1912
+ }
1913
+ return {
1914
+ migrationsWithNoChanges,
1915
+ skippedPromptsCount: skippedPrompts.length,
1916
+ notRunMigrationsCount,
1917
+ nextSteps: combinedNextSteps,
1918
+ skippedPrompts,
1919
+ migrationEmittedNextSteps,
1920
+ committedShasCount: (0, migrate_output_1.countLandedCommits)(outcomes),
1921
+ // Migrations whose commits failed and never got absorbed by a later
1922
+ // commit. The caller surfaces them so a successful run doesn't claim
1923
+ // "up to date" while leaving uncommitted diffs in the working tree.
1924
+ // Formatted as `package: name` for direct display.
1925
+ retainedAtSuccess: (0, migrate_output_1.retainedMigrations)(outcomes).map((p) => `${p.package}: ${p.name}`),
1926
+ };
1225
1927
  }
1226
1928
  class ChangedDepInstaller {
1227
1929
  constructor(root, shouldSkipInstall = false) {
@@ -1246,6 +1948,7 @@ class ChangedDepInstaller {
1246
1948
  this.initialDeps = currentDeps;
1247
1949
  }
1248
1950
  }
1951
+ exports.ChangedDepInstaller = ChangedDepInstaller;
1249
1952
  function logSkippedPostMigrationInstall(root) {
1250
1953
  const packageManager = (0, package_manager_1.detectPackageManager)(root);
1251
1954
  const installCommand = (0, package_manager_1.getPackageManagerCommand)(packageManager, root).install;
@@ -1254,21 +1957,28 @@ function logSkippedPostMigrationInstall(root) {
1254
1957
  bodyLines: [`Run "${installCommand}" to install the updated dependencies.`],
1255
1958
  });
1256
1959
  }
1257
- async function runNxOrAngularMigration(root, migration, isVerbose, shouldCreateCommits, commitPrefix, installDepsIfChanged, handleInstallDeps = false) {
1258
- if (!installDepsIfChanged) {
1259
- const changedDepInstaller = new ChangedDepInstaller(root);
1260
- installDepsIfChanged = () => changedDepInstaller.installDepsIfChanged();
1261
- }
1960
+ async function runNxOrAngularMigration(root, migration, isVerbose, captureGeneratorOutput = false) {
1262
1961
  const { collection, collectionPath } = readMigrationCollection(migration.package, root);
1263
1962
  let changes = [];
1264
1963
  let nextSteps = [];
1964
+ let agentContext = [];
1965
+ let logs = '';
1966
+ // Angular's `ngResult.changes` is synthesized from the schematic's
1967
+ // DryRunEvent stream so Nx and Angular paths can share commit/validation
1968
+ // gating via `changes.length > 0`.
1969
+ let madeChanges = false;
1970
+ logger_1.logger.info(pc.dim('→ Running generator…'));
1265
1971
  if (!isAngularMigration(collection, migration.name)) {
1266
- ({ nextSteps, changes } = await runNxMigration(root, collectionPath, collection, migration.name, migration.version));
1972
+ ({ nextSteps, changes, agentContext, logs } = await runNxMigration(root, collectionPath, collection, migration.name, migration.version, captureGeneratorOutput));
1973
+ madeChanges = changes.length > 0;
1267
1974
  logger_1.logger.info(`Ran ${migration.name} from ${migration.package}`);
1268
- logger_1.logger.info(` ${migration.description}\n`);
1269
- if (changes.length < 1) {
1975
+ if (migration.description) {
1976
+ logger_1.logger.info(` ${migration.description}`);
1977
+ }
1978
+ logger_1.logger.info('');
1979
+ if (!madeChanges) {
1270
1980
  logger_1.logger.info(`No changes were made\n`);
1271
- return { changes, nextSteps };
1981
+ return { changes, nextSteps, agentContext, logs, madeChanges };
1272
1982
  }
1273
1983
  logger_1.logger.info('Changes:');
1274
1984
  (0, tree_1.printChanges)(changes, ' ');
@@ -1277,40 +1987,26 @@ async function runNxOrAngularMigration(root, migration, isVerbose, shouldCreateC
1277
1987
  else {
1278
1988
  const ngCliAdapter = await getNgCompatLayer();
1279
1989
  const migrationProjectGraph = await (0, project_graph_1.createProjectGraphAsync)();
1280
- const { madeChanges, loggingQueue } = await ngCliAdapter.runMigration(root, migration.package, migration.name, (0, project_graph_1.readProjectsConfigurationFromProjectGraph)(migrationProjectGraph).projects, isVerbose, migrationProjectGraph);
1990
+ const ngResult = await ngCliAdapter.runMigration(root, migration.package, migration.name, (0, project_graph_1.readProjectsConfigurationFromProjectGraph)(migrationProjectGraph).projects, isVerbose, migrationProjectGraph);
1991
+ changes = ngResult.changes;
1992
+ madeChanges = ngResult.madeChanges;
1993
+ logs = ngResult.loggingQueue.join('\n');
1281
1994
  logger_1.logger.info(`Ran ${migration.name} from ${migration.package}`);
1282
- logger_1.logger.info(` ${migration.description}\n`);
1995
+ if (migration.description) {
1996
+ logger_1.logger.info(` ${migration.description}`);
1997
+ }
1998
+ logger_1.logger.info('');
1283
1999
  if (!madeChanges) {
1284
2000
  logger_1.logger.info(`No changes were made\n`);
1285
- return { changes, nextSteps };
2001
+ return { changes, nextSteps, agentContext, logs, madeChanges };
1286
2002
  }
1287
2003
  logger_1.logger.info('Changes:');
1288
- loggingQueue.forEach((log) => logger_1.logger.info(' ' + log));
2004
+ ngResult.loggingQueue.forEach((log) => logger_1.logger.info(' ' + log));
1289
2005
  logger_1.logger.info('');
1290
2006
  }
1291
- if (shouldCreateCommits) {
1292
- await installDepsIfChanged();
1293
- const commitMessage = `${commitPrefix}${migration.name}`;
1294
- try {
1295
- const committedSha = (0, git_utils_1.commitChanges)(commitMessage, root);
1296
- if (committedSha) {
1297
- logger_1.logger.info(pc.dim(`- Commit created for changes: ${committedSha}`));
1298
- }
1299
- else {
1300
- logger_1.logger.info(pc.red(`- A commit could not be created/retrieved for an unknown reason`));
1301
- }
1302
- }
1303
- catch (e) {
1304
- logger_1.logger.info(pc.red(`- ${e.message}`));
1305
- }
1306
- // if we are running this function alone, we need to install deps internally
1307
- }
1308
- else if (handleInstallDeps) {
1309
- await installDepsIfChanged();
1310
- }
1311
- return { changes, nextSteps };
2007
+ return { changes, nextSteps, agentContext, logs, madeChanges };
1312
2008
  }
1313
- async function runMigrations(root, opts, args, isVerbose, shouldCreateCommits = false, commitPrefix, shouldSkipInstall = false) {
2009
+ async function runMigrations(root, opts, args, isVerbose, shouldCreateCommits, commitPrefix, shouldSkipInstall = false) {
1314
2010
  if (!shouldSkipInstall && !process.env.NX_MIGRATE_SKIP_INSTALL) {
1315
2011
  await runInstall();
1316
2012
  }
@@ -1340,26 +2036,130 @@ async function runMigrations(root, opts, args, isVerbose, shouldCreateCommits =
1340
2036
  else if (!opts.ifExists && !migrationsExists) {
1341
2037
  throw new Error(`File '${opts.runMigrations}' doesn't exist, can't run migrations. Use flag --if-exists to run migrations only if the file exists`);
1342
2038
  }
2039
+ const migrations = (0, fileutils_1.readJsonFile)((0, path_1.join)(root, opts.runMigrations)).migrations;
2040
+ const { resolveAgentic } = require('./agentic/select');
2041
+ const agentic = await resolveAgentic({
2042
+ agentic: opts.agentic,
2043
+ migrations,
2044
+ });
2045
+ const { effective: effectiveCreateCommits, agenticHasDiffContext, warning: createCommitsWarning, error: createCommitsError, } = resolveCreateCommits({
2046
+ createCommits: shouldCreateCommits,
2047
+ agenticKind: agentic.kind,
2048
+ isGitRepo: (0, git_utils_1.isGitRepository)(root),
2049
+ commitPrefixIsCustom: commitPrefix !== command_object_1.DEFAULT_MIGRATION_COMMIT_PREFIX,
2050
+ });
2051
+ if (createCommitsError) {
2052
+ throw new Error(createCommitsError);
2053
+ }
2054
+ if (createCommitsWarning) {
2055
+ output_1.output.warn({ title: createCommitsWarning });
2056
+ }
2057
+ const shouldRunValidation = resolveShouldRunValidation({
2058
+ validate: opts.validate,
2059
+ agenticKind: agentic.kind,
2060
+ });
1343
2061
  output_1.output.log({
1344
2062
  title: `Running migrations from '${opts.runMigrations}'` +
1345
- (shouldCreateCommits ? ', with each applied in a dedicated commit' : ''),
2063
+ (effectiveCreateCommits
2064
+ ? ', with each applied in a dedicated commit'
2065
+ : ''),
1346
2066
  });
1347
- const migrations = (0, fileutils_1.readJsonFile)((0, path_1.join)(root, opts.runMigrations)).migrations;
1348
- const { migrationsWithNoChanges, nextSteps } = await executeMigrations(root, migrations, isVerbose, shouldCreateCommits, commitPrefix, shouldSkipInstall);
1349
- if (migrationsWithNoChanges.length < migrations.length) {
1350
- output_1.output.success({
1351
- title: `Successfully finished running migrations from '${opts.runMigrations}'. This workspace is up to date!`,
2067
+ if (effectiveCreateCommits) {
2068
+ (0, migrate_commits_1.commitCheckpointBeforeMigrations)(root, commitPrefix);
2069
+ }
2070
+ if (agentic.kind === 'enabled') {
2071
+ const { packageJson: nxPackageJson } = (0, package_json_1.readModulePackageJson)('nx', (0, installation_directory_1.getNxRequirePaths)(root));
2072
+ await (0, handoff_gitignore_1.applyAgenticHandoffGitignoreFallback)({
2073
+ migrations,
2074
+ installedNxVersion: nxPackageJson.version,
2075
+ effectiveCreateCommits,
2076
+ commitPrefix,
2077
+ root,
2078
+ });
2079
+ }
2080
+ const { migrationsWithNoChanges, skippedPromptsCount, notRunMigrationsCount, skippedPrompts, migrationEmittedNextSteps, committedShasCount, retainedAtSuccess, } = await executeMigrations(root, migrations, isVerbose, effectiveCreateCommits, commitPrefix, shouldSkipInstall, agentic, agenticHasDiffContext, shouldRunValidation);
2081
+ const ranWithChangesCount = migrations.length - notRunMigrationsCount - migrationsWithNoChanges.length;
2082
+ // The "applied" tally counts fully-completed migrations — those that
2083
+ // left no deferred work behind. Hybrid migrations whose prompt half was
2084
+ // deferred count as "deferred", not "applied".
2085
+ const appliedCount = migrations.length - skippedPrompts.length;
2086
+ const insideAgent = agentic.kind === 'inside-agent';
2087
+ const tallyLine = (0, migrate_output_1.buildTallyBodyLine)({
2088
+ appliedCount,
2089
+ committedShasCount,
2090
+ skippedPromptsCount,
2091
+ insideAgent,
2092
+ });
2093
+ const tallyBody = tallyLine ? [tallyLine] : undefined;
2094
+ // Only claim "up to date" when there's nothing pending: no deferred
2095
+ // prompts AND no migrations whose commits failed without being absorbed.
2096
+ const upToDateSuffix = skippedPromptsCount > 0 || retainedAtSuccess.length > 0
2097
+ ? ''
2098
+ : ' This workspace is up to date!';
2099
+ // Demote `output.success` to `output.warn` when there's uncommitted state
2100
+ // retained from failed commits — the run did its work, but it would be
2101
+ // misleading to lead with a green "Successfully finished" before the
2102
+ // retained-state block. `.bind(output)` is required: assigning the method
2103
+ // reference to a local would otherwise call it with `this === undefined`.
2104
+ const completionLog = (retainedAtSuccess.length > 0 ? output_1.output.warn : output_1.output.success).bind(output_1.output);
2105
+ const completionTitlePrefix = retainedAtSuccess.length > 0
2106
+ ? 'Finished running migrations with uncommitted state retained'
2107
+ : 'Successfully finished running migrations';
2108
+ if (notRunMigrationsCount === migrations.length && migrations.length > 0) {
2109
+ const remediation = insideAgent
2110
+ ? 'The AI agent driving this run should apply each prompt — see next steps below.'
2111
+ : 'Re-run with --agentic to apply them. See next steps below.';
2112
+ output_1.output.warn({
2113
+ title: `No migrations from '${opts.runMigrations}' were applied — every entry is a prompt-only migration. ${remediation}`,
2114
+ bodyLines: tallyBody,
2115
+ });
2116
+ }
2117
+ else if (ranWithChangesCount > 0) {
2118
+ completionLog({
2119
+ title: `${completionTitlePrefix} from '${opts.runMigrations}'.${upToDateSuffix}`,
2120
+ bodyLines: tallyBody,
1352
2121
  });
1353
2122
  }
1354
2123
  else {
1355
- output_1.output.success({
1356
- title: `No changes were made from running '${opts.runMigrations}'. This workspace is up to date!`,
2124
+ // Pathological-but-possible: a no-op run that still has retained state
2125
+ // (e.g. pre-existing pending diffs that no commit absorbed). Demote
2126
+ // explicitly rather than rely on the implicit invariant.
2127
+ completionLog({
2128
+ title: `No changes were made from running '${opts.runMigrations}'.${upToDateSuffix}`,
2129
+ bodyLines: tallyBody,
2130
+ });
2131
+ }
2132
+ if (retainedAtSuccess.length > 0) {
2133
+ output_1.output.warn({
2134
+ title: `Working-tree state retained from ${retainedAtSuccess.length} migration${retainedAtSuccess.length === 1 ? '' : 's'} whose commits could not be created`,
2135
+ bodyLines: (0, migrate_output_1.buildRetainedAtSuccessBody)(retainedAtSuccess),
1357
2136
  });
1358
2137
  }
1359
- if (nextSteps.length > 0) {
2138
+ if (insideAgent) {
2139
+ // Under inside-agent, emit a directive block so the outer agent has
2140
+ // explicit instructions to act on, not just relay.
2141
+ const directiveLines = (0, migrate_output_1.buildDirectiveBlockBodyLines)({
2142
+ skippedPrompts,
2143
+ migrationEmittedNextSteps,
2144
+ });
2145
+ if (directiveLines.length > 0) {
2146
+ output_1.output.log({
2147
+ title: 'Next steps for the AI agent driving this run',
2148
+ bodyLines: directiveLines,
2149
+ });
2150
+ }
2151
+ }
2152
+ else if (skippedPromptsCount > 0 || migrationEmittedNextSteps.length > 0) {
2153
+ // Non-inside-agent path keeps the legacy "additional information" shape —
2154
+ // the consumer is the human user.
2155
+ const bodyLines = [];
2156
+ if (skippedPromptsCount > 0) {
2157
+ bodyLines.push(formatSkippedPromptsNextStep(skippedPrompts));
2158
+ }
2159
+ bodyLines.push(...migrationEmittedNextSteps);
1360
2160
  output_1.output.log({
1361
2161
  title: `Some migrations have additional information, see below.`,
1362
- bodyLines: nextSteps.map((line) => `- ${line}`),
2162
+ bodyLines: bodyLines.map((line) => `- ${line}`),
1363
2163
  });
1364
2164
  }
1365
2165
  }
@@ -1374,22 +2174,47 @@ function getStringifiedPackageJsonDeps(root) {
1374
2174
  return '';
1375
2175
  }
1376
2176
  }
1377
- async function runNxMigration(root, collectionPath, collection, name, migrationVersion) {
2177
+ async function runNxMigration(root, collectionPath, collection, name, migrationVersion, captureGeneratorOutput) {
1378
2178
  const { path: implPath, fnSymbol } = getImplementationPath(collection, collectionPath, name, migrationVersion);
1379
2179
  const fn = require(implPath)[fnSymbol];
1380
2180
  const host = new tree_1.FsTree(root, process.env.NX_VERBOSE_LOGGING === 'true', `migration ${collection.name}:${name}`);
1381
- let nextSteps = await fn(host, {});
1382
- // This accounts for migrations that mistakenly return a generator callback
1383
- // from a migration. We've never executed these, so its not a breaking change that
1384
- // we don't call them now... but currently shipping a migration with one wouldn't break
1385
- // the migrate flow, so we are being cautious.
1386
- if (!isStringArray(nextSteps)) {
1387
- nextSteps = [];
2181
+ let result;
2182
+ let logs = '';
2183
+ if (captureGeneratorOutput) {
2184
+ const { withGeneratorOutputCapture } = require('./agentic/capture-generator-output');
2185
+ ({ result, logs } = await withGeneratorOutputCapture(() => fn(host, {})));
2186
+ }
2187
+ else {
2188
+ result = await fn(host, {});
1388
2189
  }
2190
+ const { nextSteps, agentContext } = parseMigrationReturn(result);
1389
2191
  host.lock();
1390
2192
  const changes = host.listChanges();
1391
2193
  (0, tree_1.flushChanges)(root, changes);
1392
- return { changes, nextSteps };
2194
+ return { changes, nextSteps, agentContext, logs };
2195
+ }
2196
+ function parseMigrationReturn(value) {
2197
+ if (Array.isArray(value)) {
2198
+ return { nextSteps: filterStrings(value), agentContext: [] };
2199
+ }
2200
+ if (value && typeof value === 'object') {
2201
+ const obj = value;
2202
+ return {
2203
+ nextSteps: filterStrings(obj.nextSteps),
2204
+ agentContext: filterStrings(obj.agentContext),
2205
+ };
2206
+ }
2207
+ // Catches `void`, mistakenly-returned generator callbacks, malformed values.
2208
+ return { nextSteps: [], agentContext: [] };
2209
+ }
2210
+ // Bucket-level tolerance: a single non-string entry shouldn't discard the
2211
+ // whole `nextSteps` / `agentContext` array. Migration authors occasionally
2212
+ // push `null` / `undefined` / a number into the array; we drop the bad entries
2213
+ // and keep the rest so end-of-run guidance isn't silently lost.
2214
+ function filterStrings(value) {
2215
+ if (!Array.isArray(value))
2216
+ return [];
2217
+ return value.filter((v) => typeof v === 'string');
1393
2218
  }
1394
2219
  async function migrate(root, args, rawArgs) {
1395
2220
  await client_1.daemonClient.stop();
@@ -1490,7 +2315,7 @@ function buildMigrationMissingMessage(baseMessage, collectionPath, migrationVers
1490
2315
  const packageJson = (0, fileutils_1.readJsonFile)(packageJsonPath);
1491
2316
  const installedVersion = packageJson.version;
1492
2317
  if (installedVersion &&
1493
- (0, semver_1.lt)(normalizeVersion(installedVersion), normalizeVersion(migrationVersion))) {
2318
+ (0, semver_1.lt)((0, version_utils_1.normalizeVersion)(installedVersion), (0, version_utils_1.normalizeVersion)(migrationVersion))) {
1494
2319
  const packageManager = (0, package_manager_1.detectPackageManager)();
1495
2320
  const pmc = (0, package_manager_1.getPackageManagerCommand)(packageManager);
1496
2321
  const overrideFieldName = getOverrideFieldName(packageManager);
@@ -1600,9 +2425,3 @@ const getNgCompatLayer = (() => {
1600
2425
  return _ngCliAdapter;
1601
2426
  };
1602
2427
  })();
1603
- function isStringArray(value) {
1604
- if (!Array.isArray(value)) {
1605
- return false;
1606
- }
1607
- return value.every((v) => typeof v === 'string');
1608
- }