nx 23.0.0-beta.20 → 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 +4 -11
- 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 +4 -11
- 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 +6 -8
- package/dist/src/command-line/migrate/agentic/prompts/shared-rendering.d.ts +10 -0
- package/dist/src/command-line/migrate/agentic/prompts/shared-rendering.js +58 -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/runner.js +3 -0
- 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-output.d.ts +70 -11
- package/dist/src/command-line/migrate/migrate-output.js +52 -35
- package/dist/src/command-line/migrate/migrate.d.ts +37 -2
- package/dist/src/command-line/migrate/migrate.js +180 -88
- package/dist/src/command-line/migrate/run-migration-process.js +9 -1
- 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/dist/src/utils/git-utils.d.ts +1 -0
- package/dist/src/utils/git-utils.js +65 -0
- package/migrations.json +18 -9
- package/package.json +12 -12
- 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). */
|
|
@@ -27,16 +27,9 @@ exports.GENERIC_VALIDATION_FILE_LIST_CAP = 50;
|
|
|
27
27
|
function buildGenericValidationUserPrompt(ctx) {
|
|
28
28
|
const lines = [
|
|
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
|
-
|
|
31
|
-
`<migration>`,
|
|
32
|
-
`package: ${(0, shared_rendering_1.escapeXmlBody)(ctx.package)}`,
|
|
33
|
-
`version: ${(0, shared_rendering_1.escapeXmlBody)(ctx.version)}`,
|
|
34
|
-
`name: ${(0, shared_rendering_1.escapeXmlBody)(ctx.name)}`,
|
|
30
|
+
...(0, shared_rendering_1.renderMigrationBlock)(ctx),
|
|
35
31
|
];
|
|
36
|
-
|
|
37
|
-
lines.push(...(0, shared_rendering_1.renderKeyMultilineValue)('description', (0, shared_rendering_1.escapeXmlBody)(ctx.description)));
|
|
38
|
-
}
|
|
39
|
-
lines.push(`</migration>`);
|
|
32
|
+
lines.push(...(0, shared_rendering_1.renderMigrationDocumentationBlock)(ctx.documentationPath));
|
|
40
33
|
const logs = (0, shared_rendering_1.escapeXmlBody)((0, shared_rendering_1.stripAnsi)(ctx.impl.logs ?? '').trim());
|
|
41
34
|
lines.push(...(0, shared_rendering_1.renderGeneratorOutputBlock)(logs));
|
|
42
35
|
if (!ctx.impl.hasDiffContext && ctx.impl.changes.length > 0) {
|
|
@@ -44,12 +37,12 @@ function buildGenericValidationUserPrompt(ctx) {
|
|
|
44
37
|
}
|
|
45
38
|
const agentContext = (0, shared_rendering_1.filterNonEmptyStrings)(ctx.impl.agentContext ?? []);
|
|
46
39
|
if (agentContext.length > 0) {
|
|
47
|
-
lines.push(
|
|
40
|
+
lines.push(...(0, shared_rendering_1.renderAdvisoryContext)('hints emitted by the generator; treat as supplementary context, not separate tasks', agentContext));
|
|
48
41
|
}
|
|
49
42
|
const firstStep = ctx.impl.hasDiffContext
|
|
50
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.`
|
|
51
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.`;
|
|
52
|
-
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. On \`status: "success"\`, summarize what you verified and any fixes you applied. On \`status: "failed"\`, enumerate the unresolved 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>`, ``, `Once you finish, write your handoff JSON to:`,
|
|
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. On \`status: "success"\`, summarize what you verified and any fixes you applied. On \`status: "failed"\`, enumerate the unresolved 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>`, ``, `Once you finish, write your handoff JSON to:`, ...(0, shared_rendering_1.renderHandoffPathFooter)(ctx.handoffFileAbsolutePath));
|
|
53
46
|
return lines.join('\n');
|
|
54
47
|
}
|
|
55
48
|
function renderFileListBody(changes) {
|
|
@@ -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). */
|
|
@@ -18,16 +18,9 @@ const shared_rendering_1 = require("./shared-rendering");
|
|
|
18
18
|
function buildHybridPromptUserPrompt(ctx) {
|
|
19
19
|
const lines = [
|
|
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
|
-
|
|
22
|
-
`<migration>`,
|
|
23
|
-
`package: ${(0, shared_rendering_1.escapeXmlBody)(ctx.package)}`,
|
|
24
|
-
`version: ${(0, shared_rendering_1.escapeXmlBody)(ctx.version)}`,
|
|
25
|
-
`name: ${(0, shared_rendering_1.escapeXmlBody)(ctx.name)}`,
|
|
21
|
+
...(0, shared_rendering_1.renderMigrationBlock)(ctx),
|
|
26
22
|
];
|
|
27
|
-
|
|
28
|
-
lines.push(...(0, shared_rendering_1.renderKeyMultilineValue)('description', (0, shared_rendering_1.escapeXmlBody)(ctx.description)));
|
|
29
|
-
}
|
|
30
|
-
lines.push(`</migration>`);
|
|
23
|
+
lines.push(...(0, shared_rendering_1.renderMigrationDocumentationBlock)(ctx.documentationPath));
|
|
31
24
|
const logs = (0, shared_rendering_1.escapeXmlBody)((0, shared_rendering_1.stripAnsi)(ctx.impl?.logs ?? '').trim());
|
|
32
25
|
const agentContext = (0, shared_rendering_1.filterNonEmptyStrings)(ctx.impl?.agentContext ?? []);
|
|
33
26
|
const hasDiffContext = !!ctx.impl?.hasDiffContext;
|
|
@@ -45,9 +38,9 @@ function buildHybridPromptUserPrompt(ctx) {
|
|
|
45
38
|
}
|
|
46
39
|
}
|
|
47
40
|
if (agentContext.length > 0) {
|
|
48
|
-
lines.push(
|
|
41
|
+
lines.push(...(0, shared_rendering_1.renderAdvisoryContext)('hints from the generator phase; consult while following the instructions, not as separate tasks', agentContext));
|
|
49
42
|
}
|
|
50
|
-
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 write your handoff JSON to:`,
|
|
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 write your handoff JSON to:`, ...(0, shared_rendering_1.renderHandoffPathFooter)(ctx.handoffFileAbsolutePath));
|
|
51
44
|
return lines.join('\n');
|
|
52
45
|
}
|
|
53
46
|
function renderFileList(changes) {
|
|
@@ -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.
|
|
@@ -15,15 +15,13 @@ const shared_rendering_1 = require("./shared-rendering");
|
|
|
15
15
|
function buildPromptMigrationUserPrompt(ctx) {
|
|
16
16
|
const lines = [
|
|
17
17
|
`Apply one prompt-based migration to this Nx workspace.`,
|
|
18
|
+
...(0, shared_rendering_1.renderMigrationBlock)(ctx),
|
|
19
|
+
...(0, shared_rendering_1.renderMigrationDocumentationBlock)(ctx.documentationPath),
|
|
18
20
|
``,
|
|
19
|
-
`<
|
|
20
|
-
|
|
21
|
-
`
|
|
22
|
-
|
|
21
|
+
`<instructions_file>${(0, shared_rendering_1.escapeXmlBody)(ctx.promptPath)}</instructions_file>`,
|
|
22
|
+
``,
|
|
23
|
+
`Open the instructions file (path is workspace-relative), follow its instructions step by step, then write your handoff JSON to:`,
|
|
24
|
+
...(0, shared_rendering_1.renderHandoffPathFooter)(ctx.handoffFileAbsolutePath),
|
|
23
25
|
];
|
|
24
|
-
if (ctx.description) {
|
|
25
|
-
lines.push(...(0, shared_rendering_1.renderKeyMultilineValue)('description', (0, shared_rendering_1.escapeXmlBody)(ctx.description)));
|
|
26
|
-
}
|
|
27
|
-
lines.push(`</migration>`, ``, `<instructions_file>${(0, shared_rendering_1.escapeXmlBody)(ctx.promptPath)}</instructions_file>`, ``, `Open the instructions file (path is workspace-relative), follow its instructions step by step, then write your handoff JSON to:`, `<handoff_path>`, (0, shared_rendering_1.escapeXmlBody)(ctx.handoffFileAbsolutePath), `</handoff_path>`);
|
|
28
26
|
return lines.join('\n');
|
|
29
27
|
}
|
|
@@ -7,3 +7,13 @@ export declare function filterNonEmptyStrings(entries: unknown[]): string[];
|
|
|
7
7
|
export declare function escapeXmlBody(value: string): string;
|
|
8
8
|
export declare function renderGitInspectInstruction(): string;
|
|
9
9
|
export declare function renderGeneratorOutputBlock(logs: string): string[];
|
|
10
|
+
export interface MigrationBlockContext {
|
|
11
|
+
package: string;
|
|
12
|
+
name: string;
|
|
13
|
+
version: string;
|
|
14
|
+
description?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function renderMigrationBlock(ctx: MigrationBlockContext): string[];
|
|
17
|
+
export declare function renderHandoffPathFooter(handoffFileAbsolutePath: string): string[];
|
|
18
|
+
export declare function renderAdvisoryContext(note: string, entries: string[]): string[];
|
|
19
|
+
export declare function renderMigrationDocumentationBlock(documentationPath: string | undefined): string[];
|
|
@@ -8,6 +8,10 @@ exports.filterNonEmptyStrings = filterNonEmptyStrings;
|
|
|
8
8
|
exports.escapeXmlBody = escapeXmlBody;
|
|
9
9
|
exports.renderGitInspectInstruction = renderGitInspectInstruction;
|
|
10
10
|
exports.renderGeneratorOutputBlock = renderGeneratorOutputBlock;
|
|
11
|
+
exports.renderMigrationBlock = renderMigrationBlock;
|
|
12
|
+
exports.renderHandoffPathFooter = renderHandoffPathFooter;
|
|
13
|
+
exports.renderAdvisoryContext = renderAdvisoryContext;
|
|
14
|
+
exports.renderMigrationDocumentationBlock = renderMigrationDocumentationBlock;
|
|
11
15
|
function renderFileEntry(change) {
|
|
12
16
|
return `[${change.type}] ${change.path}`;
|
|
13
17
|
}
|
|
@@ -85,3 +89,57 @@ function renderGeneratorOutputBlock(logs) {
|
|
|
85
89
|
`</generator_output>`,
|
|
86
90
|
];
|
|
87
91
|
}
|
|
92
|
+
// Identical `<migration>` block used by prompt-migration, hybrid, and
|
|
93
|
+
// generic-validation builders. Leading blank included so callers can spread
|
|
94
|
+
// directly after a lead sentence. Centralized so the schema and the
|
|
95
|
+
// `escapeXmlBody` contract on values stay in lock-step.
|
|
96
|
+
function renderMigrationBlock(ctx) {
|
|
97
|
+
const lines = [
|
|
98
|
+
``,
|
|
99
|
+
`<migration>`,
|
|
100
|
+
`package: ${escapeXmlBody(ctx.package)}`,
|
|
101
|
+
`version: ${escapeXmlBody(ctx.version)}`,
|
|
102
|
+
`name: ${escapeXmlBody(ctx.name)}`,
|
|
103
|
+
];
|
|
104
|
+
if (ctx.description) {
|
|
105
|
+
lines.push(...renderKeyMultilineValue('description', escapeXmlBody(ctx.description)));
|
|
106
|
+
}
|
|
107
|
+
lines.push(`</migration>`);
|
|
108
|
+
return lines;
|
|
109
|
+
}
|
|
110
|
+
// `<handoff_path>` footer that follows the "write your handoff JSON to:"
|
|
111
|
+
// sentence. No leading blank — the block is part of that sentence's structure,
|
|
112
|
+
// not a separate section.
|
|
113
|
+
function renderHandoffPathFooter(handoffFileAbsolutePath) {
|
|
114
|
+
return [
|
|
115
|
+
`<handoff_path>`,
|
|
116
|
+
escapeXmlBody(handoffFileAbsolutePath),
|
|
117
|
+
`</handoff_path>`,
|
|
118
|
+
];
|
|
119
|
+
}
|
|
120
|
+
// Advisory hints from the generator phase. The `note` attribute varies between
|
|
121
|
+
// callers (hybrid vs generic-validation lead-in differs), so it's parameterized
|
|
122
|
+
// rather than hardcoded. Entries are escaped here so callers can pass raw
|
|
123
|
+
// strings.
|
|
124
|
+
function renderAdvisoryContext(note, entries) {
|
|
125
|
+
return [
|
|
126
|
+
``,
|
|
127
|
+
`<advisory_context note="${note}">`,
|
|
128
|
+
...entries.map((entry) => renderListItem(escapeXmlBody(entry))),
|
|
129
|
+
`</advisory_context>`,
|
|
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 })
|
|
@@ -27,6 +27,9 @@ async function runAgentic(args) {
|
|
|
27
27
|
windowsHide: true,
|
|
28
28
|
});
|
|
29
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.
|
|
30
33
|
const spawnOptions = adapted.options;
|
|
31
34
|
try {
|
|
32
35
|
child = (0, child_process_1.spawn)(adapted.binary, adapted.args, spawnOptions);
|
|
@@ -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;
|