@angular/cli 20.2.0-rc.1 → 20.2.0
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/package.json +14 -14
- package/src/command-builder/command-module.js +8 -2
- package/src/command-builder/schematics-command-module.js +2 -0
- package/src/command-builder/utilities/json-schema.d.ts +2 -1
- package/src/command-builder/utilities/json-schema.js +41 -20
- package/src/commands/mcp/cli.js +4 -0
- package/src/commands/mcp/mcp-server.d.ts +11 -0
- package/src/commands/mcp/mcp-server.js +3 -2
- package/src/commands/mcp/tools/doc-search.js +7 -8
- package/src/utilities/package-manager.js +1 -1
- package/src/utilities/version.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@angular/cli",
|
|
3
|
-
"version": "20.2.0
|
|
3
|
+
"version": "20.2.0",
|
|
4
4
|
"description": "CLI tool for Angular",
|
|
5
5
|
"main": "lib/cli/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -25,13 +25,13 @@
|
|
|
25
25
|
},
|
|
26
26
|
"homepage": "https://github.com/angular/angular-cli",
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@angular-devkit/architect": "0.2002.0
|
|
29
|
-
"@angular-devkit/core": "20.2.0
|
|
30
|
-
"@angular-devkit/schematics": "20.2.0
|
|
28
|
+
"@angular-devkit/architect": "0.2002.0",
|
|
29
|
+
"@angular-devkit/core": "20.2.0",
|
|
30
|
+
"@angular-devkit/schematics": "20.2.0",
|
|
31
31
|
"@inquirer/prompts": "7.8.2",
|
|
32
32
|
"@listr2/prompt-adapter-inquirer": "3.0.1",
|
|
33
|
-
"@modelcontextprotocol/sdk": "1.17.
|
|
34
|
-
"@schematics/angular": "20.2.0
|
|
33
|
+
"@modelcontextprotocol/sdk": "1.17.3",
|
|
34
|
+
"@schematics/angular": "20.2.0",
|
|
35
35
|
"@yarnpkg/lockfile": "1.1.0",
|
|
36
36
|
"algoliasearch": "5.35.0",
|
|
37
37
|
"ini": "5.0.0",
|
|
@@ -47,14 +47,14 @@
|
|
|
47
47
|
"ng-update": {
|
|
48
48
|
"migrations": "@schematics/angular/migrations/migration-collection.json",
|
|
49
49
|
"packageGroup": {
|
|
50
|
-
"@angular/cli": "20.2.0
|
|
51
|
-
"@angular/build": "20.2.0
|
|
52
|
-
"@angular/ssr": "20.2.0
|
|
53
|
-
"@angular-devkit/architect": "0.2002.0
|
|
54
|
-
"@angular-devkit/build-angular": "20.2.0
|
|
55
|
-
"@angular-devkit/build-webpack": "0.2002.0
|
|
56
|
-
"@angular-devkit/core": "20.2.0
|
|
57
|
-
"@angular-devkit/schematics": "20.2.0
|
|
50
|
+
"@angular/cli": "20.2.0",
|
|
51
|
+
"@angular/build": "20.2.0",
|
|
52
|
+
"@angular/ssr": "20.2.0",
|
|
53
|
+
"@angular-devkit/architect": "0.2002.0",
|
|
54
|
+
"@angular-devkit/build-angular": "20.2.0",
|
|
55
|
+
"@angular-devkit/build-webpack": "0.2002.0",
|
|
56
|
+
"@angular-devkit/core": "20.2.0",
|
|
57
|
+
"@angular-devkit/schematics": "20.2.0"
|
|
58
58
|
}
|
|
59
59
|
},
|
|
60
60
|
"packageManager": "pnpm@10.14.0",
|
|
@@ -218,11 +218,17 @@ let CommandModule = (() => {
|
|
|
218
218
|
...Object.values(analytics_parameters_1.EventCustomMetric),
|
|
219
219
|
]);
|
|
220
220
|
for (const [name, ua] of this.optionsWithAnalytics) {
|
|
221
|
+
if (!validEventCustomDimensionAndMetrics.has(ua)) {
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
221
224
|
const value = options[name];
|
|
222
|
-
if (
|
|
223
|
-
validEventCustomDimensionAndMetrics.has(ua)) {
|
|
225
|
+
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
224
226
|
parameters[ua] = value;
|
|
225
227
|
}
|
|
228
|
+
else if (Array.isArray(value)) {
|
|
229
|
+
// GA doesn't allow array as values.
|
|
230
|
+
parameters[ua] = value.sort().join(', ');
|
|
231
|
+
}
|
|
226
232
|
}
|
|
227
233
|
return parameters;
|
|
228
234
|
}
|
|
@@ -224,11 +224,13 @@ let SchematicsCommandModule = (() => {
|
|
|
224
224
|
? {
|
|
225
225
|
name: item,
|
|
226
226
|
value: item,
|
|
227
|
+
checked: item === definition.default,
|
|
227
228
|
}
|
|
228
229
|
: {
|
|
229
230
|
...item,
|
|
230
231
|
name: item.label,
|
|
231
232
|
value: item.value,
|
|
233
|
+
checked: item.value === definition.default,
|
|
232
234
|
}),
|
|
233
235
|
});
|
|
234
236
|
break;
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { json } from '@angular-devkit/core';
|
|
9
9
|
import type { Argv, Options as YargsOptions } from 'yargs';
|
|
10
|
+
import { EventCustomDimension } from '../../analytics/analytics-parameters';
|
|
10
11
|
/**
|
|
11
12
|
* An option description.
|
|
12
13
|
*/
|
|
@@ -48,4 +49,4 @@ export declare function parseJsonSchemaToOptions(registry: json.schema.SchemaReg
|
|
|
48
49
|
*
|
|
49
50
|
* @returns A map from option name to analytics configuration.
|
|
50
51
|
*/
|
|
51
|
-
export declare function addSchemaOptionsToCommand<T>(localYargs: Argv<T>, options: Option[], includeDefaultValues: boolean): Map<string,
|
|
52
|
+
export declare function addSchemaOptionsToCommand<T>(localYargs: Argv<T>, options: Option[], includeDefaultValues: boolean): Map<string, EventCustomDimension>;
|
|
@@ -10,7 +10,25 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
10
10
|
exports.parseJsonSchemaToOptions = parseJsonSchemaToOptions;
|
|
11
11
|
exports.addSchemaOptionsToCommand = addSchemaOptionsToCommand;
|
|
12
12
|
const core_1 = require("@angular-devkit/core");
|
|
13
|
-
function
|
|
13
|
+
function checkStringMap(keyValuePairOptions, args) {
|
|
14
|
+
for (const key of keyValuePairOptions) {
|
|
15
|
+
const value = args[key];
|
|
16
|
+
if (!Array.isArray(value)) {
|
|
17
|
+
// Value has been parsed.
|
|
18
|
+
continue;
|
|
19
|
+
}
|
|
20
|
+
for (const pair of value) {
|
|
21
|
+
if (pair === undefined) {
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if (!pair.includes('=')) {
|
|
25
|
+
throw new Error(`Invalid value for argument: ${key}, Given: '${pair}', Expected key=value pair`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
function coerceToStringMap(value) {
|
|
14
32
|
const stringMap = {};
|
|
15
33
|
for (const pair of value) {
|
|
16
34
|
// This happens when the flag isn't passed at all.
|
|
@@ -19,14 +37,11 @@ function coerceToStringMap(dashedName, value) {
|
|
|
19
37
|
}
|
|
20
38
|
const eqIdx = pair.indexOf('=');
|
|
21
39
|
if (eqIdx === -1) {
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
// errors with usage output.
|
|
25
|
-
return Promise.reject(new Error(`Invalid value for argument: ${dashedName}, Given: '${pair}', Expected key=value pair`));
|
|
40
|
+
// In the case it is not valid skip processing this option and handle the error in `checkStringMap`
|
|
41
|
+
return value;
|
|
26
42
|
}
|
|
27
43
|
const key = pair.slice(0, eqIdx);
|
|
28
|
-
|
|
29
|
-
stringMap[key] = value;
|
|
44
|
+
stringMap[key] = pair.slice(eqIdx + 1);
|
|
30
45
|
}
|
|
31
46
|
return stringMap;
|
|
32
47
|
}
|
|
@@ -79,7 +94,7 @@ async function parseJsonSchemaToOptions(registry, schema, interactive = true) {
|
|
|
79
94
|
// Only include arrays if they're boolean, string or number.
|
|
80
95
|
if (core_1.json.isJsonObject(current.items) &&
|
|
81
96
|
typeof current.items.type == 'string' &&
|
|
82
|
-
|
|
97
|
+
isValidTypeForEnum(current.items.type)) {
|
|
83
98
|
return true;
|
|
84
99
|
}
|
|
85
100
|
return false;
|
|
@@ -94,20 +109,18 @@ async function parseJsonSchemaToOptions(registry, schema, interactive = true) {
|
|
|
94
109
|
return;
|
|
95
110
|
}
|
|
96
111
|
// Only keep enum values we support (booleans, numbers and strings).
|
|
97
|
-
const enumValues = ((core_1.json.isJsonArray(current.enum) && current.enum) ||
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
return false;
|
|
105
|
-
}
|
|
106
|
-
});
|
|
112
|
+
const enumValues = ((core_1.json.isJsonArray(current.enum) && current.enum) ||
|
|
113
|
+
(core_1.json.isJsonObject(current.items) &&
|
|
114
|
+
core_1.json.isJsonArray(current.items.enum) &&
|
|
115
|
+
current.items.enum) ||
|
|
116
|
+
[])
|
|
117
|
+
.filter((value) => isValidTypeForEnum(typeof value))
|
|
118
|
+
.sort();
|
|
107
119
|
let defaultValue = undefined;
|
|
108
120
|
if (current.default !== undefined) {
|
|
109
121
|
switch (types[0]) {
|
|
110
122
|
case 'string':
|
|
123
|
+
case 'array':
|
|
111
124
|
if (typeof current.default == 'string') {
|
|
112
125
|
defaultValue = current.default;
|
|
113
126
|
}
|
|
@@ -199,7 +212,7 @@ function addSchemaOptionsToCommand(localYargs, options, includeDefaultValues) {
|
|
|
199
212
|
booleanOptionsWithNoPrefix.add(dashedName);
|
|
200
213
|
}
|
|
201
214
|
if (itemValueType) {
|
|
202
|
-
keyValuePairOptions.add(
|
|
215
|
+
keyValuePairOptions.add(dashedName);
|
|
203
216
|
}
|
|
204
217
|
const sharedOptions = {
|
|
205
218
|
alias,
|
|
@@ -207,7 +220,7 @@ function addSchemaOptionsToCommand(localYargs, options, includeDefaultValues) {
|
|
|
207
220
|
description,
|
|
208
221
|
deprecated,
|
|
209
222
|
choices,
|
|
210
|
-
coerce: itemValueType ? coerceToStringMap
|
|
223
|
+
coerce: itemValueType ? coerceToStringMap : undefined,
|
|
211
224
|
// This should only be done when `--help` is used otherwise default will override options set in angular.json.
|
|
212
225
|
...(includeDefaultValues ? { default: defaultVal } : {}),
|
|
213
226
|
};
|
|
@@ -229,6 +242,10 @@ function addSchemaOptionsToCommand(localYargs, options, includeDefaultValues) {
|
|
|
229
242
|
optionsWithAnalytics.set(name, userAnalytics);
|
|
230
243
|
}
|
|
231
244
|
}
|
|
245
|
+
// Valid key/value options
|
|
246
|
+
if (keyValuePairOptions.size) {
|
|
247
|
+
localYargs.check(checkStringMap.bind(null, keyValuePairOptions), false);
|
|
248
|
+
}
|
|
232
249
|
// Handle options which have been defined in the schema with `no` prefix.
|
|
233
250
|
if (booleanOptionsWithNoPrefix.size) {
|
|
234
251
|
localYargs.middleware((options) => {
|
|
@@ -242,3 +259,7 @@ function addSchemaOptionsToCommand(localYargs, options, includeDefaultValues) {
|
|
|
242
259
|
}
|
|
243
260
|
return optionsWithAnalytics;
|
|
244
261
|
}
|
|
262
|
+
const VALID_ENUM_TYPES = new Set(['boolean', 'number', 'string']);
|
|
263
|
+
function isValidTypeForEnum(value) {
|
|
264
|
+
return VALID_ENUM_TYPES.has(value);
|
|
265
|
+
}
|
package/src/commands/mcp/cli.js
CHANGED
|
@@ -24,6 +24,8 @@ To start using the Angular CLI MCP Server, add this configuration to your host:
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
Exact configuration may differ depending on the host.
|
|
27
|
+
|
|
28
|
+
For more information and documentation, visit: https://angular.dev/ai/mcp
|
|
27
29
|
`;
|
|
28
30
|
class McpCommandModule extends command_module_1.CommandModule {
|
|
29
31
|
command = 'mcp';
|
|
@@ -46,6 +48,8 @@ class McpCommandModule extends command_module_1.CommandModule {
|
|
|
46
48
|
alias: 'E',
|
|
47
49
|
array: true,
|
|
48
50
|
describe: 'Enable an experimental tool.',
|
|
51
|
+
choices: mcp_server_1.EXPERIMENTAL_TOOLS.map(({ name }) => name),
|
|
52
|
+
hidden: true,
|
|
49
53
|
});
|
|
50
54
|
}
|
|
51
55
|
async run(options) {
|
|
@@ -8,6 +8,17 @@
|
|
|
8
8
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
9
9
|
import type { AngularWorkspace } from '../../utilities/config';
|
|
10
10
|
import { AnyMcpToolDeclaration } from './tools/tool-registry';
|
|
11
|
+
/**
|
|
12
|
+
* The set of tools that are available but not enabled by default.
|
|
13
|
+
* These tools are considered experimental and may have limitations.
|
|
14
|
+
*/
|
|
15
|
+
export declare const EXPERIMENTAL_TOOLS: readonly [import("./tools/tool-registry").McpToolDeclaration<{
|
|
16
|
+
query: import("zod").ZodString;
|
|
17
|
+
}, import("zod").ZodRawShape>, import("./tools/tool-registry").McpToolDeclaration<{
|
|
18
|
+
transformations: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodEnum<[string, ...string[]]>, "many">>;
|
|
19
|
+
}, {
|
|
20
|
+
instructions: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString, "many">>;
|
|
21
|
+
}>];
|
|
11
22
|
export declare function createMcpServer(options: {
|
|
12
23
|
workspace?: AngularWorkspace;
|
|
13
24
|
readOnly?: boolean;
|
|
@@ -10,6 +10,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
10
10
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
11
11
|
};
|
|
12
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.EXPERIMENTAL_TOOLS = void 0;
|
|
13
14
|
exports.createMcpServer = createMcpServer;
|
|
14
15
|
exports.assembleToolDeclarations = assembleToolDeclarations;
|
|
15
16
|
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
@@ -31,7 +32,7 @@ const STABLE_TOOLS = [best_practices_1.BEST_PRACTICES_TOOL, doc_search_1.DOC_SEA
|
|
|
31
32
|
* The set of tools that are available but not enabled by default.
|
|
32
33
|
* These tools are considered experimental and may have limitations.
|
|
33
34
|
*/
|
|
34
|
-
|
|
35
|
+
exports.EXPERIMENTAL_TOOLS = [examples_1.FIND_EXAMPLE_TOOL, modernize_1.MODERNIZE_TOOL];
|
|
35
36
|
async function createMcpServer(options, logger) {
|
|
36
37
|
const server = new mcp_js_1.McpServer({
|
|
37
38
|
name: 'angular-cli-server',
|
|
@@ -46,7 +47,7 @@ async function createMcpServer(options, logger) {
|
|
|
46
47
|
'When writing or modifying Angular code, use the MCP server and its tools instead of direct shell commands where possible.',
|
|
47
48
|
});
|
|
48
49
|
(0, instructions_1.registerInstructionsResource)(server);
|
|
49
|
-
const toolDeclarations = assembleToolDeclarations(STABLE_TOOLS, EXPERIMENTAL_TOOLS, {
|
|
50
|
+
const toolDeclarations = assembleToolDeclarations(STABLE_TOOLS, exports.EXPERIMENTAL_TOOLS, {
|
|
50
51
|
...options,
|
|
51
52
|
logger,
|
|
52
53
|
});
|
|
@@ -112,7 +112,7 @@ function createDocSearchHandler() {
|
|
|
112
112
|
const response = await fetch(url);
|
|
113
113
|
if (response.ok) {
|
|
114
114
|
const html = await response.text();
|
|
115
|
-
const mainContent =
|
|
115
|
+
const mainContent = extractMainContent(html);
|
|
116
116
|
if (mainContent) {
|
|
117
117
|
topText += `\n\n--- DOCUMENTATION CONTENT ---\n${mainContent}`;
|
|
118
118
|
}
|
|
@@ -138,22 +138,21 @@ function createDocSearchHandler() {
|
|
|
138
138
|
};
|
|
139
139
|
}
|
|
140
140
|
/**
|
|
141
|
-
* Extracts the content of the `<
|
|
141
|
+
* Extracts the content of the `<main>` element from an HTML string.
|
|
142
142
|
*
|
|
143
143
|
* @param html The HTML content of a page.
|
|
144
|
-
* @returns The content of the `<
|
|
144
|
+
* @returns The content of the `<main>` element, or `undefined` if not found.
|
|
145
145
|
*/
|
|
146
|
-
function
|
|
147
|
-
|
|
148
|
-
const mainTagStart = html.indexOf('<body');
|
|
146
|
+
function extractMainContent(html) {
|
|
147
|
+
const mainTagStart = html.indexOf('<main');
|
|
149
148
|
if (mainTagStart === -1) {
|
|
150
149
|
return undefined;
|
|
151
150
|
}
|
|
152
|
-
const mainTagEnd = html.lastIndexOf('</
|
|
151
|
+
const mainTagEnd = html.lastIndexOf('</main>');
|
|
153
152
|
if (mainTagEnd <= mainTagStart) {
|
|
154
153
|
return undefined;
|
|
155
154
|
}
|
|
156
|
-
// Add 7 to include '</
|
|
155
|
+
// Add 7 to include '</main>'
|
|
157
156
|
return html.substring(mainTagStart, mainTagEnd + 7);
|
|
158
157
|
}
|
|
159
158
|
/**
|
|
@@ -171,7 +171,7 @@ let PackageManagerUtils = (() => {
|
|
|
171
171
|
const { cwd = process.cwd(), silent = false } = options;
|
|
172
172
|
return new Promise((resolve) => {
|
|
173
173
|
const bufferedOutput = [];
|
|
174
|
-
const childProcess = (0, node_child_process_1.spawn)(this.name
|
|
174
|
+
const childProcess = (0, node_child_process_1.spawn)(`${this.name} ${args.join(' ')}`, {
|
|
175
175
|
// Always pipe stderr to allow for failures to be reported
|
|
176
176
|
stdio: silent ? ['ignore', 'ignore', 'pipe'] : 'pipe',
|
|
177
177
|
shell: true,
|
package/src/utilities/version.js
CHANGED