nx 23.2.1 → 23.3.0-beta.1

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 (149) hide show
  1. package/dist/src/command-line/format/format.js +2 -18
  2. package/dist/src/command-line/graph/graph.js +42 -3
  3. package/dist/src/command-line/init/implementation/format.js +2 -3
  4. package/dist/src/command-line/migrate/agentic/definitions.js +75 -19
  5. package/dist/src/command-line/migrate/agentic/format-command.d.ts +14 -0
  6. package/dist/src/command-line/migrate/agentic/format-command.js +44 -0
  7. package/dist/src/command-line/migrate/agentic/handoff-gitignore.d.ts +3 -3
  8. package/dist/src/command-line/migrate/agentic/handoff-gitignore.js +3 -3
  9. package/dist/src/command-line/migrate/agentic/handoff.d.ts +27 -17
  10. package/dist/src/command-line/migrate/agentic/handoff.js +107 -24
  11. package/dist/src/command-line/migrate/agentic/instruction-files.d.ts +21 -0
  12. package/dist/src/command-line/migrate/agentic/instruction-files.js +36 -0
  13. package/dist/src/command-line/migrate/agentic/prompts/fragments.d.ts +16 -0
  14. package/dist/src/command-line/migrate/agentic/prompts/fragments.js +70 -0
  15. package/dist/src/command-line/migrate/agentic/prompts/generic-validation.js +4 -3
  16. package/dist/src/command-line/migrate/agentic/prompts/system-prompt.d.ts +25 -1
  17. package/dist/src/command-line/migrate/agentic/prompts/system-prompt.js +57 -34
  18. package/dist/src/command-line/migrate/agentic/run-step.js +28 -4
  19. package/dist/src/command-line/migrate/agentic/runner.d.ts +17 -8
  20. package/dist/src/command-line/migrate/agentic/runner.js +110 -55
  21. package/dist/src/command-line/migrate/agentic/types.d.ts +23 -8
  22. package/dist/src/command-line/migrate/agentic/types.js +10 -7
  23. package/dist/src/command-line/migrate/migrate-commits.d.ts +1 -0
  24. package/dist/src/command-line/migrate/migrate-commits.js +9 -0
  25. package/dist/src/command-line/migrate/migrate-output.d.ts +3 -7
  26. package/dist/src/command-line/migrate/migrate-output.js +3 -7
  27. package/dist/src/command-line/migrate/migrate.js +21 -8
  28. package/dist/src/command-line/migrate/run/agent-output.d.ts +10 -0
  29. package/dist/src/command-line/migrate/run/agent-output.js +16 -2
  30. package/dist/src/command-line/migrate/run/agent-work-payload.d.ts +37 -0
  31. package/dist/src/command-line/migrate/run/agent-work-payload.js +142 -0
  32. package/dist/src/command-line/migrate/run/atomic-write.d.ts +8 -0
  33. package/dist/src/command-line/migrate/run/atomic-write.js +17 -0
  34. package/dist/src/command-line/migrate/run/index.d.ts +1 -1
  35. package/dist/src/command-line/migrate/run/issues.d.ts +106 -0
  36. package/dist/src/command-line/migrate/run/issues.js +980 -0
  37. package/dist/src/command-line/migrate/run/orchestrator.d.ts +1 -0
  38. package/dist/src/command-line/migrate/run/orchestrator.js +662 -105
  39. package/dist/src/command-line/migrate/run/run-state.d.ts +43 -8
  40. package/dist/src/command-line/migrate/run/run-state.js +184 -20
  41. package/dist/src/command-line/migrate/run/runbook.d.ts +17 -0
  42. package/dist/src/command-line/migrate/run/runbook.js +97 -0
  43. package/dist/src/command-line/migrate/run/state-lock.d.ts +2 -2
  44. package/dist/src/command-line/migrate/run/state-lock.js +2 -2
  45. package/dist/src/command-line/migrate/run/state-machine.d.ts +5 -1
  46. package/dist/src/command-line/migrate/run/state-machine.js +27 -1
  47. package/dist/src/command-line/migrate/run/worker.js +206 -56
  48. package/dist/src/command-line/nx-cloud/connect/connect-to-nx-cloud.js +5 -1
  49. package/dist/src/command-line/release/publish.js +11 -4
  50. package/dist/src/command-line/release/utils/remote-release-clients/github.js +3 -3
  51. package/dist/src/command-line/release/utils/remote-release-clients/gitlab.js +3 -3
  52. package/dist/src/command-line/release/utils/remote-release-clients/remote-release-client.d.ts +2 -0
  53. package/dist/src/command-line/release/utils/remote-release-clients/remote-release-client.js +35 -0
  54. package/dist/src/command-line/yargs-utils/shared-options.d.ts +1 -1
  55. package/dist/src/command-line/yargs-utils/shared-options.js +22 -4
  56. package/dist/src/config/workspace-json-project-json.d.ts +2 -0
  57. package/dist/src/core/graph/main.js +1 -1
  58. package/dist/src/daemon/client/client.d.ts +4 -2
  59. package/dist/src/daemon/client/client.js +20 -11
  60. package/dist/src/daemon/message-types/run-tasks-execution-hooks.d.ts +3 -2
  61. package/dist/src/daemon/server/handle-hash-tasks.d.ts +8 -2
  62. package/dist/src/daemon/server/handle-hash-tasks.js +16 -3
  63. package/dist/src/daemon/server/handle-outputs-changes.d.ts +2 -2
  64. package/dist/src/daemon/server/handle-outputs-changes.js +21 -6
  65. package/dist/src/daemon/server/handle-tasks-execution-hooks.d.ts +3 -2
  66. package/dist/src/daemon/server/outputs-tracking.d.ts +6 -0
  67. package/dist/src/daemon/server/outputs-tracking.js +18 -0
  68. package/dist/src/daemon/server/project-graph-incremental-recomputation.d.ts +22 -7
  69. package/dist/src/daemon/server/project-graph-incremental-recomputation.js +99 -69
  70. package/dist/src/daemon/server/restart-checks.d.ts +22 -0
  71. package/dist/src/daemon/server/restart-checks.js +77 -0
  72. package/dist/src/daemon/server/server.js +28 -18
  73. package/dist/src/daemon/server/shutdown-utils.d.ts +0 -5
  74. package/dist/src/daemon/server/shutdown-utils.js +3 -26
  75. package/dist/src/hasher/hash-task.js +20 -16
  76. package/dist/src/hasher/native-task-hasher-impl.d.ts +8 -0
  77. package/dist/src/hasher/native-task-hasher-impl.js +46 -3
  78. package/dist/src/hasher/task-hasher.d.ts +9 -0
  79. package/dist/src/hasher/task-hasher.js +14 -0
  80. package/dist/src/native/index.d.ts +202 -21
  81. package/dist/src/native/index.js +3 -3
  82. package/dist/src/native/native-bindings.js +3 -1
  83. package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
  84. package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
  85. package/dist/src/plugins/js/lock-file/pnpm-parser.js +1 -1
  86. package/dist/src/plugins/js/lock-file/utils/pnpm-normalizer.d.ts +2 -0
  87. package/dist/src/plugins/js/lock-file/utils/pnpm-normalizer.js +22 -0
  88. package/dist/src/plugins/js/utils/register.js +2 -1
  89. package/dist/src/project-graph/plugins/isolation/isolated-plugin.js +2 -1
  90. package/dist/src/project-graph/plugins/isolation/messaging.d.ts +3 -2
  91. package/dist/src/project-graph/plugins/isolation/plugin-worker.js +5 -1
  92. package/dist/src/project-graph/plugins/loaded-nx-plugin.d.ts +3 -2
  93. package/dist/src/project-graph/plugins/loaded-nx-plugin.js +5 -1
  94. package/dist/src/project-graph/plugins/task-results-stub.d.ts +50 -0
  95. package/dist/src/project-graph/plugins/task-results-stub.js +111 -0
  96. package/dist/src/project-graph/plugins/tasks-execution-hooks.d.ts +3 -2
  97. package/dist/src/project-graph/plugins/tasks-execution-hooks.js +2 -1
  98. package/dist/src/project-graph/project-graph.js +2 -0
  99. package/dist/src/tasks-runner/batch/run-batch.js +15 -0
  100. package/dist/src/tasks-runner/cache.d.ts +35 -0
  101. package/dist/src/tasks-runner/cache.js +40 -0
  102. package/dist/src/tasks-runner/create-task-graph.js +1 -0
  103. package/dist/src/tasks-runner/default-tasks-runner.js +1 -1
  104. package/dist/src/tasks-runner/forked-process-task-runner.d.ts +1 -1
  105. package/dist/src/tasks-runner/forked-process-task-runner.js +8 -7
  106. package/dist/src/tasks-runner/init-tasks-runner.js +5 -1
  107. package/dist/src/tasks-runner/is-tui-enabled.js +4 -3
  108. package/dist/src/tasks-runner/life-cycle.d.ts +7 -0
  109. package/dist/src/tasks-runner/life-cycle.js +7 -0
  110. package/dist/src/tasks-runner/life-cycles/summary-terminal-output-life-cycle.d.ts +35 -0
  111. package/dist/src/tasks-runner/life-cycles/summary-terminal-output-life-cycle.js +146 -0
  112. package/dist/src/tasks-runner/prune-task-graph.d.ts +17 -0
  113. package/dist/src/tasks-runner/prune-task-graph.js +45 -0
  114. package/dist/src/tasks-runner/run-command.js +14 -3
  115. package/dist/src/tasks-runner/running-tasks/batch-process.d.ts +93 -19
  116. package/dist/src/tasks-runner/running-tasks/batch-process.js +184 -88
  117. package/dist/src/tasks-runner/task-orchestrator.d.ts +64 -2
  118. package/dist/src/tasks-runner/task-orchestrator.js +232 -46
  119. package/dist/src/tasks-runner/terminal-output-path.d.ts +11 -0
  120. package/dist/src/tasks-runner/terminal-output-path.js +18 -0
  121. package/dist/src/tasks-runner/utils.js +5 -2
  122. package/dist/src/utils/ab-testing.d.ts +3 -0
  123. package/dist/src/utils/ab-testing.js +11 -3
  124. package/dist/src/utils/chunkify.d.ts +1 -7
  125. package/dist/src/utils/chunkify.js +2 -8
  126. package/dist/src/utils/command-line-utils.d.ts +19 -0
  127. package/dist/src/utils/command-line-utils.js +10 -3
  128. package/dist/src/utils/formatters/index.d.ts +6 -0
  129. package/dist/src/utils/formatters/index.js +5 -0
  130. package/dist/src/utils/formatters/prettier.d.ts +0 -8
  131. package/dist/src/utils/formatters/prettier.js +4 -26
  132. package/dist/src/utils/installed-nx-version.js +2 -48
  133. package/dist/src/utils/min-release-age/packument.js +1 -1
  134. package/dist/src/utils/output.d.ts +22 -3
  135. package/dist/src/utils/output.js +28 -5
  136. package/dist/src/utils/package-json.d.ts +25 -0
  137. package/dist/src/utils/package-json.js +41 -6
  138. package/dist/src/utils/package-manager.d.ts +6 -0
  139. package/dist/src/utils/package-manager.js +10 -0
  140. package/dist/src/utils/provenance.js +20 -4
  141. package/dist/src/utils/workspace-context.d.ts +47 -3
  142. package/dist/src/utils/workspace-context.js +162 -10
  143. package/package.json +12 -12
  144. package/schemas/nx-schema.json +4 -0
  145. package/schemas/project-schema.json +4 -0
  146. package/dist/src/daemon/server/file-watching/route-workspace-changes.d.ts +0 -9
  147. package/dist/src/daemon/server/file-watching/route-workspace-changes.js +0 -76
  148. package/dist/src/daemon/server/watcher.d.ts +0 -18
  149. package/dist/src/daemon/server/watcher.js +0 -126
@@ -7,7 +7,6 @@ const git_utils_1 = require("../../../utils/git-utils");
7
7
  const fileutils_1 = require("../../../utils/fileutils");
8
8
  const installation_directory_1 = require("../../../utils/installation-directory");
9
9
  const package_json_1 = require("../../../utils/package-json");
10
- const inception_1 = require("../agentic/inception");
11
10
  const print_dropped_agent_context_1 = require("../agentic/print-dropped-agent-context");
12
11
  const select_1 = require("../agentic/select");
13
12
  const types_1 = require("../agentic/types");
@@ -25,6 +24,8 @@ const state_lock_1 = require("./state-lock");
25
24
  const util_1 = require("./util");
26
25
  const text_1 = require("../text");
27
26
  const agent_output_1 = require("./agent-output");
27
+ const agent_work_payload_1 = require("./agent-work-payload");
28
+ const issues_1 = require("./issues");
28
29
  async function runSingleMigrationWorker(input) {
29
30
  const { root, runMigration, runId, commitPrefix, interactive, skipInstall, isVerbose, } = input;
30
31
  // The worker is a second CLI entry point: the run id reaches runDir() (where
@@ -317,22 +318,24 @@ async function runStandalone(root, migration, opts) {
317
318
  // and no "apply this manually" block for the user either.
318
319
  if ((0, migration_shape_1.isHybridMigration)(migration) && !skipAgentic) {
319
320
  emitOrPrintPrompt(root, migration, agentic.kind, {
320
- logs,
321
- changes,
322
- agentContext,
323
- }, resolvedCollection);
321
+ impl: {
322
+ logs,
323
+ changes,
324
+ agentContext,
325
+ },
326
+ resolvedCollection,
327
+ });
324
328
  }
325
329
  }
326
330
  async function runRecorded(root, runId, migration, skipInstall, isVerbose) {
327
331
  const dir = (0, run_state_1.runDir)(root, runId);
328
332
  // Version refusal (NewerRunStateFormatError) propagates.
329
333
  let state = (0, run_state_1.readRunState)(dir);
330
- // A recorded run never resolves the agentic flow (the outer agent drives
331
- // it); prompts are emitted for that agent or printed for a user hand-running
332
- // the dispensed command.
333
- const agenticKind = (0, inception_1.isInsideAgent)()
334
- ? 'inside-agent'
335
- : 'disabled';
334
+ // A recorded run never resolves the agentic flow: the outer agent drives it,
335
+ // and the dispensed command is part of the orchestrated protocol whoever
336
+ // re-runs it. Gating the block on ambient agent detection would park the
337
+ // step awaiting an outcome whose payload was never emitted.
338
+ const agenticKind = 'inside-agent';
336
339
  const migrationId = `${migration.package}:${migration.name}`;
337
340
  // The plan was read from the latest round's snapshot, so only that round's
338
341
  // step may match; a same-id step from an older round must not.
@@ -350,24 +353,71 @@ async function runRecorded(root, runId, migration, skipInstall, isVerbose) {
350
353
  startedAt: (0, util_1.nowIso)(),
351
354
  });
352
355
  // A prior attempt's generator half already ran, so this attempt must not
353
- // reapply it against a tree that already holds its changes: a hybrid
354
- // re-emits only its prompt, and a plain generator step has nothing left to
355
- // do but the install and commit its previous attempt failed on.
356
- // Read from the state the start transition returned: a delayed invocation
357
- // may have claimed a later attempt whose flag its entry snapshot predates.
356
+ // reapply it against a tree that already holds its changes. Read from the
357
+ // state the start transition returned, not the entry snapshot: a delayed
358
+ // invocation may have claimed a later attempt whose flag `step` predates.
358
359
  const startedStep = state.steps.find((s) => s.id === step.id);
359
360
  const generatorAlreadyCompleted = startedStep.generatorCompleted === true;
360
361
  // The run records its own install policy because dispensed worker commands
361
362
  // are re-invoked by the loop and never carry the user's flags; an explicit
362
363
  // --skip-install on this invocation still applies on top of it.
363
364
  const effectiveSkipInstall = state.skipInstall === true || skipInstall;
365
+ // The run records the resolved validation policy at init; absent (a state
366
+ // predating the field) falls back to the same default init applies.
367
+ const shouldValidate = state.validate !== false;
368
+ // Called before a retry hands the step's work back: the prompt or validation
369
+ // may need the dependencies the earlier attempt's generator added.
370
+ const reinstallFromBaseline = () => recordingInstallFailure(dir, step.id, () => (0, util_1.installDepsChangedSinceDispense)(root, dir, startedStep, effectiveSkipInstall, `${(0, execute_migration_1.formatSingleMigrationRerunCommand)(migrationId)} --run-id=${runId}`));
364
371
  let outcome;
372
+ let awaitingKind;
373
+ const payloadPath = (0, agent_work_payload_1.agentWorkPayloadPath)(dir, step.id, startedStep.attempt);
365
374
  try {
366
- if ((0, migration_shape_1.isPromptOnlyMigration)(migration) ||
367
- (generatorAlreadyCompleted && (0, migration_shape_1.isHybridMigration)(migration))) {
368
- emitOrPrintPrompt(root, migration, agenticKind);
375
+ if ((0, migration_shape_1.isPromptOnlyMigration)(migration)) {
376
+ emitOrPrintPrompt(root, migration, agenticKind, {
377
+ persistPath: payloadPath,
378
+ });
379
+ awaitingKind = 'migration-prompt';
380
+ }
381
+ else if (generatorAlreadyCompleted &&
382
+ startedStep.agenticWaived !== true &&
383
+ (0, migration_shape_1.isHybridMigration)(migration)) {
384
+ await reinstallFromBaseline();
385
+ const carried = (0, agent_work_payload_1.latestStoredAgentWorkPayload)(dir, step.id, startedStep.attempt, startedStep.generatorCompletedAtAttempt, {
386
+ migrationId,
387
+ kind: 'migration-prompt',
388
+ promptPath: migration.prompt,
389
+ });
390
+ if (carried) {
391
+ reemitCarriedAgentWork(migrationId, 'migration-prompt', payloadPath, carried);
392
+ }
393
+ else {
394
+ emitOrPrintPrompt(root, migration, agenticKind, {
395
+ persistPath: payloadPath,
396
+ });
397
+ }
398
+ awaitingKind = 'migration-prompt';
399
+ }
400
+ else if (generatorAlreadyCompleted &&
401
+ startedStep.validationOwed === true) {
402
+ // The persisted flag is the only record that these changes still owe a
403
+ // validation pass: the decision needed the generator result, which is
404
+ // gone with the attempt that made it, and a true waiver never writes the
405
+ // flag. The commit stays with the fold, as on a first attempt.
406
+ await reinstallFromBaseline();
407
+ const carried = (0, agent_work_payload_1.latestStoredAgentWorkPayload)(dir, step.id, startedStep.attempt, startedStep.generatorCompletedAtAttempt, { migrationId, kind: 'generator-validation' });
408
+ if (carried) {
409
+ reemitCarriedAgentWork(migrationId, 'generator-validation', payloadPath, carried);
410
+ }
411
+ else {
412
+ emitValidationBlock(root, migration, undefined, {
413
+ persistPath: payloadPath,
414
+ });
415
+ }
416
+ awaitingKind = 'generator-validation';
369
417
  }
370
418
  else if (generatorAlreadyCompleted) {
419
+ // Reached when the earlier attempt waived the AI step, or none was owed
420
+ // (no changes, validation off, or an older-nx state).
371
421
  state = await finishCompletedGenerator(dir, root, state, startedStep, migration, effectiveSkipInstall, runId);
372
422
  outcome = buildOutcome([], [], 'The generator ran in an earlier attempt; this attempt completed its install and commit.', root);
373
423
  }
@@ -375,22 +425,57 @@ async function runRecorded(root, runId, migration, skipInstall, isVerbose) {
375
425
  const installer = new execute_migration_1.ChangedDepInstaller(root, effectiveSkipInstall, `${(0, execute_migration_1.formatSingleMigrationRerunCommand)(migrationId)} --run-id=${runId}`);
376
426
  // Read once; the run and the hybrid documentation resolution share it.
377
427
  const resolvedCollection = (0, execute_migration_1.readMigrationCollection)(migration.package, root);
378
- const { changes, nextSteps, agentContext, logs, madeChanges } = await (0, execute_migration_1.runNxOrAngularMigration)(root, migration, isVerbose, (0, migration_shape_1.isHybridMigration)(migration), resolvedCollection);
428
+ const { changes, nextSteps, agentContext, skipAgentic, logs, madeChanges, } = await (0, execute_migration_1.runNxOrAngularMigration)(root, migration, isVerbose,
429
+ // Captured whenever an agent step may consume it: a hybrid's prompt
430
+ // payload, or the validation pass over the generator's changes.
431
+ (0, migration_shape_1.isHybridMigration)(migration) || shouldValidate, resolvedCollection);
432
+ // Mirrors the classic loop's waiver semantics (migrate.ts): the two must
433
+ // agree on when an AI step was owed for skipAgentic to waive.
434
+ const validationApplies = shouldValidate && !(0, migration_shape_1.isHybridMigration)(migration) && changes.length > 0;
435
+ const waivedAgenticStep = skipAgentic && ((0, migration_shape_1.isHybridMigration)(migration) || validationApplies);
436
+ const validationOwed = validationApplies && !waivedAgenticStep;
437
+ const promptOwed = (0, migration_shape_1.isHybridMigration)(migration) && !waivedAgenticStep;
379
438
  // Recorded before the commit is attempted: from here on the changes are
380
439
  // in the tree, so a failed install or commit must leave a retry with
381
- // only those left to do rather than running the generator again.
440
+ // only those left to do rather than running the generator again. The
441
+ // waiver and the owed validation ride along so that retry re-emits
442
+ // exactly the agent work this attempt decided was owed.
382
443
  state = transition(dir, {
383
444
  type: 'markGeneratorCompleted',
384
445
  stepId: step.id,
446
+ agenticWaived: waivedAgenticStep,
447
+ validationOwed,
448
+ madeChanges,
385
449
  });
386
- if (!(0, migration_shape_1.isHybridMigration)(migration)) {
450
+ if (waivedAgenticStep) {
451
+ (0, migrate_output_1.logWaivedAgenticStep)(migration, agentContext);
452
+ }
453
+ else if (!(0, migration_shape_1.isHybridMigration)(migration) && !validationApplies) {
387
454
  forwardDroppedAgentContext(migration, agentContext, agenticKind);
388
455
  }
456
+ // Stored before the install: a failed install fails the attempt, and
457
+ // the retry re-hands this payload instead of a context-free one.
458
+ const impl = { logs, changes, agentContext };
459
+ const owed = validationOwed
460
+ ? validationPayload(root, migration, impl, resolvedCollection)
461
+ : promptOwed
462
+ ? promptPayload(root, migration, impl, resolvedCollection)
463
+ : undefined;
464
+ if (owed) {
465
+ (0, agent_work_payload_1.persistAgentWorkPayload)(payloadPath, owed);
466
+ }
389
467
  const install = () => recordingInstallFailure(dir, step.id, () => installer.installDepsIfChanged());
390
468
  // Commits follow the run config, not CLI flags, and only when the
391
- // generator changed something: a no-op step must not create a commit (nor
392
- // a ledger entry) that absorbs prior pending diffs under its name.
393
- if (state.createCommits && madeChanges) {
469
+ // generator changed something: a no-op step's commit would absorb prior
470
+ // pending diffs under its name. A step handing work back defers its
471
+ // commit to the fold, so the migration lands as one commit and a failed
472
+ // hand-back leaves the changes uncommitted for review, as in the classic
473
+ // loop. The install still runs first: the agent may run tasks that need
474
+ // what the generator added.
475
+ if (state.createCommits &&
476
+ madeChanges &&
477
+ !validationOwed &&
478
+ !promptOwed) {
394
479
  state = await commitStepChanges(dir, root, state, step, migration, install);
395
480
  }
396
481
  else {
@@ -403,12 +488,13 @@ async function runRecorded(root, runId, migration, skipInstall, isVerbose) {
403
488
  (0, util_1.recordInstallLanded)(root, dir, step.id);
404
489
  }
405
490
  printNextSteps(migration, nextSteps);
406
- if ((0, migration_shape_1.isHybridMigration)(migration)) {
407
- emitOrPrintPrompt(root, migration, agenticKind, {
408
- logs,
409
- changes,
410
- agentContext,
411
- }, resolvedCollection);
491
+ if (validationOwed) {
492
+ emitValidationPayload(migrationId, owed);
493
+ awaitingKind = 'generator-validation';
494
+ }
495
+ else if (promptOwed) {
496
+ emitPromptForOuterAgent(migrationId, owed, true);
497
+ awaitingKind = 'migration-prompt';
412
498
  }
413
499
  else {
414
500
  outcome = buildOutcome(changes, nextSteps, migration.description, root);
@@ -426,14 +512,14 @@ async function runRecorded(root, runId, migration, skipInstall, isVerbose) {
426
512
  });
427
513
  throw e;
428
514
  }
429
- // A prompt half (prompt-only, or the prompt phase of a hybrid) is applied by
430
- // a separate actor, so the step parks in awaiting-prompt-outcome and this
431
- // process exits successfully.
432
- if ((0, migration_shape_1.isPromptOnlyMigration)(migration) || (0, migration_shape_1.isHybridMigration)(migration)) {
515
+ // Handed-back work is applied by a separate actor, so parking exits
516
+ // successfully rather than failing the step.
517
+ if (awaitingKind !== undefined) {
433
518
  transition(dir, {
434
519
  type: 'awaitPromptOutcome',
435
520
  stepId: step.id,
436
521
  finishedAt: (0, util_1.nowIso)(),
522
+ awaitingKind,
437
523
  });
438
524
  return;
439
525
  }
@@ -478,7 +564,10 @@ async function recordingInstallFailure(dir, stepId, install) {
478
564
  async function finishCompletedGenerator(dir, root, state, step, migration, skipInstall, runId) {
479
565
  const migrationId = `${migration.package}:${migration.name}`;
480
566
  const installDeps = () => recordingInstallFailure(dir, step.id, () => (0, util_1.installDepsChangedSinceDispense)(root, dir, step, skipInstall, `${(0, execute_migration_1.formatSingleMigrationRerunCommand)(migrationId)} --run-id=${runId}`));
481
- if (!state.createCommits) {
567
+ // Same no-op guard as the first attempt. Only an explicit false skips the
568
+ // commit: absent means an older nx wrote the marker without recording the
569
+ // answer, and the commit is kept as that version's retries did.
570
+ if (!state.createCommits || step.generatorMadeChanges === false) {
482
571
  await installDeps();
483
572
  return state;
484
573
  }
@@ -506,12 +595,14 @@ async function commitStepChanges(dir, root, state, step, migration, installDeps)
506
595
  const entry = (0, state_machine_1.commitResultToLedgerEntry)(result, step.id, absorbedStepIds);
507
596
  return entry ? appendCommit(dir, entry) : state;
508
597
  }
509
- // Appends a ledger entry to the freshest on-disk state under the lock. The git
510
- // commit itself already ran outside the lock; only this pure append is locked.
598
+ // The git commit itself already ran outside the lock; only this pure append is
599
+ // locked. A landed entry carries the resolved issues of every step it names,
600
+ // same as the orchestrator's fold and adopt appends: an absorbed step's
601
+ // resolutions would otherwise land unattached.
511
602
  function appendCommit(dir, entry) {
512
603
  return (0, state_lock_1.updateRunState)(dir, (fresh) => ({
513
604
  ...fresh,
514
- commits: [...fresh.commits, entry],
605
+ commits: [...fresh.commits, (0, issues_1.attachIssueIdsToCommitEntry)(fresh, entry)],
515
606
  }));
516
607
  }
517
608
  function buildOutcome(changes, nextSteps, description, root) {
@@ -598,32 +689,60 @@ function printNextSteps(migration, nextSteps) {
598
689
  bodyLines: nextSteps.map((line) => `- ${(0, text_1.singleLine)(line)}`),
599
690
  });
600
691
  }
601
- function emitOrPrintPrompt(root, migration, agenticKind, impl, resolvedCollection) {
692
+ function emitOrPrintPrompt(root, migration, agenticKind, opts = {}) {
602
693
  const migrationId = `${migration.package}:${migration.name}`;
603
- const promptPath = migration.prompt;
604
- const documentationPath = resolveDocumentationPath(root, migration, resolvedCollection);
605
694
  if (agenticKind === 'inside-agent') {
606
- emitPromptForOuterAgent(migrationId, promptPath, documentationPath, impl);
695
+ const payload = promptPayload(root, migration, opts.impl, opts.resolvedCollection);
696
+ if (opts.persistPath) {
697
+ (0, agent_work_payload_1.persistAgentWorkPayload)(opts.persistPath, payload);
698
+ }
699
+ emitPromptForOuterAgent(migrationId, payload, opts.persistPath !== undefined);
607
700
  }
608
701
  else {
609
- printPromptForUser(root, migration, promptPath, documentationPath);
702
+ printPromptForUser(root, migration, migration.prompt, resolveDocumentationPath(root, migration, opts.resolvedCollection));
610
703
  }
611
704
  }
612
- function emitPromptForOuterAgent(migrationId, promptPath, documentationPath, impl) {
613
- const payload = { migrationId, prompt: promptPath };
705
+ function promptPayload(root, migration, impl, resolvedCollection) {
706
+ const payload = {
707
+ migrationId: `${migration.package}:${migration.name}`,
708
+ prompt: migration.prompt,
709
+ };
710
+ const documentationPath = resolveDocumentationPath(root, migration, resolvedCollection);
614
711
  if (documentationPath)
615
712
  payload.documentationPath = documentationPath;
616
- if (impl) {
617
- payload.impl = {
618
- logs: impl.logs,
619
- changes: impl.changes.map((c) => ({ type: c.type, path: c.path })),
620
- ...(impl.agentContext.length > 0
621
- ? { agentContext: impl.agentContext }
622
- : {}),
623
- };
624
- }
713
+ if (impl)
714
+ payload.impl = implPayload(impl);
715
+ return payload;
716
+ }
717
+ const PROMPT_NOT_APPLIED = `The following prompt-based migration was not applied automatically.`;
718
+ // Recorded steps hand work back through the orchestrator, per the runbook's loop.
719
+ const RUN_NEXT_FIRST = `Run the dispensed "next" command first: its response restates this work and names the handoff file to write.`;
720
+ const CHANGES_AWAIT_VALIDATION = `The following migration's generator ran in an earlier attempt and its changes still await validation.`;
721
+ function emitPromptForOuterAgent(migrationId, payload, recorded) {
722
+ (0, agent_output_1.logToAgent)({
723
+ title: recorded
724
+ ? `${PROMPT_NOT_APPLIED} ${RUN_NEXT_FIRST}`
725
+ : `${PROMPT_NOT_APPLIED} Apply it to this workspace, then continue.`,
726
+ });
727
+ (0, agent_output_1.emitPromptBlock)(migrationId, payload);
728
+ }
729
+ function implPayload(impl) {
730
+ return {
731
+ logs: impl.logs,
732
+ changes: impl.changes.map((c) => ({ type: c.type, path: c.path })),
733
+ ...(impl.agentContext.length > 0
734
+ ? { agentContext: impl.agentContext }
735
+ : {}),
736
+ };
737
+ }
738
+ // Re-hands what an earlier attempt stored. The payload is re-persisted under
739
+ // this attempt because the dispense reads only the current attempt's file.
740
+ function reemitCarriedAgentWork(migrationId, kind, payloadPath, payload) {
741
+ (0, agent_work_payload_1.persistAgentWorkPayload)(payloadPath, payload);
625
742
  (0, agent_output_1.logToAgent)({
626
- title: `The following prompt-based migration was not applied automatically. Apply it to this workspace, then continue.`,
743
+ title: `${kind === 'migration-prompt'
744
+ ? PROMPT_NOT_APPLIED
745
+ : CHANGES_AWAIT_VALIDATION} ${RUN_NEXT_FIRST}`,
627
746
  });
628
747
  (0, agent_output_1.emitPromptBlock)(migrationId, payload);
629
748
  }
@@ -660,6 +779,37 @@ function printPromptForUser(root, migration, promptPath, documentationPath) {
660
779
  bodyLines,
661
780
  });
662
781
  }
782
+ // `impl` is absent on a retry whose earlier attempt left no stored payload;
783
+ // the emission then points at the tree.
784
+ function emitValidationBlock(root, migration, impl, opts = {}) {
785
+ const payload = validationPayload(root, migration, impl, opts.resolvedCollection);
786
+ if (opts.persistPath) {
787
+ (0, agent_work_payload_1.persistAgentWorkPayload)(opts.persistPath, payload);
788
+ }
789
+ emitValidationPayload(`${migration.package}:${migration.name}`, payload);
790
+ }
791
+ function emitValidationPayload(migrationId, payload) {
792
+ (0, agent_output_1.logToAgent)({
793
+ title: payload.impl !== undefined
794
+ ? `The following migration's generator ran without an AI-driven part. ${RUN_NEXT_FIRST}`
795
+ : `${CHANGES_AWAIT_VALIDATION} ${RUN_NEXT_FIRST} Inspect the changes with git.`,
796
+ });
797
+ (0, agent_output_1.emitPromptBlock)(migrationId, payload);
798
+ }
799
+ function validationPayload(root, migration, impl, resolvedCollection) {
800
+ // `kind` is what tells the block apart from an applied-prompt payload,
801
+ // which carries `prompt` instead.
802
+ const payload = {
803
+ migrationId: `${migration.package}:${migration.name}`,
804
+ kind: 'generator-validation',
805
+ };
806
+ const documentationPath = resolveDocumentationPath(root, migration, resolvedCollection);
807
+ if (documentationPath)
808
+ payload.documentationPath = documentationPath;
809
+ if (impl)
810
+ payload.impl = implPayload(impl);
811
+ return payload;
812
+ }
663
813
  // Non-fatal: documentation is supplementary, so a failure warns and the
664
814
  // prompt still runs without it.
665
815
  function resolveDocumentationPath(root, migration, resolvedCollection) {
@@ -249,7 +249,11 @@ function utmContentForCommand(command) {
249
249
  async function nxCloudPrompt(key, utmContent) {
250
250
  const { message, choices, initial, footer, hint } = ab_testing_1.messages.getPrompt(key);
251
251
  // No separate footer/hint slot, so both are folded into the message.
252
- const suffix = [hint, footer && `${footer} ${(0, ab_testing_1.nxCloudHyperlink)(utmContent)}`]
252
+ const suffix = [
253
+ hint,
254
+ footer && `${footer} ${(0, ab_testing_1.nxCloudHyperlink)(utmContent)}`,
255
+ `See it in action: ${(0, ab_testing_1.nxCloudDemoHyperlink)(utmContent)}`,
256
+ ]
253
257
  .filter(Boolean)
254
258
  .map((t) => pc.dim(t));
255
259
  return (await (0, prompt_helpers_1.selectPrompt)({
@@ -207,15 +207,22 @@ async function runPublishOnProjects(args, projectGraph, nxJson, projectNames, ex
207
207
  * NOTE: Force TUI to be disabled for now.
208
208
  */
209
209
  process.env.NX_TUI = 'false';
210
+ const publishOutputStyle = args.specifiedOutputStyle ?? 'static';
210
211
  const { taskResults } = await (0, run_command_1.runCommandForTasks)(projectsWithTarget, projectGraph, { nxJson }, {
211
212
  targets: [requiredTargetName],
213
+ ...args,
212
214
  // Everything this command reports — the registry, the tag, the
213
215
  // package.json diff, the dry-run summary — is printed from inside the
214
216
  // task, so the failures-only default would swallow all of it (under
215
- // --dry-run every task succeeds by definition). An explicit
216
- // --output-style still wins, since it comes in through the spread.
217
- outputStyle: 'static',
218
- ...args,
217
+ // --dry-run every task succeeds by definition). Set after the spread and
218
+ // on all three fields: the middleware always writes `resolvedOutputStyle`,
219
+ // so a value placed before it would be overwritten on every run, and the
220
+ // renderer reads the new fields rather than the deprecated one. A style
221
+ // the user actually named still wins, which is what `specifiedOutputStyle`
222
+ // being set means.
223
+ outputStyle: publishOutputStyle,
224
+ specifiedOutputStyle: publishOutputStyle,
225
+ resolvedOutputStyle: publishOutputStyle,
219
226
  // It is possible for workspaces to have circular dependencies between packages and still release them to a registry
220
227
  nxIgnoreCycles: true,
221
228
  }, overrides, null, {}, extraOptions);
@@ -227,14 +227,14 @@ class GithubRemoteReleaseClient extends remote_release_client_1.RemoteReleaseCli
227
227
  `---`,
228
228
  `Request Data:`,
229
229
  `Repo: ${this.getRemoteRepoData()?.slug}`,
230
- `Token Header Data: ${this.tokenHeader}`,
230
+ `Token Header: ${this.getRedactedTokenHeader()}`,
231
231
  `Body: ${JSON.stringify(result.requestData)}`,
232
232
  ],
233
233
  });
234
234
  }
235
235
  else {
236
- console.log(error);
237
- console.error(`An unknown error occurred while trying to create a release on GitHub, please report this on https://github.com/nrwl/nx (NOTE: make sure to redact your GitHub token from the error message!)`);
236
+ console.log(this.inspectWithRedactedToken(error));
237
+ console.error(`An unknown error occurred while trying to create a release on GitHub, please report this on https://github.com/nrwl/nx (NOTE: your GitHub token is redacted above, but please double-check before sharing)`);
238
238
  }
239
239
  }
240
240
  const shouldContinueInGitHub = await this.promptForContinueInGitHub();
@@ -143,14 +143,14 @@ class GitLabRemoteReleaseClient extends remote_release_client_1.RemoteReleaseCli
143
143
  `---`,
144
144
  `Request Data:`,
145
145
  `Repo: ${this.getRemoteRepoData()?.slug}`,
146
- `Token Header Data: ${this.tokenHeader}`,
146
+ `Token Header: ${this.getRedactedTokenHeader()}`,
147
147
  `Body: ${JSON.stringify(result.requestData)}`,
148
148
  ],
149
149
  });
150
150
  }
151
151
  else {
152
- console.log(error);
153
- console.error(`An unknown error occurred while trying to create a release on GitLab, please report this on https://github.com/nrwl/nx (NOTE: make sure to redact your GitLab token from the error message!)`);
152
+ console.log(this.inspectWithRedactedToken(error));
153
+ console.error(`An unknown error occurred while trying to create a release on GitLab, please report this on https://github.com/nrwl/nx (NOTE: your GitLab token is redacted above, but please double-check before sharing)`);
154
154
  }
155
155
  }
156
156
  const shouldContinueInGitLab = await this.promptForContinueInGitLab();
@@ -44,6 +44,8 @@ export declare abstract class RemoteReleaseClient<RemoteRelease extends Record<s
44
44
  headerName: string;
45
45
  } | null);
46
46
  getRemoteRepoData<T extends RemoteRepoData>(): T | null;
47
+ protected inspectWithRedactedToken(error: unknown): string;
48
+ protected getRedactedTokenHeader(): string;
47
49
  /**
48
50
  * Create a post git task that will be executed by nx release changelog after performing any relevant
49
51
  * git operations, if the user has opted into remote release creation.
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RemoteReleaseClient = void 0;
4
4
  exports.createRemoteReleaseClient = createRemoteReleaseClient;
5
5
  const tslib_1 = require("tslib");
6
+ const node_util_1 = require("node:util");
6
7
  const axios_1 = tslib_1.__importDefault(require("axios"));
7
8
  const handle_import_1 = require("../../../../utils/handle-import");
8
9
  const print_changes_1 = require("../print-changes");
@@ -30,6 +31,40 @@ class RemoteReleaseClient {
30
31
  getRemoteRepoData() {
31
32
  return this.remoteRepoData;
32
33
  }
34
+ inspectWithRedactedToken(error) {
35
+ const inspected = (0, node_util_1.inspect)(error);
36
+ // The dump can hold a different string than the raw token: axios trims
37
+ // header values and inspect() escapes the result, while message/stack keep
38
+ // it bare. Redact every rendering; the Set collapses them for an ordinary
39
+ // token. The CR/LF needle is belt-and-braces — node rejects such headers.
40
+ const token = this.tokenData?.token?.trim();
41
+ if (!token) {
42
+ return inspected;
43
+ }
44
+ const needles = new Set([
45
+ token,
46
+ token.replace(/[\r\n]/g, ''),
47
+ (0, node_util_1.inspect)(token).slice(1, -1),
48
+ ]);
49
+ // inspect() renders a long string carrying a line break as concatenated
50
+ // per-line chunks, which no whole-token needle spans. Redact the lines too,
51
+ // skipping fragments short enough to collide with ordinary dump text.
52
+ for (const line of token.split(/[\r\n]+/)) {
53
+ if (line.length >= 8) {
54
+ needles.add(line);
55
+ }
56
+ }
57
+ return [...needles].reduce((text, needle) => text.split(needle).join('<redacted>'), inspected);
58
+ }
59
+ getRedactedTokenHeader() {
60
+ if (!this.tokenData) {
61
+ return 'none';
62
+ }
63
+ const { headerName } = this.tokenData;
64
+ return headerName === 'Authorization'
65
+ ? `${headerName}: Bearer <redacted>`
66
+ : `${headerName}: <redacted>`;
67
+ }
33
68
  /**
34
69
  * Make an (optionally authenticated) API request to the remote release provider
35
70
  */
@@ -67,7 +67,7 @@ export declare function withOverrides<T extends {
67
67
  }>(args: T, commandLevel?: number): T & {
68
68
  __overrides_unparsed__: string[];
69
69
  };
70
- declare const allOutputStyles: readonly ['tui', 'dynamic', 'dynamic-legacy', 'static', 'static-failures-only', 'stream', 'stream-without-prefixes'];
70
+ declare const allOutputStyles: readonly ['tui', 'dynamic', 'dynamic-legacy', 'static', 'static-failures-only', 'summary', 'stream', 'stream-without-prefixes'];
71
71
  export type OutputStyle = (typeof allOutputStyles)[number];
72
72
  export declare function withOutputStyleOption<T>(yargs: Argv<T>, choices?: ReadonlyArray<OutputStyle>): Argv<T & {
73
73
  outputStyle: OutputStyle;
@@ -17,6 +17,7 @@ exports.parseNewlines = parseNewlines;
17
17
  exports.parseCSV = parseCSV;
18
18
  exports.readParallelFromArgsAndEnv = readParallelFromArgsAndEnv;
19
19
  const nx_json_1 = require("../../config/nx-json");
20
+ const native_1 = require("../../native");
20
21
  const is_tui_enabled_1 = require("../../tasks-runner/is-tui-enabled");
21
22
  const node_os_1 = require("node:os");
22
23
  exports.defaultYargsParserConfiguration = {
@@ -273,6 +274,7 @@ const allOutputStyles = [
273
274
  'dynamic-legacy',
274
275
  'static',
275
276
  'static-failures-only',
277
+ 'summary',
276
278
  'stream',
277
279
  'stream-without-prefixes',
278
280
  ];
@@ -282,12 +284,13 @@ function withOutputStyleOption(yargs, choices = [
282
284
  'tui',
283
285
  'static',
284
286
  'static-failures-only',
287
+ 'summary',
285
288
  'stream',
286
289
  'stream-without-prefixes',
287
290
  ]) {
288
291
  return yargs
289
292
  .option('outputStyle', {
290
- describe: `Defines how Nx emits outputs tasks logs. **tui**: enables the Nx Terminal UI, recommended for local development environments. **dynamic-legacy**: use dynamic-legacy output life cycle, previous content is overwritten or modified as new outputs are added, display minimal logs by default, always show errors. This output format is recommended for local development environments where tui is not supported. **static**: uses static output life cycle, no previous content is rewritten or modified as new outputs are added, and every task prints its full output regardless of status. **static-failures-only**: same as **static**, but successful and cached tasks collapse to a single line, so only failing tasks (and, for a single-project run, the project you asked for) print their full output. This is what Nx uses by default in CI and other non-interactive environments. **stream**: nx by default logs output to an internal output stream, enable this option to stream logs to stdout / stderr. **stream-without-prefixes**: nx prefixes the project name the target is running on, use this option remove the project name prefix from output.`,
293
+ describe: `Defines how Nx emits outputs tasks logs. **tui**: enables the Nx Terminal UI, recommended for local development environments. **dynamic-legacy**: use dynamic-legacy output life cycle, previous content is overwritten or modified as new outputs are added, display minimal logs by default, always show errors. This output format is recommended for local development environments where tui is not supported. **static**: uses static output life cycle, no previous content is rewritten or modified as new outputs are added, and every task prints its full output regardless of status. **static-failures-only**: same as **static**, but successful and cached tasks collapse to a single line, so only failing tasks (and, for a single-project run, the project you asked for) print their full output. This is what Nx uses by default in CI and other non-interactive environments. **summary**: prints only run counts and one line per failing task, naming the file on disk that holds its full output. No task output is printed inline, so the size of a run's output does not depend on how much its tasks logged. This is the default when Nx detects it is being driven by an AI agent. **stream**: nx by default logs output to an internal output stream, enable this option to stream logs to stdout / stderr. **stream-without-prefixes**: nx prefixes the project name the target is running on, use this option remove the project name prefix from output.`,
291
294
  type: 'string',
292
295
  choices,
293
296
  })
@@ -298,16 +301,31 @@ function withOutputStyleOption(yargs, choices = [
298
301
  choices.includes(process.env.NX_DEFAULT_OUTPUT_STYLE)) {
299
302
  args.outputStyle = process.env.NX_DEFAULT_OUTPUT_STYLE;
300
303
  }
304
+ // Everything above this line is the user naming a style; everything
305
+ // below infers one. Splitting them here is what lets the rest of the
306
+ // codebase ask either question without re-deriving it from a single
307
+ // overloaded field.
308
+ args.specifiedOutputStyle = args.outputStyle;
301
309
  },
302
310
  (args) => {
311
+ // Resolution, in precedence order. `shouldUseTui` reads
312
+ // `specifiedOutputStyle`, so it sees only what the user named and is
313
+ // not fed a value inferred here - which is what used to make this
314
+ // ordering circular. It also reads `NX_TUI` before its own agent check,
315
+ // so an explicit opt-in beats the agent default without needing a
316
+ // special case: agent detection is ambient (`REPL_ID` is exported into
317
+ // a human's shell), and a human asking for a renderer outranks it.
303
318
  const useTui = (0, is_tui_enabled_1.shouldUseTui)((0, nx_json_1.readNxJson)(), args);
319
+ process.env.NX_TUI = useTui.toString();
320
+ const resolved = args.outputStyle ??
321
+ (useTui ? 'tui' : (0, native_1.isAiAgent)() ? 'summary' : 'static-failures-only');
322
+ args.resolvedOutputStyle = resolved;
323
+ // `outputStyle` stays what yargs parsed, except for the TUI, whose
324
+ // `check` runs after kebab-case normalization and needs both spellings.
304
325
  if (useTui) {
305
- // We have to set both of these because `check` runs after the normalization that
306
- // handles the kebab-case'd args -> camelCase'd args translation.
307
326
  args['output-style'] = 'tui';
308
327
  args.outputStyle = 'tui';
309
328
  }
310
- process.env.NX_TUI = useTui.toString();
311
329
  },
312
330
  ]);
313
331
  }
@@ -182,9 +182,11 @@ export type InputDefinition = {
182
182
  input: string;
183
183
  } | {
184
184
  fileset: string;
185
+ includeIgnored?: boolean;
185
186
  } | {
186
187
  fileset: string;
187
188
  dependencies: true;
189
+ includeIgnored?: boolean;
188
190
  } | {
189
191
  runtime: string;
190
192
  } | {