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.
- package/dist/bin/init-local.js +11 -20
- package/dist/bin/nx.d.ts +1 -0
- package/dist/bin/nx.js +28 -2
- package/dist/plugins/package-json.js +4 -2
- package/dist/src/adapter/ngcli-adapter.d.ts +2 -1
- package/dist/src/adapter/ngcli-adapter.js +25 -2
- package/dist/src/ai/clone-ai-config-repo.js +20 -3
- package/dist/src/analytics/analytics.js +10 -1
- package/dist/src/command-line/add/completion.d.ts +1 -0
- package/dist/src/command-line/add/completion.js +15 -0
- package/dist/src/command-line/affected/completion.d.ts +1 -0
- package/dist/src/command-line/affected/completion.js +15 -0
- package/dist/src/command-line/completion/argv-layout.d.ts +6 -0
- package/dist/src/command-line/completion/argv-layout.js +19 -0
- package/dist/src/command-line/completion/command-completions.d.ts +19 -0
- package/dist/src/command-line/completion/command-completions.js +120 -0
- package/dist/src/command-line/completion/command-handlers.d.ts +30 -0
- package/dist/src/command-line/completion/command-handlers.js +69 -0
- package/dist/src/command-line/completion/command-object.d.ts +10 -0
- package/dist/src/command-line/completion/command-object.js +95 -0
- package/dist/src/command-line/completion/completion-providers.d.ts +21 -0
- package/dist/src/command-line/completion/completion-providers.js +194 -0
- package/dist/src/command-line/completion/infix-targets.d.ts +1 -0
- package/dist/src/command-line/completion/infix-targets.js +48 -0
- package/dist/src/command-line/completion/metadata.d.ts +22 -0
- package/dist/src/command-line/completion/metadata.js +71 -0
- package/dist/src/command-line/completion/registrations.d.ts +9 -0
- package/dist/src/command-line/completion/registrations.js +16 -0
- package/dist/src/command-line/completion/scripts/bash.sh +51 -0
- package/dist/src/command-line/completion/scripts/fish.fish +47 -0
- package/dist/src/command-line/completion/scripts/powershell.ps1 +52 -0
- package/dist/src/command-line/completion/scripts/zsh.zsh +69 -0
- package/dist/src/command-line/completion/scripts.d.ts +18 -0
- package/dist/src/command-line/completion/scripts.js +140 -0
- package/dist/src/command-line/completion/trigger.d.ts +3 -0
- package/dist/src/command-line/completion/trigger.js +21 -0
- package/dist/src/command-line/completion/value-completions.d.ts +3 -0
- package/dist/src/command-line/completion/value-completions.js +21 -0
- package/dist/src/command-line/examples.js +1 -1
- package/dist/src/command-line/format/format.js +15 -5
- package/dist/src/command-line/generate/completion.d.ts +1 -0
- package/dist/src/command-line/generate/completion.js +9 -0
- package/dist/src/command-line/graph/completion.d.ts +1 -0
- package/dist/src/command-line/graph/completion.js +13 -0
- package/dist/src/command-line/init/implementation/angular/standalone-workspace.js +14 -18
- package/dist/src/command-line/init/implementation/dot-nx/add-nx-scripts.js +1 -0
- package/dist/src/command-line/init/implementation/utils.d.ts +7 -1
- package/dist/src/command-line/init/implementation/utils.js +51 -14
- package/dist/src/command-line/migrate/agentic/capture-generator-output.d.ts +22 -0
- package/dist/src/command-line/migrate/agentic/capture-generator-output.js +100 -0
- package/dist/src/command-line/migrate/agentic/cli-args.d.ts +12 -0
- package/dist/src/command-line/migrate/agentic/cli-args.js +38 -0
- package/dist/src/command-line/migrate/agentic/definitions.d.ts +6 -0
- package/dist/src/command-line/migrate/agentic/definitions.js +98 -0
- package/dist/src/command-line/migrate/agentic/detect-installed.d.ts +10 -0
- package/dist/src/command-line/migrate/agentic/detect-installed.js +68 -0
- package/dist/src/command-line/migrate/agentic/handoff-gitignore.d.ts +46 -0
- package/dist/src/command-line/migrate/agentic/handoff-gitignore.js +87 -0
- package/dist/src/command-line/migrate/agentic/handoff.d.ts +63 -0
- package/dist/src/command-line/migrate/agentic/handoff.js +183 -0
- package/dist/src/command-line/migrate/agentic/inception.d.ts +9 -0
- package/dist/src/command-line/migrate/agentic/inception.js +15 -0
- package/dist/src/command-line/migrate/agentic/print-dropped-agent-context.d.ts +22 -0
- package/dist/src/command-line/migrate/agentic/print-dropped-agent-context.js +50 -0
- package/dist/src/command-line/migrate/agentic/prompts/generic-validation.d.ts +51 -0
- package/dist/src/command-line/migrate/agentic/prompts/generic-validation.js +65 -0
- package/dist/src/command-line/migrate/agentic/prompts/hybrid-prompt-migration.d.ts +44 -0
- package/dist/src/command-line/migrate/agentic/prompts/hybrid-prompt-migration.js +52 -0
- package/dist/src/command-line/migrate/agentic/prompts/prompt-migration.d.ts +21 -0
- package/dist/src/command-line/migrate/agentic/prompts/prompt-migration.js +26 -0
- package/dist/src/command-line/migrate/agentic/prompts/shared-rendering.d.ts +18 -0
- package/dist/src/command-line/migrate/agentic/prompts/shared-rendering.js +130 -0
- package/dist/src/command-line/migrate/agentic/prompts/system-prompt.d.ts +46 -0
- package/dist/src/command-line/migrate/agentic/prompts/system-prompt.js +88 -0
- package/dist/src/command-line/migrate/agentic/run-step.d.ts +51 -0
- package/dist/src/command-line/migrate/agentic/run-step.js +121 -0
- package/dist/src/command-line/migrate/agentic/runner.d.ts +33 -0
- package/dist/src/command-line/migrate/agentic/runner.js +442 -0
- package/dist/src/command-line/migrate/agentic/select.d.ts +14 -0
- package/dist/src/command-line/migrate/agentic/select.js +150 -0
- package/dist/src/command-line/migrate/agentic/types.d.ts +102 -0
- package/dist/src/command-line/migrate/agentic/types.js +2 -0
- package/dist/src/command-line/migrate/command-object.d.ts +1 -0
- package/dist/src/command-line/migrate/command-object.js +32 -7
- package/dist/src/command-line/migrate/migrate-commits.d.ts +50 -0
- package/dist/src/command-line/migrate/migrate-commits.js +102 -0
- package/dist/src/command-line/migrate/migrate-output.d.ts +180 -0
- package/dist/src/command-line/migrate/migrate-output.js +258 -0
- package/dist/src/command-line/migrate/migrate.d.ts +122 -12
- package/dist/src/command-line/migrate/migrate.js +1036 -217
- package/dist/src/command-line/migrate/migration-shape.d.ts +8 -0
- package/dist/src/command-line/migrate/migration-shape.js +13 -0
- package/dist/src/command-line/migrate/multi-major.d.ts +30 -0
- package/dist/src/command-line/migrate/multi-major.js +185 -0
- package/dist/src/command-line/migrate/prompt-files.d.ts +31 -0
- package/dist/src/command-line/migrate/prompt-files.js +141 -0
- package/dist/src/command-line/migrate/run-migration-process.js +28 -6
- package/dist/src/command-line/migrate/safe-prompt.d.ts +28 -0
- package/dist/src/command-line/migrate/safe-prompt.js +49 -0
- package/dist/src/command-line/migrate/update-filters.d.ts +11 -0
- package/dist/src/command-line/migrate/update-filters.js +44 -0
- package/dist/src/command-line/migrate/version-utils.d.ts +6 -0
- package/dist/src/command-line/migrate/version-utils.js +59 -0
- package/dist/src/command-line/nx-commands.js +9 -0
- package/dist/src/command-line/release/config/config.d.ts +3 -6
- package/dist/src/command-line/release/config/config.js +77 -45
- package/dist/src/command-line/release/config/use-legacy-versioning.d.ts +2 -0
- package/dist/src/command-line/release/config/use-legacy-versioning.js +8 -0
- package/dist/src/command-line/release/utils/release-graph.js +2 -3
- package/dist/src/command-line/release/utils/repository-git-tags.js +1 -1
- package/dist/src/command-line/release/utils/resolve-changelog-renderer.js +7 -19
- package/dist/src/command-line/release/version/resolve-current-version.js +1 -1
- package/dist/src/command-line/release/version/version-actions.js +3 -7
- package/dist/src/command-line/report/report.js +2 -2
- package/dist/src/command-line/run/completion.d.ts +1 -0
- package/dist/src/command-line/run/completion.js +7 -0
- package/dist/src/command-line/run-many/completion.d.ts +1 -0
- package/dist/src/command-line/run-many/completion.js +13 -0
- package/dist/src/command-line/show/completion.d.ts +1 -0
- package/dist/src/command-line/show/completion.js +27 -0
- package/dist/src/command-line/show/show-target/info.d.ts +1 -0
- package/dist/src/command-line/show/show-target/info.js +100 -19
- package/dist/src/command-line/watch/command-object.js +24 -4
- package/dist/src/command-line/watch/completion.d.ts +1 -0
- package/dist/src/command-line/watch/completion.js +10 -0
- package/dist/src/command-line/watch/watch.d.ts +7 -0
- package/dist/src/command-line/watch/watch.js +1 -1
- package/dist/src/config/misc-interfaces.d.ts +25 -2
- package/dist/src/config/nx-json.d.ts +43 -56
- package/dist/src/config/schema-utils.d.ts +21 -0
- package/dist/src/config/schema-utils.js +92 -18
- package/dist/src/config/task-graph.d.ts +4 -107
- package/dist/src/core/graph/main.js +1 -1
- package/dist/src/core/graph/styles.css +2 -3
- package/dist/src/core/graph/styles.js +1 -1
- package/dist/src/daemon/client/client.d.ts +1 -1
- package/dist/src/daemon/server/file-watching/file-watcher-sockets.d.ts +1 -1
- package/dist/src/daemon/server/file-watching/file-watcher-sockets.js +1 -1
- package/dist/src/daemon/server/file-watching/route-workspace-changes.d.ts +9 -0
- package/dist/src/daemon/server/file-watching/route-workspace-changes.js +76 -0
- package/dist/src/daemon/server/project-graph-incremental-recomputation.js +45 -11
- package/dist/src/daemon/server/server.js +4 -43
- package/dist/src/daemon/server/start.d.ts +1 -1
- package/dist/src/daemon/server/start.js +2 -0
- package/dist/src/devkit-exports.d.ts +2 -2
- package/dist/src/devkit-internals.d.ts +5 -1
- package/dist/src/devkit-internals.js +17 -1
- package/dist/src/executors/run-commands/running-tasks.d.ts +7 -0
- package/dist/src/executors/run-commands/running-tasks.js +178 -105
- package/dist/src/executors/run-script/run-script.impl.js +3 -10
- package/dist/src/executors/utils/convert-nx-executor.js +1 -1
- package/dist/src/hasher/hash-plan-inspector.d.ts +1 -1
- package/dist/src/hasher/task-hasher.js +6 -4
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +0 -3
- package/dist/src/migrations/update-16-2-0/remove-run-commands-output-path.js +6 -2
- package/dist/src/migrations/update-17-0-0/move-cache-directory.md +31 -0
- package/dist/src/migrations/update-17-0-0/use-minimal-config-for-tasks-runner-options.js +20 -4
- package/dist/src/migrations/update-20-0-0/move-use-daemon-process.md +27 -0
- package/dist/src/migrations/update-20-0-1/use-legacy-cache.md +24 -0
- package/dist/src/migrations/update-21-0-0/release-changelog-config-changes.md +49 -0
- package/dist/src/migrations/update-21-0-0/release-version-config-changes.md +54 -0
- package/dist/src/migrations/update-21-0-0/remove-custom-tasks-runner.md +28 -0
- package/dist/src/migrations/update-21-0-0/remove-legacy-cache.md +22 -0
- package/dist/src/migrations/update-22-2-0/add-self-healing-to-gitignore.md +11 -0
- package/dist/src/migrations/update-23-0-0/add-migrate-runs-to-git-ignore.d.ts +2 -0
- package/dist/src/migrations/update-23-0-0/add-migrate-runs-to-git-ignore.js +16 -0
- package/dist/src/migrations/update-23-0-0/consolidate-release-tag-config.d.ts +9 -0
- package/dist/src/migrations/update-23-0-0/consolidate-release-tag-config.js +18 -0
- package/dist/src/migrations/update-23-0-0/convert-target-defaults-to-array.d.ts +35 -0
- package/dist/src/migrations/update-23-0-0/convert-target-defaults-to-array.js +139 -0
- package/dist/src/migrations/update-23-0-0/convert-target-defaults-to-array.md +66 -0
- package/dist/src/native/index.d.ts +79 -2
- package/dist/src/native/native-bindings.js +3 -0
- package/dist/src/native/nx.wasm32-wasi.debug.wasm +0 -0
- package/dist/src/native/nx.wasm32-wasi.wasm +0 -0
- package/dist/src/plugins/js/lock-file/npm-parser.js +37 -19
- package/dist/src/plugins/js/lock-file/pnpm-parser.js +51 -4
- package/dist/src/plugins/js/lock-file/project-graph-pruning.js +12 -4
- package/dist/src/plugins/js/utils/packages.js +1 -1
- package/dist/src/plugins/js/utils/register.d.ts +103 -14
- package/dist/src/plugins/js/utils/register.js +434 -39
- package/dist/src/plugins/js/utils/typescript.d.ts +7 -0
- package/dist/src/plugins/js/utils/typescript.js +39 -0
- package/dist/src/plugins/package-json/create-nodes.d.ts +3 -2
- package/dist/src/plugins/package-json/create-nodes.js +7 -5
- package/dist/src/project-graph/affected/locators/project-glob-changes.js +2 -1
- package/dist/src/project-graph/build-project-graph.d.ts +5 -0
- package/dist/src/project-graph/build-project-graph.js +6 -2
- package/dist/src/project-graph/file-map-utils.d.ts +5 -0
- package/dist/src/project-graph/file-map-utils.js +10 -1
- package/dist/src/project-graph/plugins/get-plugins.d.ts +7 -2
- package/dist/src/project-graph/plugins/get-plugins.js +8 -5
- package/dist/src/project-graph/plugins/isolation/plugin-worker.d.ts +1 -0
- package/dist/src/project-graph/plugins/isolation/plugin-worker.js +2 -0
- package/dist/src/project-graph/plugins/resolve-plugin.d.ts +7 -4
- package/dist/src/project-graph/plugins/resolve-plugin.js +152 -33
- package/dist/src/project-graph/plugins/tasks-execution-hooks.js +4 -2
- package/dist/src/project-graph/plugins/transpiler.d.ts +12 -0
- package/dist/src/project-graph/plugins/transpiler.js +37 -0
- package/dist/src/project-graph/plugins/utils.js +13 -7
- package/dist/src/project-graph/project-graph.js +1 -1
- package/dist/src/project-graph/utils/project-configuration/target-defaults.d.ts +95 -4
- package/dist/src/project-graph/utils/project-configuration/target-defaults.js +515 -68
- package/dist/src/project-graph/utils/project-configuration-utils.d.ts +13 -5
- package/dist/src/project-graph/utils/project-configuration-utils.js +14 -6
- package/dist/src/project-graph/utils/retrieve-workspace-files.js +1 -1
- package/dist/src/tasks-runner/create-task-graph.d.ts +4 -4
- package/dist/src/tasks-runner/create-task-graph.js +1 -1
- package/dist/src/tasks-runner/default-tasks-runner.d.ts +0 -2
- package/dist/src/tasks-runner/forked-process-task-runner.d.ts +1 -1
- package/dist/src/tasks-runner/forked-process-task-runner.js +11 -6
- package/dist/src/tasks-runner/init-tasks-runner.d.ts +0 -15
- package/dist/src/tasks-runner/init-tasks-runner.js +0 -63
- package/dist/src/tasks-runner/legacy-depends-on-warning.d.ts +18 -0
- package/dist/src/tasks-runner/legacy-depends-on-warning.js +109 -0
- package/dist/src/tasks-runner/life-cycle.d.ts +7 -8
- package/dist/src/tasks-runner/life-cycles/invoke-runner-terminal-output-life-cycle.js +6 -6
- package/dist/src/tasks-runner/life-cycles/task-history-life-cycle-old.js +13 -2
- package/dist/src/tasks-runner/life-cycles/task-history-life-cycle.js +16 -5
- package/dist/src/tasks-runner/life-cycles/tui-summary-life-cycle.js +11 -2
- package/dist/src/tasks-runner/pseudo-terminal.d.ts +1 -1
- package/dist/src/tasks-runner/pseudo-terminal.js +22 -10
- package/dist/src/tasks-runner/run-command.js +8 -11
- package/dist/src/tasks-runner/running-tasks/batch-process.d.ts +1 -1
- package/dist/src/tasks-runner/running-tasks/batch-process.js +3 -5
- package/dist/src/tasks-runner/running-tasks/node-child-process.d.ts +2 -2
- package/dist/src/tasks-runner/running-tasks/node-child-process.js +5 -7
- package/dist/src/tasks-runner/task-env.d.ts +1 -1
- package/dist/src/tasks-runner/task-env.js +6 -1
- package/dist/src/tasks-runner/task-orchestrator.d.ts +11 -1
- package/dist/src/tasks-runner/task-orchestrator.js +112 -38
- package/dist/src/tasks-runner/tasks-schedule.js +3 -3
- package/dist/src/tasks-runner/utils.d.ts +7 -8
- package/dist/src/tasks-runner/utils.js +23 -27
- package/dist/src/utils/child-process.js +2 -2
- package/dist/src/utils/compile-cache.d.ts +24 -0
- package/dist/src/utils/compile-cache.js +49 -0
- package/dist/src/utils/enable-compile-cache.d.ts +1 -0
- package/dist/src/utils/enable-compile-cache.js +7 -0
- package/dist/src/utils/fileutils.d.ts +0 -8
- package/dist/src/utils/fileutils.js +0 -40
- package/dist/src/utils/git-utils.d.ts +15 -0
- package/dist/src/utils/git-utils.js +138 -0
- package/dist/src/utils/handle-import.d.ts +4 -1
- package/dist/src/utils/handle-import.js +56 -2
- package/dist/src/utils/has-nx-js-plugin.d.ts +9 -0
- package/dist/src/utils/has-nx-js-plugin.js +24 -0
- package/dist/src/utils/installed-nx-version.d.ts +14 -4
- package/dist/src/utils/installed-nx-version.js +54 -7
- package/dist/src/utils/logger.d.ts +12 -1
- package/dist/src/utils/logger.js +57 -36
- package/dist/src/utils/nx-key.d.ts +0 -1
- package/dist/src/utils/nx-key.js +20 -23
- package/dist/src/utils/nx-package-group.d.ts +8 -0
- package/dist/src/utils/nx-package-group.js +15 -0
- package/dist/src/utils/output.d.ts +3 -2
- package/dist/src/utils/output.js +29 -28
- package/dist/src/utils/package-json.d.ts +14 -1
- package/dist/src/utils/package-json.js +20 -21
- package/dist/src/utils/perf-logging.js +3 -1
- package/dist/src/utils/plugin-cache-utils.d.ts +13 -4
- package/dist/src/utils/plugin-cache-utils.js +23 -13
- package/dist/src/utils/plugins/local-plugins.d.ts +18 -0
- package/dist/src/utils/plugins/local-plugins.js +30 -0
- package/dist/src/utils/tar.d.ts +8 -0
- package/dist/src/utils/tar.js +44 -0
- package/migrations.json +16 -0
- package/package.json +28 -28
- package/schemas/nx-schema.json +114 -80
- package/dist/src/plugins/js/project-graph/build-dependencies/strip-source-code.d.ts +0 -7
- package/dist/src/plugins/js/project-graph/build-dependencies/strip-source-code.js +0 -155
- package/dist/src/plugins/js/project-graph/build-dependencies/typescript-import-locator.d.ts +0 -16
- 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
|
+
}
|