nx 24.0.0-beta.0 → 24.0.0-beta.2

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 (90) hide show
  1. package/dist/nx-cloud-internals.d.ts +1 -0
  2. package/dist/nx-cloud-internals.js +7 -1
  3. package/dist/src/command-line/graph/graph.js +2 -1
  4. package/dist/src/command-line/migrate/agentic/close-agent-session.d.ts +1 -1
  5. package/dist/src/command-line/migrate/agentic/close-agent-session.js +4 -3
  6. package/dist/src/command-line/migrate/agentic/handoff-gitignore.d.ts +19 -23
  7. package/dist/src/command-line/migrate/agentic/handoff-gitignore.js +16 -19
  8. package/dist/src/command-line/migrate/agentic/master/run-master-session.js +25 -13
  9. package/dist/src/command-line/migrate/agentic/master/spawn-master.js +82 -22
  10. package/dist/src/command-line/migrate/agentic/select.js +3 -3
  11. package/dist/src/command-line/migrate/agentic/types.js +1 -3
  12. package/dist/src/command-line/migrate/command-object.js +5 -9
  13. package/dist/src/command-line/migrate/execute-migration.d.ts +2 -2
  14. package/dist/src/command-line/migrate/execute-migration.js +18 -10
  15. package/dist/src/command-line/migrate/migrate-analytics.d.ts +6 -0
  16. package/dist/src/command-line/migrate/migrate-analytics.js +12 -0
  17. package/dist/src/command-line/migrate/migrate-commits.d.ts +4 -1
  18. package/dist/src/command-line/migrate/migrate-commits.js +10 -3
  19. package/dist/src/command-line/migrate/migrate-config.d.ts +6 -6
  20. package/dist/src/command-line/migrate/migrate-config.js +12 -6
  21. package/dist/src/command-line/migrate/migrate.d.ts +3 -30
  22. package/dist/src/command-line/migrate/migrate.js +206 -237
  23. package/dist/src/command-line/migrate/run/agent-output.d.ts +1 -0
  24. package/dist/src/command-line/migrate/run/agent-output.js +5 -0
  25. package/dist/src/command-line/migrate/run/broker.d.ts +14 -5
  26. package/dist/src/command-line/migrate/run/broker.js +68 -20
  27. package/dist/src/command-line/migrate/run/existing-run-report.d.ts +1 -1
  28. package/dist/src/command-line/migrate/run/existing-run-report.js +7 -5
  29. package/dist/src/command-line/migrate/run/index.d.ts +6 -5
  30. package/dist/src/command-line/migrate/run/index.js +12 -1
  31. package/dist/src/command-line/migrate/run/orchestrator.d.ts +21 -0
  32. package/dist/src/command-line/migrate/run/orchestrator.js +149 -32
  33. package/dist/src/command-line/migrate/run/run-state.d.ts +13 -0
  34. package/dist/src/command-line/migrate/run/run-state.js +21 -0
  35. package/dist/src/command-line/migrate/run/runbook.js +19 -0
  36. package/dist/src/command-line/migrate/run/state-machine.js +4 -1
  37. package/dist/src/command-line/migrate/run/util.d.ts +3 -0
  38. package/dist/src/command-line/migrate/run/util.js +7 -1
  39. package/dist/src/command-line/migrate/sort-migrations.d.ts +1 -7
  40. package/dist/src/command-line/migrate/sort-migrations.js +1 -12
  41. package/dist/src/command-line/show/show-target/info.js +1 -1
  42. package/dist/src/config/nx-json.d.ts +13 -4
  43. package/dist/src/config/task-graph.d.ts +1 -1
  44. package/dist/src/config/workspace-json-project-json.d.ts +72 -0
  45. package/dist/src/core/graph/main.js +1 -1
  46. package/dist/src/devkit-exports.d.ts +1 -1
  47. package/dist/src/hasher/check-task-files.js +4 -4
  48. package/dist/src/hasher/native-task-hasher-impl.js +3 -1
  49. package/dist/src/hasher/task-hasher.d.ts +2 -0
  50. package/dist/src/hasher/task-hasher.js +4 -1
  51. package/dist/src/native/index.d.ts +92 -14
  52. package/dist/src/native/native-bindings.js +3 -0
  53. package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
  54. package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
  55. package/dist/src/project-graph/utils/project-configuration/target-normalization.js +6 -1
  56. package/dist/src/project-graph/utils/retrieve-workspace-files.js +27 -6
  57. package/dist/src/tasks-runner/create-task-graph.d.ts +7 -16
  58. package/dist/src/tasks-runner/create-task-graph.js +43 -57
  59. package/dist/src/tasks-runner/default-tasks-runner.js +1 -1
  60. package/dist/src/tasks-runner/init-tasks-runner.js +13 -0
  61. package/dist/src/tasks-runner/life-cycle.d.ts +4 -1
  62. package/dist/src/tasks-runner/life-cycle.js +7 -0
  63. package/dist/src/tasks-runner/life-cycles/performance-analysis.d.ts +5 -2
  64. package/dist/src/tasks-runner/life-cycles/performance-analysis.js +13 -7
  65. package/dist/src/tasks-runner/life-cycles/performance-life-cycle.d.ts +4 -3
  66. package/dist/src/tasks-runner/life-cycles/performance-life-cycle.js +9 -2
  67. package/dist/src/tasks-runner/readiness/probes.d.ts +9 -0
  68. package/dist/src/tasks-runner/readiness/probes.js +62 -0
  69. package/dist/src/tasks-runner/readiness/ready-when.d.ts +38 -0
  70. package/dist/src/tasks-runner/readiness/ready-when.js +190 -0
  71. package/dist/src/tasks-runner/run-command.js +7 -6
  72. package/dist/src/tasks-runner/running-tasks/batch-process.js +6 -0
  73. package/dist/src/tasks-runner/running-tasks/node-child-process.js +5 -4
  74. package/dist/src/tasks-runner/task-graph-utils.d.ts +9 -6
  75. package/dist/src/tasks-runner/task-graph-utils.js +17 -12
  76. package/dist/src/tasks-runner/task-orchestrator.d.ts +37 -10
  77. package/dist/src/tasks-runner/task-orchestrator.js +459 -79
  78. package/dist/src/tasks-runner/tasks-runner.d.ts +1 -1
  79. package/dist/src/tasks-runner/tasks-schedule.d.ts +18 -1
  80. package/dist/src/tasks-runner/tasks-schedule.js +64 -6
  81. package/dist/src/tasks-runner/utils.js +6 -22
  82. package/dist/src/utils/child-process.js +1 -0
  83. package/dist/src/utils/git-utils.d.ts +6 -1
  84. package/dist/src/utils/git-utils.js +26 -5
  85. package/dist/src/utils/plugins/core-plugins.js +4 -0
  86. package/dist/src/utils/spawn-without-terminal.d.ts +27 -0
  87. package/dist/src/utils/spawn-without-terminal.js +110 -0
  88. package/package.json +12 -11
  89. package/schemas/nx-schema.json +93 -4
  90. package/schemas/project-schema.json +84 -2
@@ -13,7 +13,6 @@ exports.isRunPhaseInvocation = isRunPhaseInvocation;
13
13
  exports.migrate = migrate;
14
14
  exports.readLocalNxVersion = readLocalNxVersion;
15
15
  exports.runMigration = runMigration;
16
- exports.resolveMigrationForRun = resolveMigrationForRun;
17
16
  exports.nxCliPath = nxCliPath;
18
17
  const tslib_1 = require("tslib");
19
18
  const pc = tslib_1.__importStar(require("picocolors"));
@@ -37,6 +36,7 @@ const package_manager_1 = require("../../utils/package-manager");
37
36
  const errors_1 = require("../../utils/min-release-age/errors");
38
37
  const resolve_package_version_1 = require("./resolve-package-version");
39
38
  const handle_errors_1 = require("../../utils/handle-errors");
39
+ const exit_codes_1 = require("../../utils/exit-codes");
40
40
  const connect_to_nx_cloud_1 = require("../nx-cloud/connect/connect-to-nx-cloud");
41
41
  const output_1 = require("../../utils/output");
42
42
  const fs_1 = require("fs");
@@ -56,6 +56,7 @@ const multi_major_1 = require("./multi-major");
56
56
  const prompt_files_1 = require("./prompt-files");
57
57
  const command_object_1 = require("./command-object");
58
58
  const migrate_config_1 = require("./migrate-config");
59
+ const types_1 = require("./agentic/types");
59
60
  const migrate_commits_1 = require("./migrate-commits");
60
61
  const migrate_output_1 = require("./migrate-output");
61
62
  const migration_shape_1 = require("./migration-shape");
@@ -68,6 +69,7 @@ Object.defineProperty(exports, "normalizeVersion", { enumerable: true, get: func
68
69
  const execute_migration_1 = require("./execute-migration");
69
70
  const step_actions_1 = require("./step-actions");
70
71
  const sort_migrations_1 = require("./sort-migrations");
72
+ const text_1 = require("./text");
71
73
  const inception_1 = require("./agentic/inception");
72
74
  const version_skew_guard_1 = require("./version-skew-guard");
73
75
  const versions_1 = require("../../utils/versions");
@@ -88,20 +90,27 @@ function formatCommandFailure(command, error) {
88
90
  .trim();
89
91
  return [`Command failed: ${command}`, ...(details ? [details] : [])].join('\n');
90
92
  }
93
+ // Ctrl+C is left to the child. Killed by it, this process would hand the
94
+ // terminal back to the shell while the child still runs.
91
95
  function runOrReturnExitCode(run) {
96
+ const leaveSigintToChild = () => { };
97
+ process.on('SIGINT', leaveSigintToChild);
92
98
  try {
93
99
  run();
94
100
  return 0;
95
101
  }
96
102
  catch (e) {
97
- if (typeof e === 'object' &&
98
- e !== null &&
99
- 'status' in e &&
100
- typeof e.status === 'number') {
103
+ if (typeof e !== 'object' || e === null)
104
+ throw e;
105
+ if ('signal' in e && e.signal === 'SIGINT')
106
+ (0, exit_codes_1.exitAsInterrupted)();
107
+ if ('status' in e && typeof e.status === 'number')
101
108
  return e.status;
102
- }
103
109
  throw e;
104
110
  }
111
+ finally {
112
+ process.removeListener('SIGINT', leaveSigintToChild);
113
+ }
105
114
  }
106
115
  function cleanSemver(version) {
107
116
  return (0, semver_1.clean)(version) ?? (0, semver_1.coerce)(version);
@@ -777,9 +786,7 @@ async function parseMigrationsOptions(options, fetch) {
777
786
  options.stepAction !== undefined) {
778
787
  throw new Error(`Error: '--step-action' cannot be combined with '--run-migrations'.`);
779
788
  }
780
- // A bare '--run-id' reconciles the run it names. Ungated, unlike init:
781
- // the id has to name a run directory that exists, and only a gated init
782
- // ever creates one.
789
+ // A bare '--run-id' reconciles the run it names.
783
790
  // yargs' choices already reject bad CLI values; this guards programmatic
784
791
  // callers, where silently dropping the action would reconcile without it.
785
792
  if (options.stepAction !== undefined && !(0, step_actions_1.isStepAction)(options.stepAction)) {
@@ -1752,23 +1759,12 @@ function formatSkippedPromptsNextStep(skipped) {
1752
1759
  ...skipped.map((m) => ` - ${m.prompt}`),
1753
1760
  ].join('\n');
1754
1761
  }
1755
- async function executeMigrations(root, migrations, isVerbose, shouldCreateCommits, commitPrefix, shouldSkipInstall = false, agentic, agenticHasDiffContext = false, shouldRunValidation = false) {
1762
+ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommits, commitPrefix, shouldSkipInstall = false,
1763
+ // An enabled agent drives the whole run from one session instead.
1764
+ agentic) {
1756
1765
  const changedDepInstaller = new execute_migration_1.ChangedDepInstaller(root, shouldSkipInstall);
1757
1766
  const migrationsWithNoChanges = [];
1758
- const sortedMigrations = (0, sort_migrations_1.sortMigrations)(migrations, {
1759
- hoistHandoffGitignore: agentic?.kind === 'enabled',
1760
- });
1761
- // Lazy-load the agentic chain so non-agentic runs don't pay its startup cost.
1762
- let agenticRun;
1763
- if (agentic?.kind === 'enabled' && sortedMigrations.length > 0) {
1764
- const { initRunDir, resolveAgenticRunId } = require('./agentic/handoff');
1765
- const { runAgenticPromptStep } = require('./agentic/run-step');
1766
- agenticRun = {
1767
- agentic,
1768
- runDir: initRunDir(root, resolveAgenticRunId(sortedMigrations)),
1769
- runStep: runAgenticPromptStep,
1770
- };
1771
- }
1767
+ const sortedMigrations = (0, sort_migrations_1.sortMigrations)(migrations);
1772
1768
  const printDroppedAgentContext = agentic?.kind === 'inside-agent'
1773
1769
  ? require('./agentic/print-dropped-agent-context').printDroppedAgentContextForOuterAgent
1774
1770
  : undefined;
@@ -1803,12 +1799,6 @@ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommit
1803
1799
  const pendingForCommitBody = () => outcomes
1804
1800
  .filter((o) => o.commit.kind === 'failed')
1805
1801
  .map((o) => ({ package: o.migration.package, name: o.migration.name }));
1806
- // True while at least one prior migration's commit has failed and its
1807
- // diff hasn't been absorbed yet. While true, the working tree carries
1808
- // prior-migration state, so the `hasDiffContext` flag in the hybrid-
1809
- // agentic and validation-agentic prompt branches is suppressed (the
1810
- // prompt-only-with-agentic branch doesn't use `hasDiffContext`).
1811
- const hasPendingCommitDebt = () => outcomes.some((o) => o.commit.kind === 'failed');
1812
1802
  // Single funnel for per-migration commit attempts. Returns the
1813
1803
  // `CommitState` to record on the migration's outcome. On `committed`,
1814
1804
  // back-annotates any prior failed-commit outcomes to `kind: 'absorbed'`
@@ -1841,8 +1831,7 @@ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommit
1841
1831
  return { kind: 'landed', sha: result.sha };
1842
1832
  }
1843
1833
  if (result.status === 'failed') {
1844
- // Diff is still in WT. Subsequent prompts cannot claim git-isolation
1845
- // until a later commit absorbs the backlog.
1834
+ // The diff stays in the working tree until a later commit absorbs it.
1846
1835
  return { kind: 'failed' };
1847
1836
  }
1848
1837
  // `no-changes` and `disabled` — no commit attempted, nothing to record
@@ -1875,89 +1864,36 @@ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommit
1875
1864
  // Content-sensitive so a dirty→dirty case (this migration mutating an
1876
1865
  // already-dirty shared file like `package.json`) doesn't collapse.
1877
1866
  const baselineWorkingTreeSnapshot = (0, git_utils_1.getUncommittedChangesSnapshot)(root);
1878
- // Tracks whether a failure originated in the agentic step so the error
1879
- // event classifies it as 'agentic' rather than 'migration_exec'.
1880
- let inAgenticStep = false;
1881
1867
  try {
1882
- // Read this migration's collection once and derive everything from it:
1883
- // the implementation context (passed to runNxOrAngularMigration) and the
1884
- // documentation path (passed to the agent). Read fresh per iteration so a
1885
- // prior migration's reinstall is reflected.
1886
- const { resolvedCollection, documentationPath } = resolveMigrationForRun(root, m, !!agenticRun);
1868
+ // Read fresh per migration so a prior migration's reinstall is reflected.
1869
+ const resolvedCollection = (0, migration_shape_1.isPromptOnlyMigration)(m)
1870
+ ? undefined
1871
+ : (0, execute_migration_1.readMigrationCollection)(m.package, root);
1887
1872
  let outcome;
1888
1873
  let commit = { kind: 'none' };
1889
- // Set when the migration returned `skipAgentic: true` and something was
1890
- // actually waived, so the end-of-run recaps can report it. A hybrid's
1891
- // prompt is owed in every agentic mode, so waiving it always counts; a
1892
- // generator-only migration only counts when validation would have run.
1874
+ // Set when a hybrid returned `skipAgentic: true`, so the end-of-run
1875
+ // recaps can report the waived prompt.
1893
1876
  let waivedAgenticStep = false;
1894
1877
  if ((0, migration_shape_1.isPromptOnlyMigration)(m)) {
1895
- if (agenticRun) {
1896
- inAgenticStep = true;
1897
- const stepResult = await agenticRun.runStep({
1898
- root,
1899
- migration: m,
1900
- agentic: agenticRun.agentic,
1901
- runDir: agenticRun.runDir,
1902
- installDepsIfChanged,
1903
- documentationPath,
1904
- });
1905
- inAgenticStep = false;
1906
- commit = await attemptMigrationCommit(m);
1907
- (0, migrate_output_1.logAgenticSuccessOutcome)(stepResult.ambiguous ? 'Marked complete by user' : 'Applied', commit.kind === 'landed' ? commit.sha : null, stepResult.summary);
1908
- outcome = 'applied';
1909
- }
1910
- else {
1911
- logger_1.logger.info(pc.dim(`↷ Skipped — ${skipReason}. Listed in next steps.`));
1912
- skippedPrompts.push(m);
1913
- notRunMigrationsCount++;
1914
- outcome = 'deferred';
1915
- }
1878
+ logger_1.logger.info(pc.dim(`↷ Skipped — ${skipReason}. Listed in next steps.`));
1879
+ skippedPrompts.push(m);
1880
+ notRunMigrationsCount++;
1881
+ outcome = 'deferred';
1916
1882
  }
1917
1883
  else if ((0, migration_shape_1.isHybridMigration)(m)) {
1918
- const { changes, nextSteps, agentContext, skipAgentic, logs, madeChanges, } = await (0, execute_migration_1.runNxOrAngularMigration)(root, m, isVerbose,
1919
- /* captureGeneratorOutput: */ !!agenticRun, resolvedCollection);
1884
+ const { nextSteps, agentContext, skipAgentic, madeChanges } = await (0, execute_migration_1.runNxOrAngularMigration)(root, m, isVerbose,
1885
+ /* captureGeneratorOutput: */ false, resolvedCollection);
1920
1886
  migrationEmittedNextSteps.push(...nextSteps);
1921
1887
  if (skipAgentic) {
1922
1888
  // The generator reported the prompt half unnecessary, so nothing is
1923
- // owed: no agent run, and no next-steps entry telling the user to
1924
- // run the prompt themselves. Runs in all three agentic modes.
1889
+ // owed: no next-steps entry telling anyone to run the prompt.
1925
1890
  (0, migrate_output_1.logWaivedAgenticStep)(m, agentContext);
1926
1891
  waivedAgenticStep = true;
1927
1892
  commit = await commitOrRecordNoChanges(m, madeChanges);
1928
1893
  outcome = madeChanges ? 'applied' : 'no-changes';
1929
1894
  }
1930
- else if (agenticRun) {
1931
- // Install any deps the deterministic phase added/bumped before the
1932
- // agent runs — the prompt half may depend on them being present in
1933
- // node_modules.
1934
- await installDepsIfChanged();
1935
- inAgenticStep = true;
1936
- const stepResult = await agenticRun.runStep({
1937
- root,
1938
- migration: m,
1939
- agentic: agenticRun.agentic,
1940
- runDir: agenticRun.runDir,
1941
- installDepsIfChanged,
1942
- documentationPath,
1943
- implContext: {
1944
- logs,
1945
- changes,
1946
- agentContext,
1947
- // When prior commits failed, the working tree carries their
1948
- // diff. The git-inspect path of the prompt would mislead the
1949
- // agent in that case; fall back to embedded `<files_changed>`.
1950
- hasDiffContext: agenticHasDiffContext && !hasPendingCommitDebt(),
1951
- },
1952
- });
1953
- inAgenticStep = false;
1954
- commit = await attemptMigrationCommit(m);
1955
- (0, migrate_output_1.logAgenticSuccessOutcome)(stepResult.ambiguous ? 'Marked complete by user' : 'Applied', commit.kind === 'landed' ? commit.sha : null, stepResult.summary);
1956
- outcome = 'applied';
1957
- }
1958
1895
  else {
1959
- // The inner prompt step doesn't run here (agentic disabled, or
1960
- // running inside an outer agent). Under `inside-agent`, surface the
1896
+ // The prompt half is deferred. Under `inside-agent`, surface the
1961
1897
  // generator-emitted `agentContext` to stdout so the outer driving
1962
1898
  // agent can ingest it. Under `disabled` the run is human-driven;
1963
1899
  // agent-targeted context would only add noise — drop.
@@ -1971,59 +1907,16 @@ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommit
1971
1907
  }
1972
1908
  }
1973
1909
  else {
1974
- // Defer commit until validation succeeds; failed validation leaves
1975
- // changes uncommitted in the working tree for the user to review.
1976
- const validationRun = agenticRun && shouldRunValidation ? agenticRun : undefined;
1977
- const { changes, nextSteps, agentContext, skipAgentic, logs, madeChanges, } = await (0, execute_migration_1.runNxOrAngularMigration)(root, m, isVerbose,
1978
- /* captureGeneratorOutput: */ !!validationRun, resolvedCollection);
1910
+ const { nextSteps, agentContext, madeChanges } = await (0, execute_migration_1.runNxOrAngularMigration)(root, m, isVerbose,
1911
+ /* captureGeneratorOutput: */ false, resolvedCollection);
1979
1912
  migrationEmittedNextSteps.push(...nextSteps);
1980
- // Whether a validation step was on the table at all. `skipAgentic`
1981
- // waives nothing when it wasn't, so the whole waived path hangs off
1982
- // this, not just the log line.
1983
- const validationApplies = !!validationRun && changes.length > 0;
1984
- const canRunValidation = validationApplies && !skipAgentic;
1985
- if (canRunValidation) {
1986
- // Install any deps the deterministic phase added/bumped before the
1987
- // validation agent runs — the agent may run tasks that need them.
1988
- await installDepsIfChanged();
1989
- inAgenticStep = true;
1990
- const stepResult = await validationRun.runStep({
1991
- root,
1992
- migration: m,
1993
- agentic: validationRun.agentic,
1994
- runDir: validationRun.runDir,
1995
- installDepsIfChanged,
1996
- documentationPath,
1997
- implContext: {
1998
- logs,
1999
- changes,
2000
- agentContext,
2001
- // See the hybrid agentic branch above for the rationale on
2002
- // why pending commit debt gates git-inspect context.
2003
- hasDiffContext: agenticHasDiffContext && !hasPendingCommitDebt(),
2004
- },
2005
- mode: 'generic-validation',
2006
- });
2007
- inAgenticStep = false;
2008
- commit = await attemptMigrationCommit(m);
2009
- (0, migrate_output_1.logAgenticSuccessOutcome)(stepResult.ambiguous
2010
- ? 'Marked complete by user'
2011
- : 'Validation passed', commit.kind === 'landed' ? commit.sha : null, stepResult.summary);
2012
- outcome = 'applied';
2013
- }
2014
- else {
2015
- if (skipAgentic && validationApplies) {
2016
- (0, migrate_output_1.logWaivedAgenticStep)(m, agentContext);
2017
- waivedAgenticStep = true;
2018
- }
2019
- else if (printDroppedAgentContext && agentContext.length > 0) {
2020
- // Inner validation step didn't run. Surface `agentContext` under
2021
- // `inside-agent` so the outer driving agent can ingest it.
2022
- printDroppedAgentContext({ migration: m, agentContext });
2023
- }
2024
- commit = await commitOrRecordNoChanges(m, madeChanges);
2025
- outcome = madeChanges ? 'applied' : 'no-changes';
1913
+ if (printDroppedAgentContext && agentContext.length > 0) {
1914
+ // Surface `agentContext` under `inside-agent` so the outer driving
1915
+ // agent can ingest it.
1916
+ printDroppedAgentContext({ migration: m, agentContext });
2026
1917
  }
1918
+ commit = await commitOrRecordNoChanges(m, madeChanges);
1919
+ outcome = madeChanges ? 'applied' : 'no-changes';
2027
1920
  }
2028
1921
  outcomes.push({
2029
1922
  migration: { package: m.package, name: m.name },
@@ -2053,9 +1946,7 @@ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommit
2053
1946
  (0, migrate_analytics_1.reportMigrateRunError)({
2054
1947
  code: e instanceof execute_migration_1.NpmPeerDepsInstallError
2055
1948
  ? 'npm_install'
2056
- : inAgenticStep
2057
- ? 'agentic'
2058
- : 'migration_exec',
1949
+ : 'migration_exec',
2059
1950
  migrationPackage: m.package,
2060
1951
  migrationName: m.name,
2061
1952
  migrationCount: totalMigrations,
@@ -2117,8 +2008,70 @@ async function executeMigrations(root, migrations, isVerbose, shouldCreateCommit
2117
2008
  retainedAtSuccess: (0, migrate_output_1.retainedMigrations)(outcomes).map((p) => `${p.package}: ${p.name}`),
2118
2009
  };
2119
2010
  }
2120
- function orchestratorFlagNeedsOrchestrator(flag) {
2121
- return `'${flag}' acts on an orchestrated migrate run (NX_MIGRATE_ORCHESTRATOR=true with an enabled agent), and this invocation is not orchestrated.`;
2011
+ const CONTINUE_NEEDS_ORCHESTRATION = `'--run-id' continues an orchestrated migrate run, and this invocation is not orchestrated. Orchestration needs an enabled agent, or an AI agent running nx outside CI; --agentic=false and the WASM build turn it off.`;
2012
+ const ID_PROBLEM_LABELS = {
2013
+ 'not-shell-safe': 'not shell-safe',
2014
+ duplicate: 'listed more than once',
2015
+ };
2016
+ // The classic loop records no run, so it must not run over an active one. The
2017
+ // run is reported or asked about before the default-branch prompt; a start
2018
+ // fresh is checked there too, and the caller deletes the run only after it.
2019
+ async function settleActiveRunForClassic(root, opts, migrations) {
2020
+ const { activeRunForClassic, checkRunForStartFresh, renderContinueCommand, renderExistingRunCommands, renderExistingRunReport, } = require('./run');
2021
+ const active = activeRunForClassic(root, migrations.map((m) => `${m.package}:${m.name}`));
2022
+ // With no active run left, the check refuses as init does: the run
2023
+ // completed after the pre-install check, so its plan already ran.
2024
+ if (opts.startFresh === true &&
2025
+ (active === null || active.runId === opts.runId)) {
2026
+ checkRunForStartFresh(root, opts.runId);
2027
+ return { kind: 'run', replaceRunId: opts.runId };
2028
+ }
2029
+ if (!active) {
2030
+ return { kind: 'run' };
2031
+ }
2032
+ const { runId, facts } = active;
2033
+ // Start fresh refuses while another process holds the run, so a held run
2034
+ // leaves nothing to ask; an agent cannot answer a prompt.
2035
+ const held = facts.otherHolders === 'unknown' || facts.otherHolders.length > 0;
2036
+ if (opts.startFresh !== true &&
2037
+ !held &&
2038
+ (0, safe_prompt_1.canPrompt)(opts.interactive) &&
2039
+ !(0, inception_1.isInsideAgent)()) {
2040
+ output_1.output.log(renderExistingRunReport(facts));
2041
+ const choice = await (0, safe_prompt_1.migrateChoice)({
2042
+ message: 'What do you want to do with the active migrate run?',
2043
+ choices: [
2044
+ {
2045
+ value: 'start-fresh',
2046
+ label: 'Start fresh',
2047
+ hint: 'deletes the run record, then runs the whole plan without an agent',
2048
+ },
2049
+ { value: 'abort', label: 'Abort', hint: 'leaves the run as it is' },
2050
+ ],
2051
+ });
2052
+ switch (choice) {
2053
+ case 'start-fresh':
2054
+ checkRunForStartFresh(root, runId);
2055
+ return { kind: 'run', replaceRunId: runId };
2056
+ case 'abort':
2057
+ output_1.output.log({
2058
+ title: `Leaving migrate run ${runId} as it is. To continue it with an agent, run ${renderContinueCommand(root, runId, facts.policy)}.`,
2059
+ });
2060
+ (0, migrate_analytics_1.reportMigrateRunStopped)('aborted');
2061
+ return { kind: 'stop', exitCode: undefined };
2062
+ default: {
2063
+ const unhandled = choice;
2064
+ throw new Error(`Unhandled choice: ${unhandled}`);
2065
+ }
2066
+ }
2067
+ }
2068
+ const report = renderExistingRunReport(facts, renderExistingRunCommands(root, facts, opts.runMigrations, opts.agentic === false ? false : undefined));
2069
+ if (native_1.IS_WASM) {
2070
+ report.bodyLines.push(`The WASM build can run neither command. Continue the run with the native nx binary, or make sure no nx migrate process is acting on it, remove ${types_1.MIGRATE_RUNS_RELATIVE_DIR}/${runId}, then run the plan again.`);
2071
+ }
2072
+ output_1.output.warn(report);
2073
+ (0, migrate_analytics_1.reportMigrateRunStopped)('existing_run');
2074
+ return { kind: 'stop', exitCode: 1 };
2122
2075
  }
2123
2076
  // nx is located at spawn time, after the gated pre-install, so the child runs
2124
2077
  // the bytes that install put in place.
@@ -2134,20 +2087,18 @@ function handOffToLocalNx(args) {
2134
2087
  return exitCode !== 0 ? exitCode : undefined;
2135
2088
  }
2136
2089
  async function runMigrations(root, opts, args, isVerbose, shouldCreateCommits, commitPrefix, shouldSkipInstall = false) {
2137
- // Both flags act on an orchestrated run: refuse them where none can run
2138
- // (an explicit --agentic=false outside an agent cannot reach one either),
2139
- // before the install and before --if-exists could return silently.
2140
- const orchestratorFlag = opts.startFresh === true
2141
- ? '--start-fresh'
2142
- : opts.runId !== undefined
2143
- ? '--run-id'
2144
- : undefined;
2145
- if (orchestratorFlag !== undefined &&
2146
- (process.env.NX_MIGRATE_ORCHESTRATOR !== 'true' ||
2147
- (opts.agentic === false && !(0, inception_1.isInsideAgent)()))) {
2148
- throw new Error(orchestratorFlagNeedsOrchestrator(orchestratorFlag));
2149
- }
2090
+ // The WASM build lacks the native locks a durable run relies on.
2091
+ const outerAgentDrivesRun = (0, inception_1.isInsideAgent)() && opts.agentic !== false && !(0, is_ci_1.isCI)() && !native_1.IS_WASM;
2150
2092
  const isContinue = opts.runId !== undefined && opts.startFresh !== true;
2093
+ // Refused before the install, and before --if-exists could return silently,
2094
+ // when neither the outer agent nor a master session (an agent enabled
2095
+ // outside one, off WASM) can continue the run. A start fresh may reach the
2096
+ // classic loop, which deletes the run's record and runs the plan.
2097
+ if (isContinue &&
2098
+ !outerAgentDrivesRun &&
2099
+ ((0, inception_1.isInsideAgent)() || opts.agentic === false || native_1.IS_WASM)) {
2100
+ throw new Error(CONTINUE_NEEDS_ORCHESTRATION);
2101
+ }
2151
2102
  let continued;
2152
2103
  if (isContinue) {
2153
2104
  // Before the install: a concurrent start-fresh must not delete the run
@@ -2161,7 +2112,35 @@ async function runMigrations(root, opts, args, isVerbose, shouldCreateCommits, c
2161
2112
  activeRunToReplace(root, opts.runId);
2162
2113
  }
2163
2114
  if (!shouldSkipInstall && !process.env.NX_MIGRATE_SKIP_INSTALL) {
2164
- await (0, execute_migration_1.runInstall)();
2115
+ // The run must resume while the cause of a step's failed install persists.
2116
+ const failedInstallSteps = continued?.steps.filter((s) => s.installFailed === true) ?? [];
2117
+ if (failedInstallSteps.length > 0) {
2118
+ const { pmInstallCommand, stepLabel, TERMINAL_STEP_STATUSES } = require('./run');
2119
+ // A settled step keeps its mark, but no step action can retry it.
2120
+ const settled = failedInstallSteps.filter((s) => TERMINAL_STEP_STATUSES.has(s.status));
2121
+ const retryable = failedInstallSteps.filter((s) => !settled.includes(s));
2122
+ const notCompleted = (steps) => `The dependency install of ${steps
2123
+ .map(stepLabel)
2124
+ .join(', ')} did not complete earlier in this run.`;
2125
+ output_1.output.warn({
2126
+ title: 'Skipping the dependency install',
2127
+ bodyLines: [
2128
+ ...(retryable.length > 0
2129
+ ? [
2130
+ `${notCompleted(retryable)} Retrying that step installs again; any other choice leaves the install to you.`,
2131
+ ]
2132
+ : []),
2133
+ ...(settled.length > 0
2134
+ ? [
2135
+ `${notCompleted(settled)} Run \`${pmInstallCommand(root)}\` once the cause of the failed install is fixed.`,
2136
+ ]
2137
+ : []),
2138
+ ].map(text_1.singleLine),
2139
+ });
2140
+ }
2141
+ else {
2142
+ await (0, execute_migration_1.runInstall)();
2143
+ }
2165
2144
  }
2166
2145
  if (!__dirname.startsWith(workspace_root_1.workspaceRoot)) {
2167
2146
  // we are running from a temp installation with nx latest, switch to running
@@ -2214,11 +2193,19 @@ async function runMigrations(root, opts, args, isVerbose, shouldCreateCommits, c
2214
2193
  validate: opts.validate,
2215
2194
  finalValidation: opts.finalValidation,
2216
2195
  });
2196
+ // Orchestration refuses ids it cannot dispense verbatim or resolve to one
2197
+ // step, so a new run with one falls back to the classic loop. A continue
2198
+ // has no classic fallback, since it runs the plan its run recorded, and a
2199
+ // start fresh still reaches init's refusal.
2200
+ const idProblems = isContinue || opts.startFresh === true
2201
+ ? []
2202
+ : require('./run').migrationIdProblems(migrations);
2203
+ const idProblemLines = idProblems.map(({ id, problem }) => `- ${(0, text_1.singleLine)(id)} (${ID_PROBLEM_LABELS[problem]})`);
2217
2204
  // An outer agent drives the loop, so hand off to the orchestrator instead of
2218
2205
  // the classic loop: init starts a fresh run or reports an already-active
2219
2206
  // one; `--run-id` continues that run. Bare `--run-id` reconciles are
2220
2207
  // dispatched separately and never reach here.
2221
- if (process.env.NX_MIGRATE_ORCHESTRATOR === 'true' && (0, inception_1.isInsideAgent)()) {
2208
+ if (outerAgentDrivesRun && idProblems.length === 0) {
2222
2209
  const { runOrchestratorInit, runOrchestratorResume } = require('./run');
2223
2210
  // Orchestrated runs are agent-driven, so commits default on exactly as they
2224
2211
  // do under `--agentic=enabled`; the orchestrator replaces `resolveAgentic`.
@@ -2279,11 +2266,20 @@ async function runMigrations(root, opts, args, isVerbose, shouldCreateCommits, c
2279
2266
  });
2280
2267
  return;
2281
2268
  }
2269
+ if (outerAgentDrivesRun) {
2270
+ output_1.output.warn({
2271
+ title: 'Running the migrations without an orchestrated run: orchestrated runs need each migration id to be shell-safe (letters, digits and @/:._-) and listed once, and these are not:',
2272
+ bodyLines: [
2273
+ ...idProblemLines,
2274
+ 'No run is recorded, so --run-id cannot continue this one. Prompt work is listed as next steps for the AI agent driving this run, and no validation runs.',
2275
+ ],
2276
+ });
2277
+ }
2282
2278
  (0, migrate_analytics_1.reportMigrateRunStart)({
2283
2279
  createCommits: shouldCreateCommits ?? false,
2284
2280
  migrationCount: migrations.length,
2285
2281
  });
2286
- const { resolveAgentic, resolveShouldRunValidation } = require('./agentic/select');
2282
+ const { resolveAgentic } = require('./agentic/select');
2287
2283
  let agentic;
2288
2284
  try {
2289
2285
  agentic = await resolveAgentic({
@@ -2296,7 +2292,25 @@ async function runMigrations(root, opts, args, isVerbose, shouldCreateCommits, c
2296
2292
  (0, migrate_analytics_1.reportMigrateRunError)({ code: 'agentic', error: e });
2297
2293
  throw e;
2298
2294
  }
2299
- const { effective: effectiveCreateCommits, agenticHasDiffContext, warning: createCommitsWarning, error: createCommitsError, } = (0, migrate_commits_1.resolveCreateCommits)({
2295
+ // Resolved before the commit policy, which defaults on only for an agent.
2296
+ if (agentic.kind === 'enabled' && native_1.IS_WASM) {
2297
+ output_1.output.warn({
2298
+ title: 'Skipping the agentic flow: it needs the native nx binary, and this run loaded the WASM build.',
2299
+ bodyLines: ['Continuing the migration without the agentic flow.'],
2300
+ });
2301
+ agentic = { kind: 'disabled' };
2302
+ }
2303
+ if (agentic.kind === 'enabled' && idProblems.length > 0) {
2304
+ output_1.output.warn({
2305
+ title: 'Skipping the agentic flow: it needs each migration id to be shell-safe (letters, digits and @/:._-) and listed once, and these are not:',
2306
+ bodyLines: [
2307
+ ...idProblemLines,
2308
+ 'Continuing the migration without the agentic flow. Generators still run. Prompt-only migrations and the prompt part of hybrid migrations are skipped and listed as next steps, and no AI validation runs.',
2309
+ ],
2310
+ });
2311
+ agentic = { kind: 'disabled' };
2312
+ }
2313
+ const { effective: effectiveCreateCommits, warning: createCommitsWarning, error: createCommitsError, } = (0, migrate_commits_1.resolveCreateCommits)({
2300
2314
  createCommits: shouldCreateCommits,
2301
2315
  mode: agentic.kind,
2302
2316
  isGitRepo: (0, git_utils_1.isGitRepository)(root),
@@ -2314,12 +2328,8 @@ async function runMigrations(root, opts, args, isVerbose, shouldCreateCommits, c
2314
2328
  const confirmNewRunCommits = async () => !effectiveCreateCommits ||
2315
2329
  !(0, safe_prompt_1.canPrompt)(opts.interactive) ||
2316
2330
  (0, migrate_commits_1.confirmMigrationCommitsOnDefaultBranch)(root, 'running migrations');
2317
- // Dark: with the env var set, the agent drives the whole run through the
2318
- // orchestrator from one session instead of being spawned per step. Not
2319
- // under WASM, where the broker has no native lock to detect a dead parent.
2320
- if (agentic.kind === 'enabled' &&
2321
- process.env.NX_MIGRATE_ORCHESTRATOR === 'true' &&
2322
- !native_1.IS_WASM) {
2331
+ // The agent drives the whole run through the orchestrator from one session.
2332
+ if (agentic.kind === 'enabled') {
2323
2333
  const init = orchestratorInitInput(effectiveCreateCommits);
2324
2334
  const { runMasterSession } = require('./agentic/master/run-master-session');
2325
2335
  return await runMasterSession({
@@ -2331,16 +2341,22 @@ async function runMigrations(root, opts, args, isVerbose, shouldCreateCommits, c
2331
2341
  confirmStart: confirmNewRunCommits,
2332
2342
  });
2333
2343
  }
2334
- if (orchestratorFlag !== undefined) {
2335
- throw new Error(orchestratorFlagNeedsOrchestrator(orchestratorFlag));
2344
+ if (isContinue) {
2345
+ throw new Error(CONTINUE_NEEDS_ORCHESTRATION);
2346
+ }
2347
+ const classicGate = await settleActiveRunForClassic(root, opts, migrations);
2348
+ if (classicGate.kind === 'stop') {
2349
+ return classicGate.exitCode;
2336
2350
  }
2337
2351
  if (!(await confirmNewRunCommits())) {
2352
+ (0, migrate_analytics_1.reportMigrateRunStopped)('declined_commits');
2338
2353
  return;
2339
2354
  }
2340
- const shouldRunValidation = resolveShouldRunValidation({
2341
- validate: opts.validate,
2342
- agenticKind: agentic.kind,
2343
- });
2355
+ // After the prompt, so declining it keeps the run.
2356
+ if (classicGate.replaceRunId !== undefined) {
2357
+ const { deleteRunForStartFresh } = require('./run');
2358
+ deleteRunForStartFresh(root, classicGate.replaceRunId);
2359
+ }
2344
2360
  output_1.output.log({
2345
2361
  title: `Running migrations from '${opts.runMigrations}'` +
2346
2362
  (effectiveCreateCommits
@@ -2350,18 +2366,7 @@ async function runMigrations(root, opts, args, isVerbose, shouldCreateCommits, c
2350
2366
  if (effectiveCreateCommits) {
2351
2367
  (0, migrate_commits_1.commitCheckpointBeforeMigrations)(root, commitPrefix);
2352
2368
  }
2353
- if (agentic.kind === 'enabled') {
2354
- const { applyAgenticHandoffGitignoreFallback } = require('./agentic/handoff-gitignore');
2355
- const { packageJson: nxPackageJson } = (0, package_json_1.readModulePackageJson)('nx', (0, installation_directory_1.getNxRequirePaths)(root));
2356
- await applyAgenticHandoffGitignoreFallback({
2357
- migrations,
2358
- installedNxVersion: nxPackageJson.version,
2359
- effectiveCreateCommits,
2360
- commitPrefix,
2361
- root,
2362
- });
2363
- }
2364
- const { migrationsWithNoChanges, skippedPromptsCount, notRunMigrationsCount, skippedPrompts, migrationEmittedNextSteps, committedShasCount, waivedAgenticStepsCount, retainedAtSuccess, } = await executeMigrations(root, migrations, isVerbose, effectiveCreateCommits, commitPrefix, shouldSkipInstall, agentic, agenticHasDiffContext, shouldRunValidation);
2369
+ const { migrationsWithNoChanges, skippedPromptsCount, notRunMigrationsCount, skippedPrompts, migrationEmittedNextSteps, committedShasCount, waivedAgenticStepsCount, retainedAtSuccess, } = await executeMigrations(root, migrations, isVerbose, effectiveCreateCommits, commitPrefix, shouldSkipInstall, agentic);
2365
2370
  const ranWithChangesCount = migrations.length - notRunMigrationsCount - migrationsWithNoChanges.length;
2366
2371
  // The "applied" tally counts fully-completed migrations — those that
2367
2372
  // left no deferred work behind. Hybrid migrations whose prompt half was
@@ -2391,9 +2396,13 @@ async function runMigrations(root, opts, args, isVerbose, shouldCreateCommits, c
2391
2396
  ? 'Finished running migrations with uncommitted state retained'
2392
2397
  : 'Successfully finished running migrations';
2393
2398
  if (notRunMigrationsCount === migrations.length && migrations.length > 0) {
2399
+ // Under WASM or with ids orchestration refuses, an --agentic re-run lands
2400
+ // here again.
2394
2401
  const remediation = insideAgent
2395
2402
  ? 'The AI agent driving this run should apply each prompt — see next steps below.'
2396
- : 'Re-run with --agentic to apply them. See next steps below.';
2403
+ : native_1.IS_WASM || idProblems.length > 0
2404
+ ? 'Apply each prompt yourself. See next steps below.'
2405
+ : 'Re-run with --agentic to apply them. See next steps below.';
2397
2406
  output_1.output.warn({
2398
2407
  title: `No migrations from '${opts.runMigrations}' were applied — every entry is a prompt-only migration. ${remediation}`,
2399
2408
  bodyLines: tallyBody,
@@ -2449,7 +2458,6 @@ async function runMigrations(root, opts, args, isVerbose, shouldCreateCommits, c
2449
2458
  }
2450
2459
  (0, migrate_analytics_1.reportMigrateRunComplete)({
2451
2460
  agenticOutcome: agentic.kind,
2452
- agentUsed: agentic.kind === 'enabled' ? agentic.selectedAgent.id : undefined,
2453
2461
  migrationCount: migrations.length,
2454
2462
  appliedCount,
2455
2463
  });
@@ -2834,45 +2842,6 @@ async function runMigration() {
2834
2842
  return runLocalMigrate();
2835
2843
  });
2836
2844
  }
2837
- /**
2838
- * Resolves a migration's collection once and derives everything the run loop
2839
- * needs from that single read: the implementation context (`collection` +
2840
- * `collectionPath`, handed to `runNxOrAngularMigration`) and, for agentic runs,
2841
- * the workspace-relative documentation path handed to the agent.
2842
- *
2843
- * Read fresh per migration (not cached across the loop) so a prior migration's
2844
- * reinstall is reflected, exactly as before. Error handling matches each field's
2845
- * role:
2846
- * - Migrations that run an implementation REQUIRE the collection; an unreadable
2847
- * collection throws and aborts that migration (caught by the run loop).
2848
- * - Prompt-only migrations don't run an implementation, so the collection is
2849
- * read only to resolve documentation - a failure there is non-fatal: the
2850
- * prompt still runs and the supplementary doc is skipped with a warning.
2851
- */
2852
- function resolveMigrationForRun(root, migration, resolveDocumentation) {
2853
- let resolvedCollection;
2854
- if (!(0, migration_shape_1.isPromptOnlyMigration)(migration)) {
2855
- resolvedCollection = (0, execute_migration_1.readMigrationCollection)(migration.package, root);
2856
- }
2857
- else if (resolveDocumentation && migration.documentation) {
2858
- try {
2859
- resolvedCollection = (0, execute_migration_1.readMigrationCollection)(migration.package, root);
2860
- }
2861
- catch {
2862
- // Non-fatal: documentation is supplementary; the warning below fires.
2863
- }
2864
- }
2865
- let documentationPath;
2866
- if (resolveDocumentation && migration.documentation) {
2867
- documentationPath = resolvedCollection
2868
- ? (0, execute_migration_1.resolveDocumentationFileToWorkspacePath)(root, (0, path_1.dirname)(resolvedCollection.collectionPath), migration.documentation)
2869
- : undefined;
2870
- if (!documentationPath) {
2871
- logger_1.logger.warn(`Could not resolve the "documentation" file "${migration.documentation}" declared for migration "${migration.package}: ${migration.name}". It will be skipped as additional context for the AI agent.`);
2872
- }
2873
- }
2874
- return { resolvedCollection, documentationPath };
2875
- }
2876
2845
  async function nxCliPath(nxWorkspaceRoot) {
2877
2846
  const version = process.env.NX_MIGRATE_CLI_VERSION || 'latest';
2878
2847
  const isVerbose = process.env.NX_VERBOSE_LOGGING === 'true';