nx 23.0.0-beta.21 → 23.0.0-beta.22
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/src/adapter/compat.d.ts +1 -1
- package/dist/src/adapter/compat.js +1 -0
- package/dist/src/command-line/examples.js +4 -4
- package/dist/src/command-line/migrate/agentic/prompts/generic-validation.d.ts +5 -0
- package/dist/src/command-line/migrate/agentic/prompts/generic-validation.js +1 -0
- package/dist/src/command-line/migrate/agentic/prompts/hybrid-prompt-migration.d.ts +5 -0
- package/dist/src/command-line/migrate/agentic/prompts/hybrid-prompt-migration.js +1 -0
- package/dist/src/command-line/migrate/agentic/prompts/prompt-migration.d.ts +5 -0
- package/dist/src/command-line/migrate/agentic/prompts/prompt-migration.js +1 -0
- package/dist/src/command-line/migrate/agentic/prompts/shared-rendering.d.ts +1 -0
- package/dist/src/command-line/migrate/agentic/prompts/shared-rendering.js +15 -0
- package/dist/src/command-line/migrate/agentic/run-step.d.ts +7 -0
- package/dist/src/command-line/migrate/agentic/run-step.js +3 -1
- package/dist/src/command-line/migrate/agentic/select.js +120 -32
- package/dist/src/command-line/migrate/command-object.d.ts +42 -0
- package/dist/src/command-line/migrate/command-object.js +37 -7
- package/dist/src/command-line/migrate/migrate-config.d.ts +27 -0
- package/dist/src/command-line/migrate/migrate-config.js +103 -0
- package/dist/src/command-line/migrate/migrate.d.ts +37 -2
- package/dist/src/command-line/migrate/migrate.js +97 -8
- package/dist/src/command-line/release/changelog/version-plan-filtering.d.ts +3 -1
- package/dist/src/command-line/release/changelog/version-plan-filtering.js +7 -3
- package/dist/src/command-line/release/changelog.d.ts +7 -0
- package/dist/src/command-line/release/changelog.js +22 -9
- package/dist/src/command-line/release/release.js +65 -55
- package/dist/src/command-line/release/utils/git.d.ts +6 -0
- package/dist/src/command-line/release/utils/git.js +33 -0
- package/dist/src/command-line/release/version/derive-specifier-from-conventional-commits.js +3 -2
- package/dist/src/command-line/release/version.d.ts +3 -0
- package/dist/src/command-line/release/version.js +13 -3
- package/dist/src/config/misc-interfaces.d.ts +8 -0
- package/dist/src/config/nx-json.d.ts +49 -0
- package/dist/src/core/graph/main.js +1 -1
- 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/lock-file.d.ts +5 -0
- package/dist/src/plugins/js/lock-file/lock-file.js +34 -24
- package/dist/src/plugins/js/project-graph/affected/lock-file-changes.d.ts +2 -4
- package/dist/src/plugins/js/project-graph/affected/lock-file-changes.js +121 -43
- package/dist/src/project-graph/file-utils.d.ts +7 -0
- package/dist/src/project-graph/file-utils.js +78 -10
- package/dist/src/tasks-runner/init-tasks-runner.d.ts +2 -2
- package/dist/src/tasks-runner/init-tasks-runner.js +6 -6
- package/dist/src/tasks-runner/task-orchestrator.d.ts +2 -2
- package/dist/src/tasks-runner/task-orchestrator.js +6 -6
- package/migrations.json +18 -9
- package/package.json +11 -11
- package/schemas/nx-schema.json +41 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export declare const allowedProjectExtensions: readonly ["tags", "implicitDependencies", "configFilePath", "$schema", "generators", "namedInputs", "name", "files", "root", "sourceRoot", "projectType", "release", "includedScripts", "metadata", "owners", "nxCloudImplicitDependencies"];
|
|
2
|
-
export declare const allowedWorkspaceExtensions: readonly ["$schema", "implicitDependencies", "affected", "defaultBase", "tasksRunnerOptions", "workspaceLayout", "plugins", "targetDefaults", "files", "generators", "namedInputs", "extends", "cli", "pluginsConfig", "defaultProject", "installation", "release", "nxCloudAccessToken", "nxCloudId", "nxCloudUrl", "nxCloudEncryptionKey", "parallel", "cacheDirectory", "useDaemonProcess", "useInferencePlugins", "neverConnectToCloud", "analytics", "sync", "useLegacyCache", "maxCacheSize", "tui", "owners"];
|
|
2
|
+
export declare const allowedWorkspaceExtensions: readonly ["$schema", "implicitDependencies", "affected", "defaultBase", "tasksRunnerOptions", "workspaceLayout", "plugins", "targetDefaults", "files", "generators", "namedInputs", "extends", "cli", "pluginsConfig", "defaultProject", "installation", "release", "nxCloudAccessToken", "nxCloudId", "nxCloudUrl", "nxCloudEncryptionKey", "parallel", "cacheDirectory", "useDaemonProcess", "useInferencePlugins", "neverConnectToCloud", "analytics", "sync", "migrate", "useLegacyCache", "maxCacheSize", "tui", "owners"];
|
|
@@ -356,19 +356,19 @@ exports.examples = {
|
|
|
356
356
|
description: 'Prints the specified + inferred configuration for `my-app:build`',
|
|
357
357
|
},
|
|
358
358
|
{
|
|
359
|
-
command: 'show target my-app:build
|
|
359
|
+
command: 'show target inputs my-app:build',
|
|
360
360
|
description: 'Prints the resolved inputs for `my-app:build`',
|
|
361
361
|
},
|
|
362
362
|
{
|
|
363
|
-
command: 'show target my-app:build
|
|
363
|
+
command: 'show target inputs my-app:build --check packages/my-app/index.html',
|
|
364
364
|
description: 'Checks if `packages/my-app/index.html` is an input for `my-app:build`',
|
|
365
365
|
},
|
|
366
366
|
{
|
|
367
|
-
command: 'show target my-app:build
|
|
367
|
+
command: 'show target outputs my-app:build',
|
|
368
368
|
description: 'Prints the outputs detected on disk for `my-app:build`',
|
|
369
369
|
},
|
|
370
370
|
{
|
|
371
|
-
command: 'show target my-app:build
|
|
371
|
+
command: 'show target outputs my-app:build --check packages/my-app/dist/index.html',
|
|
372
372
|
description: 'Checks if `packages/my-app/dist/index.html` is an output for `my-app:build`',
|
|
373
373
|
},
|
|
374
374
|
],
|
|
@@ -6,6 +6,11 @@ export interface GenericValidationPromptContext {
|
|
|
6
6
|
description?: string;
|
|
7
7
|
/** Absolute path the agent must write its handoff file to. */
|
|
8
8
|
handoffFileAbsolutePath: string;
|
|
9
|
+
/**
|
|
10
|
+
* Path to the migration's documentation file, if any - workspace-relative,
|
|
11
|
+
* or absolute when it resolves outside the workspace.
|
|
12
|
+
*/
|
|
13
|
+
documentationPath?: string;
|
|
9
14
|
/** Context captured from the deterministic generator phase. */
|
|
10
15
|
impl: {
|
|
11
16
|
/** Raw output from the generator (devkit logger + console). */
|
|
@@ -29,6 +29,7 @@ function buildGenericValidationUserPrompt(ctx) {
|
|
|
29
29
|
`You are validating the output of an Nx migration's deterministic generator phase. The generator has already run; inspect what it produced, verify the workspace is in a consistent state for what this migration intended to accomplish, apply any minor in-scope fixes the generator should have produced cleanly, and report findings.`,
|
|
30
30
|
...(0, shared_rendering_1.renderMigrationBlock)(ctx),
|
|
31
31
|
];
|
|
32
|
+
lines.push(...(0, shared_rendering_1.renderMigrationDocumentationBlock)(ctx.documentationPath));
|
|
32
33
|
const logs = (0, shared_rendering_1.escapeXmlBody)((0, shared_rendering_1.stripAnsi)(ctx.impl.logs ?? '').trim());
|
|
33
34
|
lines.push(...(0, shared_rendering_1.renderGeneratorOutputBlock)(logs));
|
|
34
35
|
if (!ctx.impl.hasDiffContext && ctx.impl.changes.length > 0) {
|
|
@@ -8,6 +8,11 @@ export interface HybridPromptMigrationContext {
|
|
|
8
8
|
promptPath: string;
|
|
9
9
|
/** Absolute path the agent must write its handoff file to. */
|
|
10
10
|
handoffFileAbsolutePath: string;
|
|
11
|
+
/**
|
|
12
|
+
* Path to the migration's documentation file, if any - workspace-relative,
|
|
13
|
+
* or absolute when it resolves outside the workspace.
|
|
14
|
+
*/
|
|
15
|
+
documentationPath?: string;
|
|
11
16
|
/** Context captured from the deterministic generator phase. */
|
|
12
17
|
impl?: {
|
|
13
18
|
/** Raw output from the generator (devkit logger + console). */
|
|
@@ -20,6 +20,7 @@ function buildHybridPromptUserPrompt(ctx) {
|
|
|
20
20
|
`Complete the AI-driven step that follows the generator phase of a two-phase Nx migration. The deterministic generator phase has already run; the sections below summarize what it did. The step may apply additional changes, verify the generator's output, or both — follow the instructions file.`,
|
|
21
21
|
...(0, shared_rendering_1.renderMigrationBlock)(ctx),
|
|
22
22
|
];
|
|
23
|
+
lines.push(...(0, shared_rendering_1.renderMigrationDocumentationBlock)(ctx.documentationPath));
|
|
23
24
|
const logs = (0, shared_rendering_1.escapeXmlBody)((0, shared_rendering_1.stripAnsi)(ctx.impl?.logs ?? '').trim());
|
|
24
25
|
const agentContext = (0, shared_rendering_1.filterNonEmptyStrings)(ctx.impl?.agentContext ?? []);
|
|
25
26
|
const hasDiffContext = !!ctx.impl?.hasDiffContext;
|
|
@@ -7,6 +7,11 @@ export interface PromptMigrationContext {
|
|
|
7
7
|
promptPath: string;
|
|
8
8
|
/** Absolute path the agent must write its handoff file to. */
|
|
9
9
|
handoffFileAbsolutePath: string;
|
|
10
|
+
/**
|
|
11
|
+
* Path to the migration's documentation file, if any - workspace-relative,
|
|
12
|
+
* or absolute when it resolves outside the workspace.
|
|
13
|
+
*/
|
|
14
|
+
documentationPath?: string;
|
|
10
15
|
}
|
|
11
16
|
/**
|
|
12
17
|
* Builds the first user-facing message for a prompt-migration step.
|
|
@@ -16,6 +16,7 @@ function buildPromptMigrationUserPrompt(ctx) {
|
|
|
16
16
|
const lines = [
|
|
17
17
|
`Apply one prompt-based migration to this Nx workspace.`,
|
|
18
18
|
...(0, shared_rendering_1.renderMigrationBlock)(ctx),
|
|
19
|
+
...(0, shared_rendering_1.renderMigrationDocumentationBlock)(ctx.documentationPath),
|
|
19
20
|
``,
|
|
20
21
|
`<instructions_file>${(0, shared_rendering_1.escapeXmlBody)(ctx.promptPath)}</instructions_file>`,
|
|
21
22
|
``,
|
|
@@ -16,3 +16,4 @@ export interface MigrationBlockContext {
|
|
|
16
16
|
export declare function renderMigrationBlock(ctx: MigrationBlockContext): string[];
|
|
17
17
|
export declare function renderHandoffPathFooter(handoffFileAbsolutePath: string): string[];
|
|
18
18
|
export declare function renderAdvisoryContext(note: string, entries: string[]): string[];
|
|
19
|
+
export declare function renderMigrationDocumentationBlock(documentationPath: string | undefined): string[];
|
|
@@ -11,6 +11,7 @@ exports.renderGeneratorOutputBlock = renderGeneratorOutputBlock;
|
|
|
11
11
|
exports.renderMigrationBlock = renderMigrationBlock;
|
|
12
12
|
exports.renderHandoffPathFooter = renderHandoffPathFooter;
|
|
13
13
|
exports.renderAdvisoryContext = renderAdvisoryContext;
|
|
14
|
+
exports.renderMigrationDocumentationBlock = renderMigrationDocumentationBlock;
|
|
14
15
|
function renderFileEntry(change) {
|
|
15
16
|
return `[${change.type}] ${change.path}`;
|
|
16
17
|
}
|
|
@@ -128,3 +129,17 @@ function renderAdvisoryContext(note, entries) {
|
|
|
128
129
|
`</advisory_context>`,
|
|
129
130
|
];
|
|
130
131
|
}
|
|
132
|
+
// Points the agent at the migration's documentation file (reference, not
|
|
133
|
+
// instructions). Returns `[]` when there's no doc so callers can spread without
|
|
134
|
+
// guarding; the path is escaped like other user-authored values interpolated
|
|
135
|
+
// into the prompt.
|
|
136
|
+
function renderMigrationDocumentationBlock(documentationPath) {
|
|
137
|
+
if (!documentationPath)
|
|
138
|
+
return [];
|
|
139
|
+
return [
|
|
140
|
+
``,
|
|
141
|
+
`<migration_documentation note="reference: documents what this migration does; read this file if you need more context on its intent, not as instructions">`,
|
|
142
|
+
escapeXmlBody(documentationPath),
|
|
143
|
+
`</migration_documentation>`,
|
|
144
|
+
];
|
|
145
|
+
}
|
|
@@ -31,6 +31,13 @@ export interface RunAgenticPromptStepInput {
|
|
|
31
31
|
description?: string;
|
|
32
32
|
prompt?: string;
|
|
33
33
|
};
|
|
34
|
+
/**
|
|
35
|
+
* Path to the migration's documentation file, resolved by the orchestrator -
|
|
36
|
+
* workspace-relative, or absolute when the file resolves outside the
|
|
37
|
+
* workspace. Omitted when the migration declares no `documentation` or it
|
|
38
|
+
* can't be resolved.
|
|
39
|
+
*/
|
|
40
|
+
documentationPath?: string;
|
|
34
41
|
agentic: EnabledResolvedAgentic;
|
|
35
42
|
runDir: string;
|
|
36
43
|
installDepsIfChanged: () => Promise<void>;
|
|
@@ -27,7 +27,7 @@ const runner_1 = require("./runner");
|
|
|
27
27
|
* the file free of cross-imports with the orchestrator.
|
|
28
28
|
*/
|
|
29
29
|
async function runAgenticPromptStep(input) {
|
|
30
|
-
const { root, migration, agentic, runDir, installDepsIfChanged, implContext, mode = 'author', } = input;
|
|
30
|
+
const { root, migration, agentic, runDir, installDepsIfChanged, implContext, documentationPath, mode = 'author', } = input;
|
|
31
31
|
const handoffFilePath = (0, handoff_1.stepHandoffPath)(runDir, migration);
|
|
32
32
|
// The system prompt tells the agent the parent dir exists, so the agent
|
|
33
33
|
// doesn't defensively `mkdir -p` (which triggers a workspace-permission
|
|
@@ -52,6 +52,7 @@ async function runAgenticPromptStep(input) {
|
|
|
52
52
|
version: migration.version,
|
|
53
53
|
description: migration.description,
|
|
54
54
|
handoffFileAbsolutePath: handoffFilePath,
|
|
55
|
+
documentationPath,
|
|
55
56
|
impl: implContext,
|
|
56
57
|
});
|
|
57
58
|
}
|
|
@@ -63,6 +64,7 @@ async function runAgenticPromptStep(input) {
|
|
|
63
64
|
description: migration.description,
|
|
64
65
|
promptPath: migration.prompt,
|
|
65
66
|
handoffFileAbsolutePath: handoffFilePath,
|
|
67
|
+
documentationPath,
|
|
66
68
|
};
|
|
67
69
|
userPrompt = implContext
|
|
68
70
|
? (0, hybrid_prompt_migration_1.buildHybridPromptUserPrompt)({ ...promptCtx, impl: implContext })
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.resolveAgentic = resolveAgentic;
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const jsonc_parser_1 = require("jsonc-parser");
|
|
6
|
+
const path_1 = require("path");
|
|
4
7
|
const output_1 = require("../../../utils/output");
|
|
8
|
+
const workspace_root_1 = require("../../../utils/workspace-root");
|
|
5
9
|
const safe_prompt_1 = require("../safe-prompt");
|
|
6
10
|
const detect_installed_1 = require("./detect-installed");
|
|
7
11
|
const inception_1 = require("./inception");
|
|
@@ -32,20 +36,35 @@ async function resolveAgentic(input) {
|
|
|
32
36
|
return { kind: 'disabled' };
|
|
33
37
|
}
|
|
34
38
|
const detected = await (0, detect_installed_1.detectInstalledAgents)(definitions_1.AGENT_DEFINITIONS);
|
|
35
|
-
const { enabled, explicitId } = await resolveFlag(input, isInteractive, detected);
|
|
39
|
+
const { enabled, explicitId, persist } = await resolveFlag(input, isInteractive, detected);
|
|
36
40
|
if (!enabled) {
|
|
41
|
+
if (persist === false) {
|
|
42
|
+
persistAgenticChoice(false);
|
|
43
|
+
}
|
|
37
44
|
return { kind: 'disabled' };
|
|
38
45
|
}
|
|
39
46
|
const selected = await selectAgent(detected, explicitId, isInteractive);
|
|
47
|
+
if (persist === true) {
|
|
48
|
+
persistAgenticChoice(true);
|
|
49
|
+
}
|
|
50
|
+
else if (persist === 'pin') {
|
|
51
|
+
persistAgenticChoice(selected.id);
|
|
52
|
+
}
|
|
40
53
|
return { kind: 'enabled', selectedAgent: selected };
|
|
41
54
|
}
|
|
42
55
|
async function resolveFlag(input, isInteractive, detected) {
|
|
43
56
|
if (input.agentic === true) {
|
|
44
|
-
|
|
57
|
+
if (!isInteractive) {
|
|
58
|
+
warnAgenticInteractiveOnly();
|
|
59
|
+
return { enabled: false };
|
|
60
|
+
}
|
|
45
61
|
return { enabled: true };
|
|
46
62
|
}
|
|
47
63
|
if (typeof input.agentic === 'string') {
|
|
48
|
-
|
|
64
|
+
if (!isInteractive) {
|
|
65
|
+
warnAgenticInteractiveOnly();
|
|
66
|
+
return { enabled: false };
|
|
67
|
+
}
|
|
49
68
|
return { enabled: true, explicitId: input.agentic };
|
|
50
69
|
}
|
|
51
70
|
// undefined — fire the up-front prompt only when we have a TTY, something
|
|
@@ -61,7 +80,7 @@ async function resolveFlag(input, isInteractive, detected) {
|
|
|
61
80
|
if (detected.length === 0) {
|
|
62
81
|
return { enabled: false };
|
|
63
82
|
}
|
|
64
|
-
return
|
|
83
|
+
return firePromptForAgentic(input.migrations, detected);
|
|
65
84
|
}
|
|
66
85
|
function requireInteractiveOrAbort(isInteractive) {
|
|
67
86
|
if (isInteractive)
|
|
@@ -74,29 +93,102 @@ function requireInteractiveOrAbort(isInteractive) {
|
|
|
74
93
|
});
|
|
75
94
|
throw new Error('Agentic flow requires an interactive terminal.');
|
|
76
95
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
96
|
+
function warnAgenticInteractiveOnly() {
|
|
97
|
+
output_1.output.warn({
|
|
98
|
+
title: 'Skipping the agentic flow: it is interactive-only in this release and this is a non-interactive terminal.',
|
|
99
|
+
bodyLines: [
|
|
100
|
+
'Continuing the migration without the agentic flow. Re-run in an interactive terminal to use it.',
|
|
101
|
+
],
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
async function firePromptForAgentic(migrations, detected) {
|
|
105
|
+
// The apply hint assumes at least one prompt-bearing migration is queued. If
|
|
106
|
+
// we later extend the prompt to fire for generator-only runs (validation-
|
|
107
|
+
// only), the hint needs to branch.
|
|
81
108
|
const promptCount = migrations.filter((m) => !!m.prompt).length;
|
|
82
|
-
const
|
|
83
|
-
const
|
|
109
|
+
const applyHint = `Apply ${promptCount} prompt migration${promptCount === 1 ? '' : 's'} and validate generator output with an AI agent`;
|
|
110
|
+
const skipHint = `Skip prompts and run generators without AI validation`;
|
|
111
|
+
const rememberHint = `Saved to nx.json so Nx won't ask again`;
|
|
112
|
+
// The pin-vs-flexible distinction only matters when more than one agent is
|
|
113
|
+
// installed. With a single agent, "always" simply persists `true` and the
|
|
114
|
+
// pin option is dropped.
|
|
115
|
+
const multipleAgents = detected.length > 1;
|
|
116
|
+
const choices = [
|
|
117
|
+
{ name: 'yes-once', message: 'Yes, just this time', hint: applyHint },
|
|
118
|
+
{
|
|
119
|
+
name: 'yes-flex',
|
|
120
|
+
message: multipleAgents
|
|
121
|
+
? "Yes, always (I'll pick the agent each run)"
|
|
122
|
+
: 'Yes, always',
|
|
123
|
+
hint: rememberHint,
|
|
124
|
+
},
|
|
125
|
+
...(multipleAgents
|
|
126
|
+
? [
|
|
127
|
+
{
|
|
128
|
+
name: 'yes-pin',
|
|
129
|
+
message: 'Yes, always with the same agent',
|
|
130
|
+
hint: rememberHint,
|
|
131
|
+
},
|
|
132
|
+
]
|
|
133
|
+
: []),
|
|
134
|
+
{ name: 'no-once', message: 'No, just this time', hint: skipHint },
|
|
135
|
+
{ name: 'no-never', message: 'No, never', hint: rememberHint },
|
|
136
|
+
];
|
|
84
137
|
// Blank line keeps the prompt from gluing to the previous `npm install`
|
|
85
138
|
// output or any earlier orchestrator line.
|
|
86
139
|
console.log();
|
|
87
140
|
// `as any` because enquirer's TS types lag the runtime (per-choice `hint`
|
|
88
|
-
//
|
|
141
|
+
// is supported but not in the .d.ts).
|
|
89
142
|
const response = await (0, safe_prompt_1.migratePrompt)({
|
|
90
|
-
name: '
|
|
91
|
-
type: '
|
|
143
|
+
name: 'choice',
|
|
144
|
+
type: 'select',
|
|
92
145
|
message: 'Enable the agentic flow?',
|
|
93
|
-
choices
|
|
94
|
-
{ name: 'yes', message: 'Yes', hint: yesHint },
|
|
95
|
-
{ name: 'no', message: 'No', hint: noHint },
|
|
96
|
-
],
|
|
146
|
+
choices,
|
|
97
147
|
initial: 0,
|
|
98
148
|
});
|
|
99
|
-
|
|
149
|
+
switch (response.choice) {
|
|
150
|
+
case 'yes-once':
|
|
151
|
+
return { enabled: true };
|
|
152
|
+
case 'yes-flex':
|
|
153
|
+
return { enabled: true, persist: true };
|
|
154
|
+
case 'yes-pin':
|
|
155
|
+
return { enabled: true, persist: 'pin' };
|
|
156
|
+
case 'no-never':
|
|
157
|
+
return { enabled: false, persist: false };
|
|
158
|
+
case 'no-once':
|
|
159
|
+
default:
|
|
160
|
+
return { enabled: false };
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
// Persists the user's agentic choice to `nx.json` so the up-front prompt is
|
|
164
|
+
// skipped on future runs. Edits the raw file in place via jsonc-parser, so it
|
|
165
|
+
// touches only the `migrate.agentic` key and preserves comments, formatting,
|
|
166
|
+
// and any `extends` preset (the prompt fires mid-migration, so a silent
|
|
167
|
+
// reformat would be surprising). Never throws - a failed write only costs the
|
|
168
|
+
// user the prompt again next time.
|
|
169
|
+
function persistAgenticChoice(value) {
|
|
170
|
+
const nxJsonPath = (0, path_1.join)(workspace_root_1.workspaceRoot, 'nx.json');
|
|
171
|
+
if (!(0, fs_1.existsSync)(nxJsonPath)) {
|
|
172
|
+
output_1.output.warn({
|
|
173
|
+
title: `Could not save your agentic choice: no nx.json found at the workspace root.`,
|
|
174
|
+
});
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
try {
|
|
178
|
+
const content = (0, fs_1.readFileSync)(nxJsonPath, 'utf-8');
|
|
179
|
+
const edits = (0, jsonc_parser_1.modify)(content, ['migrate', 'agentic'], value, {
|
|
180
|
+
formattingOptions: { insertSpaces: true, tabSize: 2 },
|
|
181
|
+
});
|
|
182
|
+
(0, fs_1.writeFileSync)(nxJsonPath, (0, jsonc_parser_1.applyEdits)(content, edits));
|
|
183
|
+
output_1.output.log({
|
|
184
|
+
title: `Saved your choice to nx.json (migrate.agentic = ${JSON.stringify(value)}). Nx won't ask again.`,
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
catch (e) {
|
|
188
|
+
output_1.output.warn({
|
|
189
|
+
title: `Could not save your agentic choice to nx.json: ${e instanceof Error ? e.message : String(e)}`,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
100
192
|
}
|
|
101
193
|
async function selectAgent(detected, explicitId, isInteractive) {
|
|
102
194
|
if (explicitId) {
|
|
@@ -104,22 +196,18 @@ async function selectAgent(detected, explicitId, isInteractive) {
|
|
|
104
196
|
if (match) {
|
|
105
197
|
return match;
|
|
106
198
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
199
|
+
// The requested agent isn't installed. Rather than aborting the migration,
|
|
200
|
+
// warn and fall through to resolve from the agents that ARE installed
|
|
201
|
+
// (pick when 2+, auto-select the only one, error only when none exist).
|
|
202
|
+
if (detected.length > 0) {
|
|
203
|
+
output_1.output.warn({
|
|
204
|
+
title: `The requested agent "${explicitId}" is not installed; using the installed agent(s) instead.`,
|
|
205
|
+
bodyLines: [
|
|
206
|
+
'Currently installed agents:',
|
|
207
|
+
...detected.map((d) => ` - ${d.displayName} (${d.id})`),
|
|
208
|
+
],
|
|
111
209
|
});
|
|
112
|
-
throw new Error(`The requested agent "${explicitId}" is not installed.`);
|
|
113
210
|
}
|
|
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
211
|
}
|
|
124
212
|
if (detected.length === 0) {
|
|
125
213
|
output_1.output.error({
|
|
@@ -1,4 +1,46 @@
|
|
|
1
1
|
import { CommandModule } from 'yargs';
|
|
2
|
+
import type { AgenticArg } from './agentic/select';
|
|
2
3
|
export declare const yargsMigrateCommand: CommandModule;
|
|
3
4
|
export declare const yargsInternalMigrateCommand: CommandModule;
|
|
4
5
|
export declare const DEFAULT_MIGRATION_COMMIT_PREFIX = "chore: [nx migration] ";
|
|
6
|
+
/** Allowed values for `--mode` / `migrate.mode`. */
|
|
7
|
+
export declare const MIGRATE_MODES: readonly ["first-party", "third-party", "all"];
|
|
8
|
+
export type MigrateMode = (typeof MIGRATE_MODES)[number];
|
|
9
|
+
/** Allowed values for `--multi-major-mode` / `migrate.multiMajorMode`. */
|
|
10
|
+
export declare const MULTI_MAJOR_MODES: readonly ["direct", "gradual"];
|
|
11
|
+
export type MultiMajorMode = (typeof MULTI_MAJOR_MODES)[number];
|
|
12
|
+
/**
|
|
13
|
+
* The `nx migrate` args bag. Types the keys the nx.json overlay and the
|
|
14
|
+
* commit-prefix invariant read/write; the index signature keeps the rest of
|
|
15
|
+
* the yargs args flowing through untouched.
|
|
16
|
+
*/
|
|
17
|
+
export interface MigrateArgs {
|
|
18
|
+
packageAndVersion?: string;
|
|
19
|
+
runMigrations?: string;
|
|
20
|
+
mode?: MigrateMode;
|
|
21
|
+
/**
|
|
22
|
+
* nx.json `migrate.mode` default. Consumed by `resolveMode` only when the
|
|
23
|
+
* target is Nx itself; kept separate from `mode` so it is never mistaken for
|
|
24
|
+
* an explicit `--mode` (which would hard-fail for non-Nx targets).
|
|
25
|
+
*/
|
|
26
|
+
modeFromConfig?: MigrateMode;
|
|
27
|
+
multiMajorMode?: MultiMajorMode;
|
|
28
|
+
createCommits?: boolean;
|
|
29
|
+
commitPrefix?: string;
|
|
30
|
+
agentic?: AgenticArg;
|
|
31
|
+
validate?: boolean;
|
|
32
|
+
[key: string]: any;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Whether a custom commit prefix would be silently ignored: commits aren't
|
|
36
|
+
* enabled and the agentic flow can't enable them either. Shared by the yargs
|
|
37
|
+
* `.check()` (CLI args) and the nx.json overlay (merged args) so the rule lives
|
|
38
|
+
* in one place. `agentic` may flip commits on by default, so a configured
|
|
39
|
+
* agentic value (other than `false`, and not paired with `--no-create-commits`)
|
|
40
|
+
* keeps the prefix in play.
|
|
41
|
+
*/
|
|
42
|
+
export declare function customCommitPrefixHasNoEffect(args: {
|
|
43
|
+
createCommits: boolean | undefined;
|
|
44
|
+
commitPrefix: string | undefined;
|
|
45
|
+
agentic: unknown;
|
|
46
|
+
}): boolean;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DEFAULT_MIGRATION_COMMIT_PREFIX = exports.yargsInternalMigrateCommand = exports.yargsMigrateCommand = void 0;
|
|
3
|
+
exports.MULTI_MAJOR_MODES = exports.MIGRATE_MODES = exports.DEFAULT_MIGRATION_COMMIT_PREFIX = exports.yargsInternalMigrateCommand = exports.yargsMigrateCommand = void 0;
|
|
4
|
+
exports.customCommitPrefixHasNoEffect = customCommitPrefixHasNoEffect;
|
|
4
5
|
const handle_import_1 = require("../../utils/handle-import");
|
|
5
6
|
const documentation_1 = require("../yargs-utils/documentation");
|
|
6
7
|
const shared_options_1 = require("../yargs-utils/shared-options");
|
|
@@ -20,6 +21,27 @@ exports.yargsInternalMigrateCommand = {
|
|
|
20
21
|
handler: async (args) => process.exit(await (await (0, handle_import_1.handleImport)('./migrate.js', __dirname)).migrate(process.cwd(), args, process.argv.slice(3))),
|
|
21
22
|
};
|
|
22
23
|
exports.DEFAULT_MIGRATION_COMMIT_PREFIX = 'chore: [nx migration] ';
|
|
24
|
+
/** Allowed values for `--mode` / `migrate.mode`. */
|
|
25
|
+
exports.MIGRATE_MODES = ['first-party', 'third-party', 'all'];
|
|
26
|
+
/** Allowed values for `--multi-major-mode` / `migrate.multiMajorMode`. */
|
|
27
|
+
exports.MULTI_MAJOR_MODES = ['direct', 'gradual'];
|
|
28
|
+
/**
|
|
29
|
+
* Whether a custom commit prefix would be silently ignored: commits aren't
|
|
30
|
+
* enabled and the agentic flow can't enable them either. Shared by the yargs
|
|
31
|
+
* `.check()` (CLI args) and the nx.json overlay (merged args) so the rule lives
|
|
32
|
+
* in one place. `agentic` may flip commits on by default, so a configured
|
|
33
|
+
* agentic value (other than `false`, and not paired with `--no-create-commits`)
|
|
34
|
+
* keeps the prefix in play.
|
|
35
|
+
*/
|
|
36
|
+
function customCommitPrefixHasNoEffect(args) {
|
|
37
|
+
const agenticMayEnableCommits = args.agentic !== undefined &&
|
|
38
|
+
args.agentic !== false &&
|
|
39
|
+
args.createCommits !== false;
|
|
40
|
+
return (args.createCommits !== true &&
|
|
41
|
+
!agenticMayEnableCommits &&
|
|
42
|
+
args.commitPrefix !== undefined &&
|
|
43
|
+
args.commitPrefix !== exports.DEFAULT_MIGRATION_COMMIT_PREFIX);
|
|
44
|
+
}
|
|
23
45
|
function withMigrationOptions(yargs) {
|
|
24
46
|
return (0, shared_options_1.withVerbose)(yargs)
|
|
25
47
|
.positional('packageAndVersion', {
|
|
@@ -70,12 +92,12 @@ function withMigrationOptions(yargs) {
|
|
|
70
92
|
.option('mode', {
|
|
71
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'.",
|
|
72
94
|
type: 'string',
|
|
73
|
-
choices:
|
|
95
|
+
choices: exports.MIGRATE_MODES,
|
|
74
96
|
})
|
|
75
97
|
.option('multiMajorMode', {
|
|
76
98
|
describe: "Skip the multi-major migration prompt/warning and pick how to handle the jump. 'direct' migrates straight to the requested target. 'gradual' migrates to the smallest recommended step (re-run `nx migrate` to continue toward the original target). Equivalent env var: NX_MULTI_MAJOR_MODE=direct|gradual.",
|
|
77
99
|
type: 'string',
|
|
78
|
-
choices:
|
|
100
|
+
choices: exports.MULTI_MAJOR_MODES,
|
|
79
101
|
})
|
|
80
102
|
.option('agentic', {
|
|
81
103
|
describe: 'Enable the agentic flow for prompt-based migrations and AI-driven review. Pass `--agentic=<agent>` to pin a specific agent (claude-code, codex, or opencode). Pass `--agentic=false` or `--no-agentic` to disable.',
|
|
@@ -86,10 +108,18 @@ function withMigrationOptions(yargs) {
|
|
|
86
108
|
type: 'boolean',
|
|
87
109
|
})
|
|
88
110
|
.check(({ createCommits, commitPrefix, from, excludeAppliedMigrations, mode, agentic, }) => {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
111
|
+
// Only an explicit `--no-create-commits` is decidable here, before the
|
|
112
|
+
// nx.json overlay runs: an explicit `false` can't be rescued by nx.json
|
|
113
|
+
// (the CLI flag wins, and the agentic flow can't enable commits when
|
|
114
|
+
// they're explicitly off). When `createCommits` is undefined, nx.json
|
|
115
|
+
// may still enable commits, so defer to the post-overlay
|
|
116
|
+
// `assertCommitPrefixHasCommits` check.
|
|
117
|
+
if (createCommits === false &&
|
|
118
|
+
customCommitPrefixHasNoEffect({
|
|
119
|
+
createCommits,
|
|
120
|
+
commitPrefix,
|
|
121
|
+
agentic,
|
|
122
|
+
})) {
|
|
93
123
|
throw new Error('Error: Providing a custom commit prefix requires --create-commits to be enabled');
|
|
94
124
|
}
|
|
95
125
|
if (excludeAppliedMigrations && !from && mode !== 'third-party') {
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { NxMigrateConfiguration } from '../../config/nx-json';
|
|
2
|
+
import { type MigrateArgs } from './command-object';
|
|
3
|
+
/**
|
|
4
|
+
* Overlays `nx.json` `migrate` defaults onto the raw `nx migrate` CLI args so a
|
|
5
|
+
* CLI flag always wins, then `nx.json`, then the built-in default. Returns a new
|
|
6
|
+
* args object; the input is not mutated.
|
|
7
|
+
*
|
|
8
|
+
* Phase-aware: generate-only options (`mode`, `multiMajorMode`) are applied only
|
|
9
|
+
* when not running migrations; run-only options (`createCommits`,
|
|
10
|
+
* `commitPrefix`, `agentic`, `validate`) only when running migrations. This
|
|
11
|
+
* mirrors where each option is consumed and avoids tripping the "cannot be
|
|
12
|
+
* combined with --run-migrations" guards in `parseMigrationsOptions`.
|
|
13
|
+
*
|
|
14
|
+
* `mode` is carried as `modeFromConfig` rather than `mode` so it is never
|
|
15
|
+
* mistaken for an explicit `--mode`: `resolveMode` applies it only when the
|
|
16
|
+
* target is Nx itself, leaving `nx migrate <non-nx-pkg>` unaffected.
|
|
17
|
+
*/
|
|
18
|
+
export declare function applyNxJsonMigrateDefaults(args: MigrateArgs, migrateConfig: NxMigrateConfiguration | undefined, env?: NodeJS.ProcessEnv): MigrateArgs;
|
|
19
|
+
/**
|
|
20
|
+
* The single authority for the "a custom commit prefix needs commits enabled"
|
|
21
|
+
* invariant. Run it against the final merged args (after
|
|
22
|
+
* `applyNxJsonMigrateDefaults`): the yargs `.check()` only sees the CLI args,
|
|
23
|
+
* but nx.json may enable commits via `createCommits` or `agentic`. The CLI
|
|
24
|
+
* `.check()` only fast-fails the unrescuable explicit `--no-create-commits`
|
|
25
|
+
* case; everything else is decided here.
|
|
26
|
+
*/
|
|
27
|
+
export declare function assertCommitPrefixHasCommits(merged: MigrateArgs): void;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.applyNxJsonMigrateDefaults = applyNxJsonMigrateDefaults;
|
|
4
|
+
exports.assertCommitPrefixHasCommits = assertCommitPrefixHasCommits;
|
|
5
|
+
const cli_args_1 = require("./agentic/cli-args");
|
|
6
|
+
const command_object_1 = require("./command-object");
|
|
7
|
+
const MULTI_MAJOR_MODE_ENV = 'NX_MULTI_MAJOR_MODE';
|
|
8
|
+
/**
|
|
9
|
+
* Overlays `nx.json` `migrate` defaults onto the raw `nx migrate` CLI args so a
|
|
10
|
+
* CLI flag always wins, then `nx.json`, then the built-in default. Returns a new
|
|
11
|
+
* args object; the input is not mutated.
|
|
12
|
+
*
|
|
13
|
+
* Phase-aware: generate-only options (`mode`, `multiMajorMode`) are applied only
|
|
14
|
+
* when not running migrations; run-only options (`createCommits`,
|
|
15
|
+
* `commitPrefix`, `agentic`, `validate`) only when running migrations. This
|
|
16
|
+
* mirrors where each option is consumed and avoids tripping the "cannot be
|
|
17
|
+
* combined with --run-migrations" guards in `parseMigrationsOptions`.
|
|
18
|
+
*
|
|
19
|
+
* `mode` is carried as `modeFromConfig` rather than `mode` so it is never
|
|
20
|
+
* mistaken for an explicit `--mode`: `resolveMode` applies it only when the
|
|
21
|
+
* target is Nx itself, leaving `nx migrate <non-nx-pkg>` unaffected.
|
|
22
|
+
*/
|
|
23
|
+
function applyNxJsonMigrateDefaults(args, migrateConfig, env = process.env) {
|
|
24
|
+
if (!migrateConfig) {
|
|
25
|
+
return args;
|
|
26
|
+
}
|
|
27
|
+
const merged = { ...args };
|
|
28
|
+
// `--run-migrations` with no value is normalized to '' by yargs, so a defined
|
|
29
|
+
// (even empty-string) value means we're in the run-migrations phase.
|
|
30
|
+
const isRunMigrations = merged.runMigrations !== undefined;
|
|
31
|
+
if (isRunMigrations) {
|
|
32
|
+
if (merged.createCommits === undefined &&
|
|
33
|
+
migrateConfig.createCommits !== undefined) {
|
|
34
|
+
assertType(migrateConfig.createCommits, 'boolean', 'createCommits');
|
|
35
|
+
merged.createCommits = migrateConfig.createCommits;
|
|
36
|
+
}
|
|
37
|
+
// `commitPrefix` carries a yargs default, so the default value is
|
|
38
|
+
// indistinguishable from "not provided" - treat it as not provided so
|
|
39
|
+
// nx.json can override it.
|
|
40
|
+
if ((merged.commitPrefix === undefined ||
|
|
41
|
+
merged.commitPrefix === command_object_1.DEFAULT_MIGRATION_COMMIT_PREFIX) &&
|
|
42
|
+
migrateConfig.commitPrefix !== undefined) {
|
|
43
|
+
assertType(migrateConfig.commitPrefix, 'string', 'commitPrefix');
|
|
44
|
+
merged.commitPrefix = migrateConfig.commitPrefix;
|
|
45
|
+
}
|
|
46
|
+
if (merged.agentic === undefined && migrateConfig.agentic !== undefined) {
|
|
47
|
+
assertValidAgentic(migrateConfig.agentic);
|
|
48
|
+
merged.agentic = (0, cli_args_1.coerceAgenticArg)(migrateConfig.agentic);
|
|
49
|
+
}
|
|
50
|
+
if (merged.validate === undefined && migrateConfig.validate !== undefined) {
|
|
51
|
+
assertType(migrateConfig.validate, 'boolean', 'validate');
|
|
52
|
+
merged.validate = migrateConfig.validate;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
if (merged.mode === undefined && migrateConfig.mode !== undefined) {
|
|
57
|
+
assertOneOf(migrateConfig.mode, command_object_1.MIGRATE_MODES, 'mode');
|
|
58
|
+
merged.modeFromConfig = migrateConfig.mode;
|
|
59
|
+
}
|
|
60
|
+
// The NX_MULTI_MAJOR_MODE env var is an established per-invocation override,
|
|
61
|
+
// so it takes precedence over nx.json (CLI flag > env > nx.json > default).
|
|
62
|
+
if (merged.multiMajorMode === undefined &&
|
|
63
|
+
!env[MULTI_MAJOR_MODE_ENV] &&
|
|
64
|
+
migrateConfig.multiMajorMode !== undefined) {
|
|
65
|
+
assertOneOf(migrateConfig.multiMajorMode, command_object_1.MULTI_MAJOR_MODES, 'multiMajorMode');
|
|
66
|
+
merged.multiMajorMode = migrateConfig.multiMajorMode;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return merged;
|
|
70
|
+
}
|
|
71
|
+
function assertOneOf(value, allowed, field) {
|
|
72
|
+
if (!allowed.includes(value)) {
|
|
73
|
+
throw new Error(`Error: Invalid nx.json migrate.${field} "${value}". Allowed: ${allowed.join(', ')}.`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
function assertType(value, type, field) {
|
|
77
|
+
if (typeof value !== type) {
|
|
78
|
+
throw new Error(`Error: Invalid nx.json migrate.${field} ${JSON.stringify(value)}. Expected a ${type}.`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function assertValidAgentic(agentic) {
|
|
82
|
+
if (typeof agentic === 'boolean') {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (typeof agentic !== 'string' ||
|
|
86
|
+
!cli_args_1.AGENT_IDS.includes(agentic)) {
|
|
87
|
+
throw new Error(`Error: Invalid nx.json migrate.agentic "${agentic}". Allowed: ${cli_args_1.AGENT_IDS.join(', ')}, true, false.`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* The single authority for the "a custom commit prefix needs commits enabled"
|
|
92
|
+
* invariant. Run it against the final merged args (after
|
|
93
|
+
* `applyNxJsonMigrateDefaults`): the yargs `.check()` only sees the CLI args,
|
|
94
|
+
* but nx.json may enable commits via `createCommits` or `agentic`. The CLI
|
|
95
|
+
* `.check()` only fast-fails the unrescuable explicit `--no-create-commits`
|
|
96
|
+
* case; everything else is decided here.
|
|
97
|
+
*/
|
|
98
|
+
function assertCommitPrefixHasCommits(merged) {
|
|
99
|
+
const { createCommits, commitPrefix, agentic } = merged;
|
|
100
|
+
if ((0, command_object_1.customCommitPrefixHasNoEffect)({ createCommits, commitPrefix, agentic })) {
|
|
101
|
+
throw new Error('Error: A custom migrate commit prefix requires commits to be enabled. Set `migrate.createCommits` to `true` in nx.json or pass `--create-commits`.');
|
|
102
|
+
}
|
|
103
|
+
}
|