nx 23.0.0-beta.22 → 23.0.0-beta.24
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/plugins/package-json.js +1 -1
- package/dist/src/adapter/angular-json.d.ts +2 -2
- package/dist/src/adapter/angular-json.js +0 -1
- package/dist/src/ai/set-up-ai-agents/set-up-ai-agents.js +2 -1
- package/dist/src/command-line/configure-ai-agents/configure-ai-agents.js +1 -1
- package/dist/src/command-line/examples.js +1 -1
- package/dist/src/command-line/graph/graph.js +1 -1
- package/dist/src/command-line/init/init-v2.js +1 -1
- package/dist/src/command-line/migrate/agentic/definitions.js +24 -1
- package/dist/src/command-line/migrate/agentic/handoff.d.ts +6 -0
- package/dist/src/command-line/migrate/agentic/handoff.js +8 -1
- package/dist/src/command-line/migrate/agentic/prompts/generic-validation.js +1 -1
- package/dist/src/command-line/migrate/agentic/prompts/hybrid-prompt-migration.js +1 -1
- package/dist/src/command-line/migrate/agentic/prompts/prompt-migration.js +1 -1
- package/dist/src/command-line/migrate/agentic/prompts/system-prompt.js +18 -17
- package/dist/src/command-line/migrate/agentic/select.d.ts +2 -0
- package/dist/src/command-line/migrate/agentic/select.js +4 -2
- package/dist/src/command-line/migrate/command-object.js +2 -2
- package/dist/src/command-line/migrate/migrate-ui-api.d.ts +12 -0
- package/dist/src/command-line/migrate/migrate-ui-api.js +74 -5
- package/dist/src/command-line/migrate/migrate.d.ts +4 -0
- package/dist/src/command-line/migrate/migrate.js +143 -45
- package/dist/src/command-line/migrate/multi-major.d.ts +1 -0
- package/dist/src/command-line/migrate/multi-major.js +9 -6
- package/dist/src/command-line/migrate/safe-prompt.d.ts +5 -0
- package/dist/src/command-line/migrate/safe-prompt.js +9 -0
- package/dist/src/core/graph/main.js +1 -1
- package/dist/src/core/graph/styles.css +1 -1
- package/dist/src/daemon/server/latest-nx.js +3 -1
- package/dist/src/daemon/server/project-graph-incremental-recomputation.js +27 -19
- package/dist/src/devkit-exports.d.ts +11 -11
- package/dist/src/devkit-exports.js +7 -4
- package/dist/src/generators/utils/project-configuration.d.ts +10 -1
- package/dist/src/generators/utils/project-configuration.js +0 -8
- 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/index.d.ts +2 -2
- package/dist/src/plugins/js/index.js +4 -4
- package/dist/src/plugins/js/lock-file/lock-file.d.ts +2 -2
- package/dist/src/plugins/js/lock-file/lock-file.js +1 -1
- package/dist/src/plugins/js/utils/register.d.ts +52 -0
- package/dist/src/plugins/js/utils/register.js +201 -0
- package/dist/src/plugins/package-json/create-nodes.d.ts +2 -2
- package/dist/src/plugins/package-json/create-nodes.js +2 -2
- package/dist/src/plugins/project-json/build-nodes/project-json.d.ts +2 -2
- package/dist/src/plugins/project-json/build-nodes/project-json.js +1 -1
- package/dist/src/project-graph/error-types.d.ts +6 -6
- package/dist/src/project-graph/error-types.js +1 -1
- package/dist/src/project-graph/plugins/get-plugins.js +63 -19
- package/dist/src/project-graph/plugins/isolation/isolated-plugin.d.ts +2 -2
- package/dist/src/project-graph/plugins/isolation/messaging.d.ts +2 -2
- package/dist/src/project-graph/plugins/loaded-nx-plugin.d.ts +3 -3
- package/dist/src/project-graph/plugins/loaded-nx-plugin.js +6 -4
- package/dist/src/project-graph/plugins/public-api.d.ts +31 -12
- package/dist/src/project-graph/plugins/resolve-plugin.d.ts +7 -0
- package/dist/src/project-graph/plugins/resolve-plugin.js +15 -0
- package/dist/src/project-graph/plugins/transpiler.js +11 -0
- package/dist/src/project-graph/plugins/utils.d.ts +3 -3
- package/dist/src/project-graph/utils/project-configuration-utils.d.ts +1 -1
- package/dist/src/project-graph/utils/project-configuration-utils.js +6 -17
- package/dist/src/project-graph/utils/retrieve-workspace-files.d.ts +6 -0
- package/dist/src/project-graph/utils/retrieve-workspace-files.js +9 -0
- package/dist/src/utils/package-json.d.ts +3 -3
- package/dist/src/utils/package-json.js +9 -11
- package/dist/src/utils/package-manager.d.ts +13 -0
- package/dist/src/utils/package-manager.js +27 -0
- package/package.json +11 -11
- package/schemas/migrations-schema.json +186 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ProjectsConfigurations } from '../config/workspace-json-project-json';
|
|
2
|
-
import {
|
|
2
|
+
import { NxPlugin } from '../project-graph/plugins';
|
|
3
3
|
export declare const NX_ANGULAR_JSON_PLUGIN_NAME = "nx-angular-json-plugin";
|
|
4
|
-
export declare const NxAngularJsonPlugin:
|
|
4
|
+
export declare const NxAngularJsonPlugin: NxPlugin;
|
|
5
5
|
export default NxAngularJsonPlugin;
|
|
6
6
|
export declare function shouldMergeAngularProjects(root: string, includeProjectsFromAngularJson: boolean): boolean;
|
|
7
7
|
export declare function isAngularPluginInstalled(): boolean;
|
|
@@ -25,7 +25,6 @@ const createNodes = [
|
|
|
25
25
|
exports.NxAngularJsonPlugin = {
|
|
26
26
|
name: exports.NX_ANGULAR_JSON_PLUGIN_NAME,
|
|
27
27
|
createNodes,
|
|
28
|
-
createNodesV2: createNodes,
|
|
29
28
|
};
|
|
30
29
|
exports.default = exports.NxAngularJsonPlugin;
|
|
31
30
|
function shouldMergeAngularProjects(root, includeProjectsFromAngularJson) {
|
|
@@ -12,6 +12,7 @@ const generate_files_1 = require("../../generators/utils/generate-files");
|
|
|
12
12
|
const json_1 = require("../../generators/utils/json");
|
|
13
13
|
const native_1 = require("../../native");
|
|
14
14
|
const package_json_1 = require("../../utils/package-json");
|
|
15
|
+
const package_manager_1 = require("../../utils/package-manager");
|
|
15
16
|
const ignore_1 = require("../../utils/ignore");
|
|
16
17
|
const provenance_1 = require("../../utils/provenance");
|
|
17
18
|
const workspace_root_1 = require("../../utils/workspace-root");
|
|
@@ -48,7 +49,7 @@ async function setupAiAgentsGenerator(tree, options, inner = false) {
|
|
|
48
49
|
}
|
|
49
50
|
try {
|
|
50
51
|
await (0, provenance_1.ensurePackageHasProvenance)('nx', normalizedOptions.packageVersion);
|
|
51
|
-
const { tempDir, cleanup } = (0, package_json_1.installPackageToTmp)('nx', normalizedOptions.packageVersion);
|
|
52
|
+
const { tempDir, cleanup } = (0, package_json_1.installPackageToTmp)('nx', normalizedOptions.packageVersion, (0, package_manager_1.detectPackageManager)(tree.root));
|
|
52
53
|
let modulePath = (0, path_1.join)(tempDir, 'node_modules', 'nx', 'src/ai/set-up-ai-agents/set-up-ai-agents.js');
|
|
53
54
|
const module = await (0, handle_import_1.handleImport)(modulePath);
|
|
54
55
|
const setupAiAgentsGeneratorResult = await module.setupAiAgentsGenerator(tree, normalizedOptions, true);
|
|
@@ -44,7 +44,7 @@ async function configureAiAgentsHandler(args, inner = false) {
|
|
|
44
44
|
let cleanup;
|
|
45
45
|
try {
|
|
46
46
|
await (0, provenance_1.ensurePackageHasProvenance)('nx', 'latest');
|
|
47
|
-
const packageInstallResults = (0, devkit_internals_1.installPackageToTmp)('nx', 'latest');
|
|
47
|
+
const packageInstallResults = (0, devkit_internals_1.installPackageToTmp)('nx', 'latest', (0, package_manager_1.detectPackageManager)(workspace_root_1.workspaceRoot));
|
|
48
48
|
cleanup = packageInstallResults.cleanup;
|
|
49
49
|
let modulePath = require.resolve('nx/src/command-line/configure-ai-agents/configure-ai-agents.js', { paths: [packageInstallResults.tempDir] });
|
|
50
50
|
const module = await (0, handle_import_1.handleImport)(modulePath);
|
|
@@ -273,7 +273,7 @@ exports.examples = {
|
|
|
273
273
|
},
|
|
274
274
|
{
|
|
275
275
|
command: 'migrate latest --interactive',
|
|
276
|
-
description: 'Collect package updates and migrations in interactive mode. In this mode, the user will be prompted whether to apply any optional package update and migration',
|
|
276
|
+
description: 'Collect package updates and migrations in interactive mode. In this mode, the user will be prompted whether to apply any optional package update and migration. Only supported when migrating to Nx versions lower than v23',
|
|
277
277
|
},
|
|
278
278
|
{
|
|
279
279
|
command: 'migrate latest --from=nx@14.5.0 --exclude-applied-migrations',
|
|
@@ -172,7 +172,7 @@ async function generateGraph(args, affectedProjects) {
|
|
|
172
172
|
});
|
|
173
173
|
}
|
|
174
174
|
output_1.output.warn({
|
|
175
|
-
title: `${errors?.length > 1 ? `${errors.length} errors` : `An error`}
|
|
175
|
+
title: `${errors?.length > 1 ? `${errors.length} errors` : `An error`} occurred while processing the project graph. Showing partial graph.`,
|
|
176
176
|
});
|
|
177
177
|
}
|
|
178
178
|
}
|
|
@@ -42,7 +42,7 @@ async function initHandler(options, inner = false) {
|
|
|
42
42
|
let cleanup;
|
|
43
43
|
try {
|
|
44
44
|
await (0, provenance_1.ensurePackageHasProvenance)('nx', 'latest');
|
|
45
|
-
const packageInstallResults = (0, devkit_internals_1.installPackageToTmp)('nx', 'latest');
|
|
45
|
+
const packageInstallResults = (0, devkit_internals_1.installPackageToTmp)('nx', 'latest', (0, package_manager_2.detectPackageManager)(process.cwd()));
|
|
46
46
|
cleanup = packageInstallResults.cleanup;
|
|
47
47
|
let modulePath = require.resolve('nx/src/command-line/init/init-v2.js', {
|
|
48
48
|
paths: [packageInstallResults.tempDir],
|
|
@@ -4,6 +4,7 @@ exports.AGENT_DEFINITIONS = exports.opencodeDefinition = exports.codexDefinition
|
|
|
4
4
|
exports.getAgentDefinition = getAgentDefinition;
|
|
5
5
|
const os_1 = require("os");
|
|
6
6
|
const path_1 = require("path");
|
|
7
|
+
const handoff_1 = require("./handoff");
|
|
7
8
|
// --- Claude Code ---------------------------------------------------------
|
|
8
9
|
function claudeCodeWellKnownPaths() {
|
|
9
10
|
if (process.platform === 'win32') {
|
|
@@ -12,9 +13,25 @@ function claudeCodeWellKnownPaths() {
|
|
|
12
13
|
}
|
|
13
14
|
return [(0, path_1.join)((0, os_1.homedir)(), '.claude', 'local', 'claude')];
|
|
14
15
|
}
|
|
16
|
+
// Pre-authorizes the handoff write: Claude Code's default permission mode
|
|
17
|
+
// asks before file writes it has no allow rule for, so without this each step
|
|
18
|
+
// ends with an approval prompt for nx's own handoff scratch. Prefix-less
|
|
19
|
+
// patterns resolve against the session cwd (pinned to the workspace root
|
|
20
|
+
// below); `Edit` covers corrections to an already-written handoff.
|
|
21
|
+
const CLAUDE_CODE_HANDOFF_ALLOWED_TOOLS = `Write(${handoff_1.MIGRATE_RUNS_RELATIVE_DIR}/**),Edit(${handoff_1.MIGRATE_RUNS_RELATIVE_DIR}/**)`;
|
|
15
22
|
function claudeCodeBuildInteractive(ctx) {
|
|
16
23
|
return {
|
|
17
|
-
|
|
24
|
+
// `--allowedTools` is variadic (space/comma separated): a positional
|
|
25
|
+
// placed right after its value gets swallowed as another rule. The rules
|
|
26
|
+
// must stay in one comma-joined element with a non-variadic flag
|
|
27
|
+
// (`--system-prompt`) between them and the user prompt.
|
|
28
|
+
args: [
|
|
29
|
+
'--allowedTools',
|
|
30
|
+
CLAUDE_CODE_HANDOFF_ALLOWED_TOOLS,
|
|
31
|
+
'--system-prompt',
|
|
32
|
+
ctx.systemContext,
|
|
33
|
+
ctx.userPrompt,
|
|
34
|
+
],
|
|
18
35
|
cwd: ctx.workspaceRoot,
|
|
19
36
|
};
|
|
20
37
|
}
|
|
@@ -29,6 +46,9 @@ exports.claudeCodeDefinition = {
|
|
|
29
46
|
function codexWellKnownPaths() {
|
|
30
47
|
return [];
|
|
31
48
|
}
|
|
49
|
+
// No handoff permission flag: codex's default sandbox already allows writes
|
|
50
|
+
// inside the cwd tree without prompting, and a user-hardened read-only config
|
|
51
|
+
// is a deliberate choice we don't override.
|
|
32
52
|
function codexBuildInteractive(ctx) {
|
|
33
53
|
return {
|
|
34
54
|
args: ['-c', `developer_instructions=${ctx.systemContext}`, ctx.userPrompt],
|
|
@@ -62,6 +82,9 @@ function opencodeWellKnownPaths() {
|
|
|
62
82
|
candidates.push((0, path_1.join)(home, '.opencode', 'bin', 'opencode'));
|
|
63
83
|
return candidates;
|
|
64
84
|
}
|
|
85
|
+
// No handoff permission config: opencode's `edit` permission defaults to
|
|
86
|
+
// allow, and injecting one would clobber (not merge with) a user's own
|
|
87
|
+
// permission patterns.
|
|
65
88
|
function opencodeBuildInteractive(ctx) {
|
|
66
89
|
const config = {
|
|
67
90
|
agent: {
|
|
@@ -1,4 +1,10 @@
|
|
|
1
1
|
import { HandoffFile } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Workspace-relative directory holding all migrate-run scratch (handoff
|
|
4
|
+
* files). Shared with the agent permission rules in `definitions.ts` so the
|
|
5
|
+
* pre-authorized write scope can't drift from the actual layout.
|
|
6
|
+
*/
|
|
7
|
+
export declare const MIGRATE_RUNS_RELATIVE_DIR = ".nx/migrate-runs";
|
|
2
8
|
/** Returns the run directory for a given workspace + run id (target version). */
|
|
3
9
|
export declare function runDirPath(workspaceRoot: string, runId: string): string;
|
|
4
10
|
/**
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MIGRATE_RUNS_RELATIVE_DIR = void 0;
|
|
3
4
|
exports.runDirPath = runDirPath;
|
|
4
5
|
exports.mkdirSafely = mkdirSafely;
|
|
5
6
|
exports.initRunDir = initRunDir;
|
|
@@ -9,9 +10,15 @@ exports.readHandoff = readHandoff;
|
|
|
9
10
|
exports.waitForValidHandoff = waitForValidHandoff;
|
|
10
11
|
const fs_1 = require("fs");
|
|
11
12
|
const path_1 = require("path");
|
|
13
|
+
/**
|
|
14
|
+
* Workspace-relative directory holding all migrate-run scratch (handoff
|
|
15
|
+
* files). Shared with the agent permission rules in `definitions.ts` so the
|
|
16
|
+
* pre-authorized write scope can't drift from the actual layout.
|
|
17
|
+
*/
|
|
18
|
+
exports.MIGRATE_RUNS_RELATIVE_DIR = '.nx/migrate-runs';
|
|
12
19
|
/** Returns the run directory for a given workspace + run id (target version). */
|
|
13
20
|
function runDirPath(workspaceRoot, runId) {
|
|
14
|
-
return (0, path_1.join)(workspaceRoot,
|
|
21
|
+
return (0, path_1.join)(workspaceRoot, exports.MIGRATE_RUNS_RELATIVE_DIR, runId);
|
|
15
22
|
}
|
|
16
23
|
/**
|
|
17
24
|
* `mkdir -p` with a contextual error wrapper. Without this, the raw
|
|
@@ -42,7 +42,7 @@ function buildGenericValidationUserPrompt(ctx) {
|
|
|
42
42
|
const firstStep = ctx.impl.hasDiffContext
|
|
43
43
|
? `1. Inspect this migration's changes. ${(0, shared_rendering_1.renderGitInspectInstruction)()} Resolve each affected path to its owning Nx project via \`nx show project <name>\` (or by reading the project's \`project.json\` / \`package.json\`) to discover which targets each project actually defines — do not assume \`typecheck\` / \`test\` / \`lint\` exist. If no typecheck-equivalent exists, \`build\` is an acceptable substitute.`
|
|
44
44
|
: `1. Resolve each path in <files_changed> to its owning Nx project. Use \`nx show project <name>\` (or read the project's \`project.json\` / \`package.json\`) to discover which targets each project actually defines — do not assume \`typecheck\` / \`test\` / \`lint\` exist. If no typecheck-equivalent exists, \`build\` is an acceptable substitute.`;
|
|
45
|
-
lines.push(``, `<validation_instructions>`, firstStep, `2. Pick the smallest relevant subset of available targets to verify the change. Prefer \`nx affected -t <target>\` (or \`nx run <project>:<target>\` for a single project). When many small projects are affected, you may use \`nx run-many -t <target> -p <project1>,<project2>\` with the project list derived from the changed files. Unscoped \`nx run-many\` (no \`-p\`) is forbidden.`, `3. If a verification surfaces an issue the migration should have produced cleanly (e.g. a missing import, a type annotation the generator's template missed), you may apply a minor in-scope fix. The boundary is "what this migration intended to accomplish" — do not refactor, do not modify functionality unrelated to the migration, do not extend the migration's scope, do not touch code the migration was not concerned with. If you are unsure whether a fix is in scope, report it in \`summary\` instead of applying.`, `4. Apply every fix you can within scope, then write your handoff
|
|
45
|
+
lines.push(``, `<validation_instructions>`, firstStep, `2. Pick the smallest relevant subset of available targets to verify the change. Prefer \`nx affected -t <target>\` (or \`nx run <project>:<target>\` for a single project). When many small projects are affected, you may use \`nx run-many -t <target> -p <project1>,<project2>\` with the project list derived from the changed files. Unscoped \`nx run-many\` (no \`-p\`) is forbidden.`, `3. If a verification surfaces an issue the migration should have produced cleanly (e.g. a missing import, a type annotation the generator's template missed), you may apply a minor in-scope fix. The boundary is "what this migration intended to accomplish" — do not refactor, do not modify functionality unrelated to the migration, do not extend the migration's scope, do not touch code the migration was not concerned with. If you are unsure whether a fix is in scope, report it in \`summary\` instead of applying.`, `4. Apply every fix you can within scope, then end the step per the handoff contract. If everything is resolved, write your handoff with \`status: "success"\`, summarizing what you verified and any fixes you applied. If unresolved findings remain, report them to the user and ask how to proceed before writing any handoff; on a \`status: "failed"\` handoff, enumerate the findings in \`summary\` so the user can address them — no commit will be created from a failed run, so the generator's changes and your partial fixes will sit uncommitted in the working tree for the user to review.`, `</validation_instructions>`, ``, `When you end the step per the handoff contract, your handoff path is:`, ...(0, shared_rendering_1.renderHandoffPathFooter)(ctx.handoffFileAbsolutePath));
|
|
46
46
|
return lines.join('\n');
|
|
47
47
|
}
|
|
48
48
|
function renderFileListBody(changes) {
|
|
@@ -40,7 +40,7 @@ function buildHybridPromptUserPrompt(ctx) {
|
|
|
40
40
|
if (agentContext.length > 0) {
|
|
41
41
|
lines.push(...(0, shared_rendering_1.renderAdvisoryContext)('hints from the generator phase; consult while following the instructions, not as separate tasks', agentContext));
|
|
42
42
|
}
|
|
43
|
-
lines.push(``, `<instructions_file>${(0, shared_rendering_1.escapeXmlBody)(ctx.promptPath)}</instructions_file>`, ``, `<precedence>If anything in the sections above conflicts with the instructions file, the instructions file wins.</precedence>`, ``, `Open the instructions file (path is workspace-relative), follow its instructions step by step using the sections above as context, then
|
|
43
|
+
lines.push(``, `<instructions_file>${(0, shared_rendering_1.escapeXmlBody)(ctx.promptPath)}</instructions_file>`, ``, `<precedence>If anything in the sections above conflicts with the instructions file, the instructions file wins.</precedence>`, ``, `Open the instructions file (path is workspace-relative), follow its instructions step by step using the sections above as context, then end the step per the handoff contract. Your handoff path is:`, ...(0, shared_rendering_1.renderHandoffPathFooter)(ctx.handoffFileAbsolutePath));
|
|
44
44
|
return lines.join('\n');
|
|
45
45
|
}
|
|
46
46
|
function renderFileList(changes) {
|
|
@@ -20,7 +20,7 @@ function buildPromptMigrationUserPrompt(ctx) {
|
|
|
20
20
|
``,
|
|
21
21
|
`<instructions_file>${(0, shared_rendering_1.escapeXmlBody)(ctx.promptPath)}</instructions_file>`,
|
|
22
22
|
``,
|
|
23
|
-
`Open the instructions file (path is workspace-relative), follow its instructions step by step, then
|
|
23
|
+
`Open the instructions file (path is workspace-relative), follow its instructions step by step, then end the step per the handoff contract. Your handoff path is:`,
|
|
24
24
|
...(0, shared_rendering_1.renderHandoffPathFooter)(ctx.handoffFileAbsolutePath),
|
|
25
25
|
];
|
|
26
26
|
return lines.join('\n');
|
|
@@ -31,27 +31,28 @@ function buildSystemPrompt(ctx) {
|
|
|
31
31
|
`</opening_brief>`,
|
|
32
32
|
``,
|
|
33
33
|
`<handoff_contract>`,
|
|
34
|
-
`
|
|
34
|
+
`A step ends when you write the handoff file — a JSON file at:`,
|
|
35
|
+
`<handoff_path>`,
|
|
36
|
+
`${(0, shared_rendering_1.escapeXmlBody)(ctx.handoffFileAbsolutePath)}`,
|
|
37
|
+
`</handoff_path>`,
|
|
38
|
+
`With this shape:`,
|
|
39
|
+
`{`,
|
|
40
|
+
` "status": "success" | "failed",`,
|
|
41
|
+
` "summary": "[one to three sentences: what was done, or why it failed]"`,
|
|
42
|
+
`}`,
|
|
43
|
+
`\`nx migrate\` is watching for this file; once it appears nx closes this session automatically and continues with the next step. Do not attempt further work after the handoff is written.`,
|
|
35
44
|
``,
|
|
36
|
-
`
|
|
37
|
-
`
|
|
38
|
-
`
|
|
39
|
-
`
|
|
40
|
-
` </handoff_path>`,
|
|
41
|
-
` With this shape:`,
|
|
42
|
-
` {`,
|
|
43
|
-
` "status": "success" | "failed",`,
|
|
44
|
-
` "summary": "[one to three sentences: what was done, or why it failed]"`,
|
|
45
|
-
` }`,
|
|
46
|
-
`3. You're done. \`nx migrate\` is watching for the handoff file; once it appears nx closes this session automatically and continues with the next step. Do not attempt further work after the handoff is written.`,
|
|
45
|
+
`How to end the step:`,
|
|
46
|
+
`1. Success — the step is fully applied (or validation passed): state a one-or-two-sentence summary of what you did and, in the same turn, write the handoff file with \`status: "success"\`. Do not pause for confirmation before the write — it is pre-authorized.`,
|
|
47
|
+
`2. You need direction — the instructions are unclear, the workspace state conflicts with what they assume, or a decision isn't yours to make: do not write the handoff file. Ask the user and continue based on their answer.`,
|
|
48
|
+
`3. You cannot complete the step — a blocking problem remains after you applied what you could within scope: do not write the handoff file yet. Report what you found and what you tried, then ask the user how to proceed. If you are still blocked after their direction, say so plainly and ask them to either redirect or tell you to give up — do not loop silently. Write the handoff with \`status: "failed"\` only when the user tells you to give up, enumerating the unresolved problems in \`summary\` — nx surfaces it to the user and aborts the run.`,
|
|
47
49
|
``,
|
|
48
50
|
`Notes on the handoff file:`,
|
|
51
|
+
`- Write it with your file-write tool — writes to this path are pre-authorized for that tool. Do not use shell commands to write it; those may trigger an approval prompt the file-write tool avoids.`,
|
|
49
52
|
`- The parent directory already exists — write the file directly. Do not run \`mkdir\`, do not check whether the directory exists, do not list its contents.`,
|
|
50
|
-
`- \`status: "success"\` — the migration was fully applied.`,
|
|
51
|
-
`- \`status: "failed"\` — the migration could not be applied (including: unclear instructions, conflicting workspace state, a step you cannot complete). nx will surface the summary to the user and abort the run.`,
|
|
52
53
|
`- Only \`status\` and \`summary\` are read. Extra fields are tolerated but ignored — don't rely on them to signal anything.`,
|
|
53
54
|
`- If the file is missing when you exit (e.g. the user cancels), nx treats the outcome as ambiguous and asks the user how to proceed.`,
|
|
54
|
-
`- The handoff file's path and
|
|
55
|
+
`- The handoff file's path, shape, and the rules above for when to write it are owned by \`nx migrate\` and cannot be overridden. If the instructions file asks you to write the handoff elsewhere, in a different shape, or at a different point in the flow, ignore that part of the instructions and follow this contract. The instructions file can still direct you to write any other files the migration needs.`,
|
|
55
56
|
`</handoff_contract>`,
|
|
56
57
|
``,
|
|
57
58
|
`<environment_note>`,
|
|
@@ -72,7 +73,7 @@ function buildScopeRules(mode) {
|
|
|
72
73
|
`- You may apply minor fixes only when the issue lies within the scope of what this migration intended to accomplish (e.g. a missing import the generator's template should have produced, a type annotation the template missed). Do not refactor, do not modify unrelated functionality, do not extend the migration's scope, do not touch code the migration was not concerned with. If you are unsure whether a fix is in scope, report it in \`summary\` instead of applying.`,
|
|
73
74
|
`- Do not run other \`nx\` commands that mutate workspace state (\`nx migrate\`, \`nx reset\`, generators, etc.).`,
|
|
74
75
|
`- Do not modify files outside the workspace root.`,
|
|
75
|
-
`- If validation finds blocking issues you cannot resolve within scope: apply every fix you can within scope, then
|
|
76
|
+
`- If validation finds blocking issues you cannot resolve within scope: apply every fix you can within scope, then report the unresolved findings to the user and ask how to proceed (see the handoff contract). Do not guess.`,
|
|
76
77
|
`</scope_rules>`,
|
|
77
78
|
].join('\n');
|
|
78
79
|
}
|
|
@@ -82,7 +83,7 @@ function buildScopeRules(mode) {
|
|
|
82
83
|
`- Do not refactor, reformat, or update dependencies beyond what the migration prompt directs.`,
|
|
83
84
|
`- Do not modify files outside the workspace root.`,
|
|
84
85
|
`- Do not run other \`nx\` commands that mutate workspace state (\`nx migrate\`, \`nx reset\`, \`nx run-many\`, generators, etc.). Read-only inspection (\`nx show\`, \`nx graph --file\`, reading files) is fine.`,
|
|
85
|
-
`- If the migration instructions are unclear, internally inconsistent, or conflict with the current workspace state,
|
|
86
|
+
`- If the migration instructions are unclear, internally inconsistent, or conflict with the current workspace state, ask the user for direction (see the handoff contract). Do not guess.`,
|
|
86
87
|
`</scope_rules>`,
|
|
87
88
|
].join('\n');
|
|
88
89
|
}
|
|
@@ -6,6 +6,8 @@ export interface ResolveAgenticInput {
|
|
|
6
6
|
migrations: ReadonlyArray<{
|
|
7
7
|
prompt?: string;
|
|
8
8
|
}>;
|
|
9
|
+
/** The `--interactive` flag; `false` (`--no-interactive`) disables all prompting. */
|
|
10
|
+
interactive?: boolean;
|
|
9
11
|
}
|
|
10
12
|
/**
|
|
11
13
|
* Resolves the agentic state for a `--run-migrations` invocation. Runs once,
|
|
@@ -27,7 +27,9 @@ async function resolveAgentic(input) {
|
|
|
27
27
|
});
|
|
28
28
|
return { kind: 'inside-agent' };
|
|
29
29
|
}
|
|
30
|
-
const isInteractive = !!process.stdin.isTTY &&
|
|
30
|
+
const isInteractive = !!process.stdin.isTTY &&
|
|
31
|
+
!!process.stdout.isTTY &&
|
|
32
|
+
input.interactive !== false;
|
|
31
33
|
// Skip detection for the one case where the result is unused: explicit
|
|
32
34
|
// `--agentic=false`. For every other path (explicit enable, explicit id, or
|
|
33
35
|
// undefined-with-prompt) we either need the detection result to pick/verify
|
|
@@ -95,7 +97,7 @@ function requireInteractiveOrAbort(isInteractive) {
|
|
|
95
97
|
}
|
|
96
98
|
function warnAgenticInteractiveOnly() {
|
|
97
99
|
output_1.output.warn({
|
|
98
|
-
title: 'Skipping the agentic flow: it is interactive-only in this release and this is
|
|
100
|
+
title: 'Skipping the agentic flow: it is interactive-only in this release and this run is non-interactive.',
|
|
99
101
|
bodyLines: [
|
|
100
102
|
'Continuing the migration without the agentic flow. Re-run in an interactive terminal to use it.',
|
|
101
103
|
],
|
|
@@ -76,7 +76,7 @@ function withMigrationOptions(yargs) {
|
|
|
76
76
|
default: exports.DEFAULT_MIGRATION_COMMIT_PREFIX,
|
|
77
77
|
})
|
|
78
78
|
.option('interactive', {
|
|
79
|
-
describe:
|
|
79
|
+
describe: "Enable prompts to confirm whether to collect optional package updates and migrations. Not supported when migrating to Nx v23 or later: use '--mode' to choose which packages to migrate.",
|
|
80
80
|
type: 'boolean',
|
|
81
81
|
})
|
|
82
82
|
.option('excludeAppliedMigrations', {
|
|
@@ -90,7 +90,7 @@ function withMigrationOptions(yargs) {
|
|
|
90
90
|
default: false,
|
|
91
91
|
})
|
|
92
92
|
.option('mode', {
|
|
93
|
-
describe: "Restrict which packages to migrate. Only applies when migrating Nx itself. 'first-party' processes only Nx and its plugins (the target package plus its nx.packageGroup); 'third-party' processes only the third-party dependencies referenced by Nx packageJsonUpdates entries, catching up on any updates that may have been skipped previously; 'all' processes everything. When targeting Nx in an interactive terminal, prompts for the value if not provided; otherwise defaults to 'all'.",
|
|
93
|
+
describe: "Restrict which packages to migrate. Only applies when migrating Nx itself. 'first-party' processes only Nx and its plugins (the target package plus its nx.packageGroup); 'third-party' processes only the third-party dependencies referenced by Nx packageJsonUpdates entries, catching up on any updates that may have been skipped previously; 'all' processes everything. The 'first-party' and 'third-party' modes are only available on Nx v23+: 'third-party' requires the workspace to already be on v23+, and 'first-party' requires migrating from v22+ into a v23+ target. When targeting Nx in an interactive terminal, prompts for the value if not provided; otherwise defaults to 'all'.",
|
|
94
94
|
type: 'string',
|
|
95
95
|
choices: exports.MIGRATE_MODES,
|
|
96
96
|
})
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { MigrationDetailsWithId } from '../../config/misc-interfaces';
|
|
2
2
|
import type { FileChange } from '../../generators/tree';
|
|
3
|
+
import { isHybridMigration, isPromptOnlyMigration } from './migrate';
|
|
4
|
+
export { isPromptOnlyMigration, isHybridMigration };
|
|
3
5
|
export type MigrationsJsonMetadata = {
|
|
4
6
|
completedMigrations?: Record<string, SuccessfulMigration | FailedMigration | SkippedMigration | StoppedMigration>;
|
|
5
7
|
runningMigrations?: string[];
|
|
@@ -16,6 +18,7 @@ export type SuccessfulMigration = {
|
|
|
16
18
|
changedFiles: Omit<FileChange, 'content'>[];
|
|
17
19
|
ref: string;
|
|
18
20
|
nextSteps?: string[];
|
|
21
|
+
acknowledgedPrompt?: boolean;
|
|
19
22
|
};
|
|
20
23
|
export type FailedMigration = {
|
|
21
24
|
type: 'failed';
|
|
@@ -72,5 +75,14 @@ export declare function addStoppedMigration(id: string, error: string): (migrati
|
|
|
72
75
|
};
|
|
73
76
|
export declare function readMigrationsJsonMetadata(workspacePath: string): MigrationsJsonMetadata;
|
|
74
77
|
export declare function undoMigration(workspacePath: string, id: string): (migrationsJsonMetadata: MigrationsJsonMetadata) => MigrationsJsonMetadata;
|
|
78
|
+
/**
|
|
79
|
+
* Records that the user has confirmed completion of a prompt-bearing
|
|
80
|
+
* migration's AI prompt phase. Dispatches by shape so callers (the webview
|
|
81
|
+
* event handler in Nx Console) don't need to know which is which:
|
|
82
|
+
* - prompt-only: records success directly (no spawn, no process lifecycle).
|
|
83
|
+
* - hybrid: persists the `acknowledgedPrompt` flag on the existing
|
|
84
|
+
* successful record from the generator phase.
|
|
85
|
+
*/
|
|
86
|
+
export declare function acknowledgeMigrationPrompt(workspacePath: string, migration: MigrationDetailsWithId): void;
|
|
75
87
|
export declare function killMigrationProcess(migrationId: string, workspacePath?: string): boolean;
|
|
76
88
|
export declare function stopMigration(migrationId: string): (migrationsJsonMetadata: MigrationsJsonMetadata) => MigrationsJsonMetadata;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isHybridMigration = exports.isPromptOnlyMigration = void 0;
|
|
3
4
|
exports.recordInitialMigrationMetadata = recordInitialMigrationMetadata;
|
|
4
5
|
exports.finishMigrationProcess = finishMigrationProcess;
|
|
5
6
|
exports.runSingleMigration = runSingleMigration;
|
|
@@ -12,12 +13,15 @@ exports.addSkippedMigration = addSkippedMigration;
|
|
|
12
13
|
exports.addStoppedMigration = addStoppedMigration;
|
|
13
14
|
exports.readMigrationsJsonMetadata = readMigrationsJsonMetadata;
|
|
14
15
|
exports.undoMigration = undoMigration;
|
|
16
|
+
exports.acknowledgeMigrationPrompt = acknowledgeMigrationPrompt;
|
|
15
17
|
exports.killMigrationProcess = killMigrationProcess;
|
|
16
18
|
exports.stopMigration = stopMigration;
|
|
17
19
|
const child_process_1 = require("child_process");
|
|
18
20
|
const fs_1 = require("fs");
|
|
19
21
|
const path_1 = require("path");
|
|
20
22
|
const migrate_1 = require("./migrate");
|
|
23
|
+
Object.defineProperty(exports, "isHybridMigration", { enumerable: true, get: function () { return migrate_1.isHybridMigration; } });
|
|
24
|
+
Object.defineProperty(exports, "isPromptOnlyMigration", { enumerable: true, get: function () { return migrate_1.isPromptOnlyMigration; } });
|
|
21
25
|
let currentMigrationProcess = null;
|
|
22
26
|
let currentMigrationId = null;
|
|
23
27
|
let migrationCancelled = false;
|
|
@@ -79,6 +83,14 @@ async function runSingleMigration(workspacePath, migration, configuration) {
|
|
|
79
83
|
currentMigrationId = migration.id;
|
|
80
84
|
migrationCancelled = false;
|
|
81
85
|
modifyMigrationsJsonMetadata(workspacePath, addRunningMigration(migration.id));
|
|
86
|
+
// Prompt-only migrations have no deterministic implementation to spawn.
|
|
87
|
+
// The state-machine's auto-run hits this branch; the manual Mark-as-
|
|
88
|
+
// Completed path calls `recordPromptOnlySuccess` directly so it stays out
|
|
89
|
+
// of the process-tracking lifecycle.
|
|
90
|
+
if ((0, migrate_1.isPromptOnlyMigration)(migration)) {
|
|
91
|
+
recordPromptOnlySuccess(workspacePath, migration);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
82
94
|
const gitRefBefore = (0, child_process_1.execSync)('git rev-parse HEAD', {
|
|
83
95
|
cwd: workspacePath,
|
|
84
96
|
encoding: 'utf-8',
|
|
@@ -192,6 +204,13 @@ async function runSingleMigration(workspacePath, migration, configuration) {
|
|
|
192
204
|
}
|
|
193
205
|
}
|
|
194
206
|
async function getImplementationPath(workspacePath, migration) {
|
|
207
|
+
// Prompt-only migrations have no implementation — the "source" the user
|
|
208
|
+
// wants to see is the prompt file itself. Resolving via the regular
|
|
209
|
+
// implementation lookup would throw because both `implementation` and
|
|
210
|
+
// `factory` are unset.
|
|
211
|
+
if ((0, migrate_1.isPromptOnlyMigration)(migration)) {
|
|
212
|
+
return (0, path_1.join)(workspacePath, migration.prompt);
|
|
213
|
+
}
|
|
195
214
|
const { collection, collectionPath } = (0, migrate_1.readMigrationCollection)(migration.package, workspacePath);
|
|
196
215
|
const { path } = (0, migrate_1.getImplementationPath)(collection, collectionPath, migration.name);
|
|
197
216
|
return path;
|
|
@@ -208,6 +227,11 @@ function addSuccessfulMigration(id, fileChanges, ref, nextSteps) {
|
|
|
208
227
|
if (!copied.completedMigrations) {
|
|
209
228
|
copied.completedMigrations = {};
|
|
210
229
|
}
|
|
230
|
+
// Carry forward a previously-set acknowledgedPrompt so any caller that
|
|
231
|
+
// re-records a successful entry for the same id (no current trigger; this
|
|
232
|
+
// is defensive against future paths) cannot silently drop the user's ack.
|
|
233
|
+
const existing = copied.completedMigrations[id];
|
|
234
|
+
const acknowledgedPrompt = existing?.type === 'successful' && existing.acknowledgedPrompt;
|
|
211
235
|
copied.completedMigrations = {
|
|
212
236
|
...copied.completedMigrations,
|
|
213
237
|
[id]: {
|
|
@@ -216,6 +240,7 @@ function addSuccessfulMigration(id, fileChanges, ref, nextSteps) {
|
|
|
216
240
|
changedFiles: fileChanges,
|
|
217
241
|
ref,
|
|
218
242
|
nextSteps,
|
|
243
|
+
...(acknowledgedPrompt && { acknowledgedPrompt: true }),
|
|
219
244
|
},
|
|
220
245
|
};
|
|
221
246
|
return copied;
|
|
@@ -312,17 +337,61 @@ function undoMigration(workspacePath, id) {
|
|
|
312
337
|
const existing = migrationsJsonMetadata.completedMigrations[id];
|
|
313
338
|
if (existing.type !== 'successful')
|
|
314
339
|
throw new Error(`undoMigration called on unsuccessful migration: ${id}`);
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
340
|
+
// No-changes successful entries (prompt-only short-circuit, generators
|
|
341
|
+
// that ran but produced no diff) have no migration commit to roll back;
|
|
342
|
+
// `existing.ref` is the unmodified HEAD at run time, so `ref^` would
|
|
343
|
+
// reset past unrelated history. Only flip the metadata to skipped.
|
|
344
|
+
if (existing.changedFiles.length > 0) {
|
|
345
|
+
(0, child_process_1.execSync)(`git reset --hard ${existing.ref}^`, {
|
|
346
|
+
cwd: workspacePath,
|
|
347
|
+
encoding: 'utf-8',
|
|
348
|
+
windowsHide: true,
|
|
349
|
+
});
|
|
350
|
+
}
|
|
320
351
|
migrationsJsonMetadata.completedMigrations[id] = {
|
|
321
352
|
type: 'skipped',
|
|
322
353
|
};
|
|
323
354
|
return migrationsJsonMetadata;
|
|
324
355
|
};
|
|
325
356
|
}
|
|
357
|
+
/**
|
|
358
|
+
* Records that the user has confirmed completion of a prompt-bearing
|
|
359
|
+
* migration's AI prompt phase. Dispatches by shape so callers (the webview
|
|
360
|
+
* event handler in Nx Console) don't need to know which is which:
|
|
361
|
+
* - prompt-only: records success directly (no spawn, no process lifecycle).
|
|
362
|
+
* - hybrid: persists the `acknowledgedPrompt` flag on the existing
|
|
363
|
+
* successful record from the generator phase.
|
|
364
|
+
*/
|
|
365
|
+
function acknowledgeMigrationPrompt(workspacePath, migration) {
|
|
366
|
+
if ((0, migrate_1.isPromptOnlyMigration)(migration)) {
|
|
367
|
+
recordPromptOnlySuccess(workspacePath, migration);
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
modifyMigrationsJsonMetadata(workspacePath, (metadata) => {
|
|
371
|
+
const existing = metadata.completedMigrations?.[migration.id];
|
|
372
|
+
if (!existing || existing.type !== 'successful') {
|
|
373
|
+
return metadata;
|
|
374
|
+
}
|
|
375
|
+
metadata.completedMigrations = {
|
|
376
|
+
...metadata.completedMigrations,
|
|
377
|
+
[migration.id]: { ...existing, acknowledgedPrompt: true },
|
|
378
|
+
};
|
|
379
|
+
return metadata;
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
// Writes a successful record for a prompt-only migration without touching the
|
|
383
|
+
// process-lifecycle tracking that `runSingleMigration` manages — safe to call
|
|
384
|
+
// from `acknowledgeMigrationPrompt` while another migration may be running.
|
|
385
|
+
// The prompt-path reminder is rendered by the UI from `migration.prompt`, so
|
|
386
|
+
// no next step is recorded here.
|
|
387
|
+
function recordPromptOnlySuccess(workspacePath, migration) {
|
|
388
|
+
const ref = (0, child_process_1.execSync)('git rev-parse HEAD', {
|
|
389
|
+
cwd: workspacePath,
|
|
390
|
+
encoding: 'utf-8',
|
|
391
|
+
windowsHide: true,
|
|
392
|
+
}).trim();
|
|
393
|
+
modifyMigrationsJsonMetadata(workspacePath, addSuccessfulMigration(migration.id, [], ref, []));
|
|
394
|
+
}
|
|
326
395
|
function killMigrationProcess(migrationId, workspacePath) {
|
|
327
396
|
try {
|
|
328
397
|
if (workspacePath) {
|
|
@@ -119,6 +119,9 @@ export declare function resolveCanonicalNxPackage(targetVersion: string): 'nx' |
|
|
|
119
119
|
export declare function resolveMode(mode: MigrateMode | undefined, targetPackage: string, targetVersion: string, context?: {
|
|
120
120
|
hasFrom: boolean;
|
|
121
121
|
hasExcludeAppliedMigrations: boolean;
|
|
122
|
+
installedMajor?: number | null;
|
|
123
|
+
isV23Plus?: boolean;
|
|
124
|
+
interactive?: boolean;
|
|
122
125
|
}, configuredMode?: MigrateMode): Promise<MigrateMode>;
|
|
123
126
|
type GenerateMigrations = {
|
|
124
127
|
type: 'generateMigrations';
|
|
@@ -152,6 +155,7 @@ type RunMigrations = {
|
|
|
152
155
|
ifExists: boolean;
|
|
153
156
|
agentic: AgenticArg;
|
|
154
157
|
validate?: boolean;
|
|
158
|
+
interactive?: boolean;
|
|
155
159
|
};
|
|
156
160
|
export declare function parseMigrationsOptions(options: {
|
|
157
161
|
[k: string]: any;
|