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
@@ -0,0 +1,442 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runAgentic = runAgentic;
4
+ exports.adaptSpawnForWindowsShim = adaptSpawnForWindowsShim;
5
+ const child_process_1 = require("child_process");
6
+ const path_1 = require("path");
7
+ const output_1 = require("../../../utils/output");
8
+ const safe_prompt_1 = require("../safe-prompt");
9
+ const handoff_1 = require("./handoff");
10
+ // How long to wait for the agent to exit gracefully after sending SIGINT once
11
+ // a valid handoff has been written. Long enough for an interactive agent to
12
+ // finish its current render and clean up; short enough that a frozen child
13
+ // still gets escalated to SIGTERM in a sensible time.
14
+ const AGENT_GRACEFUL_EXIT_MS = 5_000;
15
+ /**
16
+ * Spawns the selected agent with `stdio: 'inherit'`, swallows SIGINT while the
17
+ * child is alive, waits for it to exit, and resolves the run's outcome from
18
+ * the handoff file (or the user when the file is missing).
19
+ */
20
+ async function runAgentic(args) {
21
+ const { detected, definition, invocationContext, handoffFilePath, handoffPollIntervalMs, gracefulExitMs = AGENT_GRACEFUL_EXIT_MS, forceKillWaitMs = FORCE_KILL_WAIT_MS, } = args;
22
+ const spec = definition.buildInteractive(invocationContext);
23
+ const adapted = adaptSpawnForWindowsShim(detected.binary, spec.args, {
24
+ stdio: 'inherit',
25
+ cwd: spec.cwd ?? invocationContext.workspaceRoot,
26
+ env: spec.env ? { ...process.env, ...spec.env } : process.env,
27
+ windowsHide: true,
28
+ });
29
+ let child;
30
+ // Local alias so `@nx/workspace-require-windows-hide` recognizes the
31
+ // options arg as a tracked Identifier rather than giving up on a
32
+ // member-expression skip — keeps the lint rule strict on other call sites.
33
+ const spawnOptions = adapted.options;
34
+ try {
35
+ child = (0, child_process_1.spawn)(adapted.binary, adapted.args, spawnOptions);
36
+ }
37
+ catch (err) {
38
+ return resolveFromHandoffOrPrompt(handoffFilePath, false, {
39
+ spawnError: err instanceof Error ? err.message : String(err),
40
+ });
41
+ }
42
+ // Counts SIGINTs while the agent runs. Non-zero after exit means the
43
+ // user pressed Ctrl+C → skip the ambiguous abort/continue prompt, because
44
+ // enquirer misbehaves in the TTY state left by a SIGINT-killed child
45
+ // (ERR_USE_AFTER_CLOSE, setRawMode EIO) and the errors surface from async
46
+ // chains that `await` cannot catch.
47
+ let userInterruptCount = 0;
48
+ const swallowSigint = () => {
49
+ userInterruptCount++;
50
+ };
51
+ process.on('SIGINT', swallowSigint);
52
+ const handoffWatchAbort = new AbortController();
53
+ const exitPromise = waitForExit(child);
54
+ const handoffPromise = (0, handoff_1.waitForValidHandoff)(handoffFilePath, {
55
+ signal: handoffWatchAbort.signal,
56
+ intervalMs: handoffPollIntervalMs,
57
+ });
58
+ let exitInfo = {};
59
+ try {
60
+ const winner = await Promise.race([
61
+ exitPromise.then((info) => {
62
+ exitInfo = info;
63
+ return 'exit';
64
+ }),
65
+ // Rejection handler swallows the abort-triggered rejection from the
66
+ // `finally` block after the race has settled — otherwise Node logs it
67
+ // as an unhandled rejection.
68
+ handoffPromise.then(() => 'handoff', () => 'exit'),
69
+ ]);
70
+ if (winner === 'handoff') {
71
+ await closeAgentSession(child, exitPromise, gracefulExitMs, forceKillWaitMs);
72
+ // Extra safety bound so the orchestrator can't hang if the child
73
+ // stays stuck after SIGKILL / taskkill (e.g. D-state on a hung NFS
74
+ // read, or taskkill returning before the process actually exits).
75
+ await raceWithTimeout(exitPromise, forceKillWaitMs);
76
+ }
77
+ }
78
+ finally {
79
+ handoffWatchAbort.abort();
80
+ process.removeListener('SIGINT', swallowSigint);
81
+ restoreTermiosAfterAgent();
82
+ }
83
+ return resolveFromHandoffOrPrompt(handoffFilePath, userInterruptCount > 0, exitInfoToCause(exitInfo));
84
+ }
85
+ function exitInfoToCause(info) {
86
+ const cause = {};
87
+ // Include `code === 0` so the prompt can distinguish "agent exited cleanly
88
+ // without writing a handoff" (likely the user closed the agent on purpose,
89
+ // or the agent terminated without invoking the handoff step) from "agent
90
+ // was killed before it could write" (signal) or "agent crashed" (non-zero
91
+ // code). Without this distinction every clean-exit-no-handoff collapses
92
+ // into the same uninformative ambiguous-prompt body.
93
+ if (info.code !== undefined && info.code !== null) {
94
+ cause.exitCode = info.code;
95
+ }
96
+ if (info.signal)
97
+ cause.exitSignal = info.signal;
98
+ if (info.error)
99
+ cause.exitError = info.error.message;
100
+ return cause;
101
+ }
102
+ function restoreTermiosAfterAgent() {
103
+ if (process.platform === 'win32')
104
+ return;
105
+ if (!process.stdin.isTTY)
106
+ return;
107
+ try {
108
+ // `stty sane` resets termios to a known cooked state via the kernel —
109
+ // independent of Node's libuv mode tracking (Node's setRawMode(false)
110
+ // short-circuits when libuv's per-handle mode is already NORMAL, even
111
+ // if the OS-level termios was changed out-of-band by the agent).
112
+ (0, child_process_1.execSync)('stty sane < /dev/tty', {
113
+ stdio: ['ignore', 'ignore', 'ignore'],
114
+ windowsHide: true,
115
+ });
116
+ // Carriage-return + clear to end of screen, to wipe any agent TUI
117
+ // cells below our row that subsequent log lines won't overwrite
118
+ // (e.g. a status footer past where our text wraps).
119
+ process.stdout.write('\r\x1B[J');
120
+ }
121
+ catch {
122
+ // best-effort — if stty isn't on PATH or /dev/tty isn't accessible,
123
+ // the worst case is the pre-existing staircase + cell-bleed output.
124
+ }
125
+ }
126
+ // Safety bound after force-kill. SIGKILL normally reaps in microseconds;
127
+ // the bound exists for uninterruptible kernel calls or taskkill returning
128
+ // before the process actually exits.
129
+ const FORCE_KILL_WAIT_MS = 500;
130
+ /**
131
+ * Stops the agent process after a successful handoff. Platform-branched:
132
+ *
133
+ * - POSIX: SIGINT (graceful, equivalent to user Ctrl+C) → wait
134
+ * `gracefulExitMs` for the child to exit → SIGKILL → wait
135
+ * `FORCE_KILL_WAIT_MS` (bounded) → return. SIGTERM is intentionally
136
+ * skipped: a process that ignores SIGINT for 5s will hit the same
137
+ * handler on SIGTERM, the extra step only delays the inevitable.
138
+ *
139
+ * - Windows: skip SIGINT entirely. `child.kill('*')` on Windows is a
140
+ * `TerminateProcess` call regardless of the signal name (Windows has
141
+ * no POSIX signals), and on the `cmd.exe /d /s /c "..."` shim path it
142
+ * would terminate cmd.exe while leaving the agent orphaned (parent
143
+ * death doesn't cascade to children on Windows). `taskkill /T /F`
144
+ * walks the process tree and kills cmd.exe AND the agent atomically;
145
+ * that's the only reliable shutdown path here. `taskkill` failures
146
+ * (binary missing, race with already-dead pid) are swallowed; the
147
+ * safety bound returns regardless.
148
+ */
149
+ async function closeAgentSession(child, exitPromise, gracefulExitMs, forceKillWaitMs) {
150
+ if (child.exitCode !== null || child.signalCode !== null)
151
+ return;
152
+ if (process.platform === 'win32') {
153
+ await forceKillWindowsTree(child, exitPromise, forceKillWaitMs);
154
+ return;
155
+ }
156
+ // POSIX path.
157
+ try {
158
+ child.kill('SIGINT');
159
+ }
160
+ catch {
161
+ // child already gone between the check above and here
162
+ return;
163
+ }
164
+ let escalation;
165
+ try {
166
+ await Promise.race([
167
+ exitPromise,
168
+ new Promise((resolve) => {
169
+ escalation = setTimeout(resolve, gracefulExitMs);
170
+ }),
171
+ ]);
172
+ }
173
+ finally {
174
+ if (escalation)
175
+ clearTimeout(escalation);
176
+ }
177
+ if (child.exitCode !== null || child.signalCode !== null)
178
+ return;
179
+ // Graceful timeout elapsed without the agent exiting. SIGKILL is
180
+ // uncatchable; bound the post-kill wait so a pathological uninterruptible
181
+ // syscall can't hang us forever.
182
+ try {
183
+ child.kill('SIGKILL');
184
+ }
185
+ catch {
186
+ /* child already gone */
187
+ }
188
+ await raceWithTimeout(exitPromise, forceKillWaitMs);
189
+ }
190
+ async function forceKillWindowsTree(child, exitPromise, forceKillWaitMs) {
191
+ const pid = child.pid;
192
+ // `child.pid` is undefined only when spawn itself failed; the early-return
193
+ // guard in `closeAgentSession` should short-circuit that path. Reaching
194
+ // here without a pid means a narrow race between handoff-detection and
195
+ // error-event propagation — without a pid we can't taskkill, so wait
196
+ // briefly and return.
197
+ if (pid !== undefined) {
198
+ try {
199
+ (0, child_process_1.execSync)(`taskkill /T /F /PID ${pid}`, {
200
+ stdio: 'ignore',
201
+ windowsHide: true,
202
+ // Bound so a hung Windows shell can't block the orchestrator.
203
+ timeout: 2_000,
204
+ });
205
+ }
206
+ catch {
207
+ /* taskkill missing, pid already dead, or timed out — fall through */
208
+ }
209
+ }
210
+ await raceWithTimeout(exitPromise, forceKillWaitMs);
211
+ }
212
+ async function raceWithTimeout(promise, timeoutMs) {
213
+ let timer;
214
+ try {
215
+ await Promise.race([
216
+ promise,
217
+ new Promise((resolve) => {
218
+ timer = setTimeout(resolve, timeoutMs);
219
+ }),
220
+ ]);
221
+ }
222
+ finally {
223
+ if (timer)
224
+ clearTimeout(timer);
225
+ }
226
+ }
227
+ // Merge window so a paired exit + error both land in the same ExitInfo
228
+ // (e.g. error from IPC followed by exit when the process actually
229
+ // terminates). For error-only paths like spawn ENOENT — where Node fires
230
+ // error but never exit — this timer is the SOLE settlement mechanism.
231
+ const EXIT_MERGE_WINDOW_MS = 10;
232
+ function waitForExit(child) {
233
+ return new Promise((resolve) => {
234
+ const info = {};
235
+ let pending = null;
236
+ let settled = false;
237
+ const settle = () => {
238
+ if (settled)
239
+ return;
240
+ settled = true;
241
+ if (pending)
242
+ clearTimeout(pending);
243
+ resolve(info);
244
+ };
245
+ const onFirst = () => {
246
+ if (settled || pending)
247
+ return;
248
+ pending = setTimeout(settle, EXIT_MERGE_WINDOW_MS);
249
+ };
250
+ child.on('exit', (code, signal) => {
251
+ info.code = code;
252
+ info.signal = signal;
253
+ onFirst();
254
+ });
255
+ // `error` fires when spawn itself fails (e.g. binary disappeared between
256
+ // detection and run) OR alongside `exit` when the process started but
257
+ // emitted an error event later. Treat both as exit with no handoff so
258
+ // the ambiguous flow kicks in; field-merge so we don't drop the loser's
259
+ // contribution when both fire.
260
+ child.on('error', (error) => {
261
+ info.error = error;
262
+ onFirst();
263
+ });
264
+ });
265
+ }
266
+ async function resolveFromHandoffOrPrompt(handoffFilePath, userInterrupted = false, cause = {}) {
267
+ const read = (0, handoff_1.readHandoffWithReason)(handoffFilePath);
268
+ if (read.ok === true) {
269
+ return {
270
+ kind: read.handoff.status,
271
+ summary: read.handoff.summary,
272
+ extras: read.handoff.extras,
273
+ };
274
+ }
275
+ const fullCause = {
276
+ ...cause,
277
+ handoff: { reason: read.reason, detail: read.detail },
278
+ };
279
+ if (userInterrupted) {
280
+ // User pressed Ctrl+C. Don't show the abort/continue prompt — they
281
+ // already told us what they want, and the TTY state after a
282
+ // SIGINT-killed child trips enquirer's setRawMode-EIO and
283
+ // ERR_USE_AFTER_CLOSE bugs. The orchestrator's standard failure
284
+ // cascade surfaces the abort outcome.
285
+ //
286
+ // Forward the underlying cause as pre-rendered summary lines so the
287
+ // caller can log it before "Aborted by user" — a Ctrl+C that masked
288
+ // a SEPARATE crash still needs to show the user what crashed. Scrub
289
+ // fields that are just the Ctrl+C itself reverberating: exit code
290
+ // 130 (SIGINT) / 143 (SIGTERM from our escalation) and signals
291
+ // SIGINT / SIGTERM reflect the user's own keystroke (and our
292
+ // graceful-exit handling of it); surfacing them as "agent crashed"
293
+ // would be noise. Anything else — code 1, code 137 (OOM), an
294
+ // unrelated signal — is a separate diagnostic worth keeping. Note
295
+ // that `spawnError` is structurally impossible here: the spawn-throw
296
+ // path returns directly without registering the SIGINT listener, so
297
+ // `userInterrupted` can never be true on that branch.
298
+ const exitWasCtrlC = cause.exitCode === 130 ||
299
+ cause.exitCode === 143 ||
300
+ cause.exitSignal === 'SIGINT' ||
301
+ cause.exitSignal === 'SIGTERM';
302
+ const userScrubbed = {
303
+ exitError: cause.exitError,
304
+ exitCode: exitWasCtrlC ? undefined : cause.exitCode,
305
+ exitSignal: exitWasCtrlC ? undefined : cause.exitSignal,
306
+ handoff: read.reason === 'missing'
307
+ ? undefined
308
+ : { reason: read.reason, detail: read.detail },
309
+ };
310
+ const causeSummary = describeAmbiguousCause(userScrubbed);
311
+ return {
312
+ kind: 'ambiguous-abort',
313
+ ...(causeSummary.length > 0 && { causeSummary }),
314
+ };
315
+ }
316
+ return promptAmbiguous(fullCause);
317
+ }
318
+ /**
319
+ * Node's `spawn` cannot directly execute `.cmd` / `.bat` shims on Windows;
320
+ * `which` resolves to those when an agent was installed via npm. Wrap them in
321
+ * a `cmd.exe /d /s /c` invocation with `windowsVerbatimArguments` so quoting
322
+ * follows the cmd.exe convention rather than Node's default cooking.
323
+ *
324
+ * On non-Windows or for non-shim binaries this is a passthrough.
325
+ */
326
+ function adaptSpawnForWindowsShim(binary, args, options) {
327
+ if (process.platform !== 'win32') {
328
+ return { binary, args: [...args], options };
329
+ }
330
+ const ext = (0, path_1.extname)(binary).toLowerCase();
331
+ if (ext !== '.cmd' && ext !== '.bat') {
332
+ return { binary, args: [...args], options };
333
+ }
334
+ const cmdLine = [escapeCmdCommand(binary), ...args.map(escapeCmdArg)].join(' ');
335
+ return {
336
+ binary: process.env.comspec || 'cmd.exe',
337
+ // Outer pair of quotes is required so cmd.exe /c does not strip the inner
338
+ // quotes around the binary path.
339
+ args: ['/d', '/s', '/c', `"${cmdLine}"`],
340
+ options: { ...options, windowsVerbatimArguments: true },
341
+ };
342
+ }
343
+ const CMD_META_CHARS = /([()\][%!^"`<>&|;, ])/g;
344
+ // Backslash-escape embedded quotes per MS C runtime convention, wrap in
345
+ // quotes, then caret-escape cmd.exe metacharacters.
346
+ function escapeCmdArg(arg) {
347
+ const quoted = `"${arg
348
+ .replace(/(\\*)"/g, '$1$1\\"')
349
+ .replace(/(\\*)$/, '$1$1')}"`;
350
+ return quoted.replace(CMD_META_CHARS, '^$1');
351
+ }
352
+ function escapeCmdCommand(arg) {
353
+ // cmd.exe interprets the command portion through an extra parsing pass;
354
+ // apply the caret-escape twice so the .cmd shim sees the original.
355
+ return escapeCmdArg(arg).replace(CMD_META_CHARS, '^$1');
356
+ }
357
+ async function promptAmbiguous(cause) {
358
+ // stderr blank line so the spacer lands in the same stream as output.warn
359
+ // and the enquirer prompt — buffered stdout could otherwise reorder it
360
+ // and glue the prompt to the agent's trailing exit message.
361
+ process.stderr.write('\n');
362
+ // Cause rendered ABOVE the prompt rather than inlined into prompt.message:
363
+ // enquirer's select redraw uses different math for clear (wrap-aware) vs
364
+ // restore (raw \n-split count), so a multi-line message can leave orphaned
365
+ // cells on arrow-key re-renders. Keeping message single-line sidesteps it.
366
+ const causeLines = describeAmbiguousCause(cause);
367
+ if (causeLines.length > 0) {
368
+ output_1.output.warn({
369
+ title: 'The agent run ended without a usable handoff',
370
+ bodyLines: causeLines,
371
+ });
372
+ }
373
+ // `migratePrompt` injects `options.cancel` so Ctrl+C and Esc exit cleanly
374
+ // via `process.exit(130)` before enquirer's broken cancel cleanup runs.
375
+ // Any other rejection we treat as abort.
376
+ try {
377
+ const response = await (0, safe_prompt_1.migratePrompt)({
378
+ name: 'choice',
379
+ type: 'select',
380
+ message: 'How should nx migrate proceed?',
381
+ choices: [
382
+ { name: 'abort', message: 'Treat as failed — abort the run' },
383
+ {
384
+ name: 'continue',
385
+ message: 'Treat as completed — mark done and continue',
386
+ },
387
+ ],
388
+ });
389
+ return response.choice === 'continue'
390
+ ? { kind: 'ambiguous-continue' }
391
+ : { kind: 'ambiguous-abort' };
392
+ }
393
+ catch {
394
+ return { kind: 'ambiguous-abort' };
395
+ }
396
+ }
397
+ function describeAmbiguousCause(cause) {
398
+ const lines = [];
399
+ if (cause.spawnError) {
400
+ lines.push(`Could not spawn the agent: ${cause.spawnError}`);
401
+ }
402
+ if (cause.exitError) {
403
+ lines.push(`Agent process emitted an error: ${cause.exitError}`);
404
+ }
405
+ if (cause.exitCode !== undefined && cause.exitCode !== null) {
406
+ // Distinguish clean-exit-without-handoff (likely a deliberate close by
407
+ // the user, or the agent ended its session without invoking the handoff
408
+ // step) from a non-zero crash, so the prompt body doesn't collapse them
409
+ // into the same uninformative "exited with code N" line.
410
+ lines.push(cause.exitCode === 0
411
+ ? 'Agent exited cleanly (code 0) without writing a handoff.'
412
+ : `Agent exited with code ${cause.exitCode}.`);
413
+ }
414
+ if (cause.exitSignal) {
415
+ lines.push(`Agent was terminated by signal ${cause.exitSignal}.`);
416
+ }
417
+ // "No handoff file was written." is redundant when another cause line
418
+ // already implies it (spawn error, exit code/signal, process error).
419
+ // Independent diagnostics (read-error, parse-error, shape-mismatch) always
420
+ // surface — they're not implied by the exit shape.
421
+ const handoffMissingIsRedundant = !!cause.spawnError ||
422
+ !!cause.exitError ||
423
+ cause.exitCode !== undefined ||
424
+ cause.exitSignal !== undefined;
425
+ switch (cause.handoff?.reason) {
426
+ case 'missing':
427
+ if (!handoffMissingIsRedundant) {
428
+ lines.push('No handoff file was written.');
429
+ }
430
+ break;
431
+ case 'read-error':
432
+ lines.push(`Handoff file could not be read${cause.handoff.detail ? `: ${cause.handoff.detail}` : '.'}`);
433
+ break;
434
+ case 'parse-error':
435
+ lines.push(`Handoff file contained invalid JSON${cause.handoff.detail ? `: ${cause.handoff.detail}` : '.'}`);
436
+ break;
437
+ case 'shape-mismatch':
438
+ lines.push('Handoff file was missing required fields or had an unexpected shape.');
439
+ break;
440
+ }
441
+ return lines;
442
+ }
@@ -0,0 +1,14 @@
1
+ import { AgentId, ResolvedAgentic } from './types';
2
+ /** Possible values for `--agentic` after yargs normalization. */
3
+ export type AgenticArg = undefined | boolean | AgentId;
4
+ export interface ResolveAgenticInput {
5
+ agentic: AgenticArg;
6
+ migrations: ReadonlyArray<{
7
+ prompt?: string;
8
+ }>;
9
+ }
10
+ /**
11
+ * Resolves the agentic state for a `--run-migrations` invocation. Runs once,
12
+ * before the migration loop, and its result is cached for every entry.
13
+ */
14
+ export declare function resolveAgentic(input: ResolveAgenticInput): Promise<ResolvedAgentic>;
@@ -0,0 +1,150 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveAgentic = resolveAgentic;
4
+ const output_1 = require("../../../utils/output");
5
+ const safe_prompt_1 = require("../safe-prompt");
6
+ const detect_installed_1 = require("./detect-installed");
7
+ const inception_1 = require("./inception");
8
+ const definitions_1 = require("./definitions");
9
+ const INSTALL_SUPPORTED_AGENTS_HINT = [
10
+ 'Install one of the supported agents and re-run the migration:',
11
+ ...definitions_1.AGENT_DEFINITIONS.map((def) => ` - ${def.displayName}`),
12
+ ];
13
+ /**
14
+ * Resolves the agentic state for a `--run-migrations` invocation. Runs once,
15
+ * before the migration loop, and its result is cached for every entry.
16
+ */
17
+ async function resolveAgentic(input) {
18
+ if ((0, inception_1.isInsideAgent)()) {
19
+ output_1.output.log({
20
+ title: input.agentic === true || typeof input.agentic === 'string'
21
+ ? 'Agentic flow skipped: nx detected this run is invoked from inside an AI agent — the outer agent drives the migration. The explicit --agentic flag is ignored in this context.'
22
+ : 'Agentic flow skipped: nx detected this run is invoked from inside an AI agent.',
23
+ });
24
+ return { kind: 'inside-agent' };
25
+ }
26
+ const isInteractive = !!process.stdin.isTTY && !!process.stdout.isTTY;
27
+ // Skip detection for the one case where the result is unused: explicit
28
+ // `--agentic=false`. For every other path (explicit enable, explicit id, or
29
+ // undefined-with-prompt) we either need the detection result to pick/verify
30
+ // an agent or to decide whether the up-front prompt is even worth asking.
31
+ if (input.agentic === false) {
32
+ return { kind: 'disabled' };
33
+ }
34
+ const detected = await (0, detect_installed_1.detectInstalledAgents)(definitions_1.AGENT_DEFINITIONS);
35
+ const { enabled, explicitId } = await resolveFlag(input, isInteractive, detected);
36
+ if (!enabled) {
37
+ return { kind: 'disabled' };
38
+ }
39
+ const selected = await selectAgent(detected, explicitId, isInteractive);
40
+ return { kind: 'enabled', selectedAgent: selected };
41
+ }
42
+ async function resolveFlag(input, isInteractive, detected) {
43
+ if (input.agentic === true) {
44
+ requireInteractiveOrAbort(isInteractive);
45
+ return { enabled: true };
46
+ }
47
+ if (typeof input.agentic === 'string') {
48
+ requireInteractiveOrAbort(isInteractive);
49
+ return { enabled: true, explicitId: input.agentic };
50
+ }
51
+ // undefined — fire the up-front prompt only when we have a TTY, something
52
+ // agentic-eligible is queued, and at least one agent is installed. Without
53
+ // an installed agent the user can't say "Yes" meaningfully — asking would
54
+ // walk them into a dead end.
55
+ if (!isInteractive) {
56
+ return { enabled: false };
57
+ }
58
+ if (!input.migrations.some((m) => !!m.prompt)) {
59
+ return { enabled: false };
60
+ }
61
+ if (detected.length === 0) {
62
+ return { enabled: false };
63
+ }
64
+ return { enabled: await firePromptForAgentic(input.migrations) };
65
+ }
66
+ function requireInteractiveOrAbort(isInteractive) {
67
+ if (isInteractive)
68
+ return;
69
+ output_1.output.error({
70
+ title: 'The agentic flow is interactive-only in this release.',
71
+ bodyLines: [
72
+ 'Re-run in an interactive terminal, or pass `--agentic=false` to skip the agentic flow.',
73
+ ],
74
+ });
75
+ throw new Error('Agentic flow requires an interactive terminal.');
76
+ }
77
+ async function firePromptForAgentic(migrations) {
78
+ // The "Yes"/"No" hints below assume at least one prompt-bearing migration is
79
+ // queued. If we later extend the prompt to fire for generator-only runs
80
+ // (validation-only), the hints need to branch.
81
+ const promptCount = migrations.filter((m) => !!m.prompt).length;
82
+ const yesHint = `Apply ${promptCount} prompt migration${promptCount === 1 ? '' : 's'} and validate generator output with an AI agent`;
83
+ const noHint = `Skip prompts and run generators without AI validation`;
84
+ // Blank line keeps the prompt from gluing to the previous `npm install`
85
+ // output or any earlier orchestrator line.
86
+ console.log();
87
+ // `as any` because enquirer's TS types lag the runtime (per-choice `hint`
88
+ // and `value` are supported but not in the .d.ts).
89
+ const response = await (0, safe_prompt_1.migratePrompt)({
90
+ name: 'enable',
91
+ type: 'autocomplete',
92
+ message: 'Enable the agentic flow?',
93
+ choices: [
94
+ { name: 'yes', message: 'Yes', hint: yesHint },
95
+ { name: 'no', message: 'No', hint: noHint },
96
+ ],
97
+ initial: 0,
98
+ });
99
+ return response.enable === 'yes';
100
+ }
101
+ async function selectAgent(detected, explicitId, isInteractive) {
102
+ if (explicitId) {
103
+ const match = detected.find((d) => d.id === explicitId);
104
+ if (match) {
105
+ return match;
106
+ }
107
+ if (detected.length === 0) {
108
+ output_1.output.error({
109
+ title: `The agent "${explicitId}" was requested via --agentic but no supported AI agent is installed on this machine.`,
110
+ bodyLines: INSTALL_SUPPORTED_AGENTS_HINT,
111
+ });
112
+ throw new Error(`The requested agent "${explicitId}" is not installed.`);
113
+ }
114
+ output_1.output.error({
115
+ title: `The agent "${explicitId}" was requested via --agentic but is not installed.`,
116
+ bodyLines: [
117
+ 'Install the requested agent and re-run, or pass --agentic without an explicit agent to choose from installed ones.',
118
+ 'Currently installed agents:',
119
+ ...detected.map((d) => ` - ${d.displayName} (${d.id})`),
120
+ ],
121
+ });
122
+ throw new Error(`The requested agent "${explicitId}" is not installed.`);
123
+ }
124
+ if (detected.length === 0) {
125
+ output_1.output.error({
126
+ title: 'Agentic flow was enabled, but no supported AI agent is installed on this machine.',
127
+ bodyLines: INSTALL_SUPPORTED_AGENTS_HINT,
128
+ });
129
+ throw new Error('No installed AI agent available.');
130
+ }
131
+ if (detected.length === 1) {
132
+ const only = detected[0];
133
+ output_1.output.log({
134
+ title: `Using ${only.displayName} for the agentic flow.`,
135
+ });
136
+ return only;
137
+ }
138
+ // 2+ detected. The picker only fires interactively. We've already required
139
+ // a TTY before resolving an enabled state, so this is defense-in-depth.
140
+ requireInteractiveOrAbort(isInteractive);
141
+ // Blank line keeps the prompt from gluing to the preceding output.
142
+ console.log();
143
+ const response = await (0, safe_prompt_1.migratePrompt)({
144
+ name: 'id',
145
+ type: 'select',
146
+ message: 'Multiple AI agents detected. Which one should Nx use?',
147
+ choices: detected.map((d) => ({ name: d.id, message: d.displayName })),
148
+ });
149
+ return detected.find((d) => d.id === response.id);
150
+ }