@angular/cli 20.1.0-rc.0 → 20.1.1
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/lib/config/schema.json +10 -2
- package/package.json +16 -14
- package/src/commands/mcp/constants.d.ts +10 -0
- package/src/commands/mcp/constants.js +15 -0
- package/src/commands/mcp/mcp-server.js +57 -10
- package/src/commands/mcp/tools/doc-search.d.ts +16 -0
- package/src/commands/mcp/tools/doc-search.js +130 -0
- package/src/utilities/version.js +1 -1
package/lib/config/schema.json
CHANGED
|
@@ -4299,7 +4299,7 @@
|
|
|
4299
4299
|
},
|
|
4300
4300
|
"watch": {
|
|
4301
4301
|
"type": "boolean",
|
|
4302
|
-
"description": "
|
|
4302
|
+
"description": "Re-run tests when source files change. Defaults to `true` in TTY environments and `false` otherwise."
|
|
4303
4303
|
},
|
|
4304
4304
|
"debug": {
|
|
4305
4305
|
"type": "boolean",
|
|
@@ -4354,6 +4354,13 @@
|
|
|
4354
4354
|
"type": "string",
|
|
4355
4355
|
"description": "TypeScript file that exports an array of Angular providers to use during test execution. The array must be a default export.",
|
|
4356
4356
|
"minLength": 1
|
|
4357
|
+
},
|
|
4358
|
+
"setupFiles": {
|
|
4359
|
+
"type": "array",
|
|
4360
|
+
"items": {
|
|
4361
|
+
"type": "string"
|
|
4362
|
+
},
|
|
4363
|
+
"description": "A list of global setup and configuration files that are included before the test files. The application's polyfills are always included before these files. The Angular Testbed is also initialized prior to the execution of these files."
|
|
4357
4364
|
}
|
|
4358
4365
|
},
|
|
4359
4366
|
"additionalProperties": false,
|
|
@@ -4616,7 +4623,8 @@
|
|
|
4616
4623
|
},
|
|
4617
4624
|
"watch": {
|
|
4618
4625
|
"type": "boolean",
|
|
4619
|
-
"description": "
|
|
4626
|
+
"description": "Re-run tests when source files change.",
|
|
4627
|
+
"default": true
|
|
4620
4628
|
},
|
|
4621
4629
|
"poll": {
|
|
4622
4630
|
"type": "number",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@angular/cli",
|
|
3
|
-
"version": "20.1.
|
|
3
|
+
"version": "20.1.1",
|
|
4
4
|
"description": "CLI tool for Angular",
|
|
5
5
|
"main": "lib/cli/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -25,14 +25,15 @@
|
|
|
25
25
|
},
|
|
26
26
|
"homepage": "https://github.com/angular/angular-cli",
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@angular-devkit/architect": "0.2001.
|
|
29
|
-
"@angular-devkit/core": "20.1.
|
|
30
|
-
"@angular-devkit/schematics": "20.1.
|
|
28
|
+
"@angular-devkit/architect": "0.2001.1",
|
|
29
|
+
"@angular-devkit/core": "20.1.1",
|
|
30
|
+
"@angular-devkit/schematics": "20.1.1",
|
|
31
31
|
"@inquirer/prompts": "7.6.0",
|
|
32
32
|
"@listr2/prompt-adapter-inquirer": "2.0.22",
|
|
33
33
|
"@modelcontextprotocol/sdk": "1.13.3",
|
|
34
|
-
"@schematics/angular": "20.1.
|
|
34
|
+
"@schematics/angular": "20.1.1",
|
|
35
35
|
"@yarnpkg/lockfile": "1.1.0",
|
|
36
|
+
"algoliasearch": "5.32.0",
|
|
36
37
|
"ini": "5.0.0",
|
|
37
38
|
"jsonc-parser": "3.3.1",
|
|
38
39
|
"listr2": "8.3.3",
|
|
@@ -41,19 +42,20 @@
|
|
|
41
42
|
"pacote": "21.0.0",
|
|
42
43
|
"resolve": "1.22.10",
|
|
43
44
|
"semver": "7.7.2",
|
|
44
|
-
"yargs": "18.0.0"
|
|
45
|
+
"yargs": "18.0.0",
|
|
46
|
+
"zod": "3.25.75"
|
|
45
47
|
},
|
|
46
48
|
"ng-update": {
|
|
47
49
|
"migrations": "@schematics/angular/migrations/migration-collection.json",
|
|
48
50
|
"packageGroup": {
|
|
49
|
-
"@angular/cli": "20.1.
|
|
50
|
-
"@angular/build": "20.1.
|
|
51
|
-
"@angular/ssr": "20.1.
|
|
52
|
-
"@angular-devkit/architect": "0.2001.
|
|
53
|
-
"@angular-devkit/build-angular": "20.1.
|
|
54
|
-
"@angular-devkit/build-webpack": "0.2001.
|
|
55
|
-
"@angular-devkit/core": "20.1.
|
|
56
|
-
"@angular-devkit/schematics": "20.1.
|
|
51
|
+
"@angular/cli": "20.1.1",
|
|
52
|
+
"@angular/build": "20.1.1",
|
|
53
|
+
"@angular/ssr": "20.1.1",
|
|
54
|
+
"@angular-devkit/architect": "0.2001.1",
|
|
55
|
+
"@angular-devkit/build-angular": "20.1.1",
|
|
56
|
+
"@angular-devkit/build-webpack": "0.2001.1",
|
|
57
|
+
"@angular-devkit/core": "20.1.1",
|
|
58
|
+
"@angular-devkit/schematics": "20.1.1"
|
|
57
59
|
}
|
|
58
60
|
},
|
|
59
61
|
"packageManager": "pnpm@9.15.9",
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright Google LLC All Rights Reserved.
|
|
4
|
+
*
|
|
5
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
6
|
+
* found in the LICENSE file at https://angular.dev/license
|
|
7
|
+
*/
|
|
8
|
+
export declare const k1 = "@angular/cli";
|
|
9
|
+
export declare const at = "QBHBbOdEO4CmBOC2d7jNmg==";
|
|
10
|
+
export declare const iv: Buffer<ArrayBuffer>;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright Google LLC All Rights Reserved.
|
|
5
|
+
*
|
|
6
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
7
|
+
* found in the LICENSE file at https://angular.dev/license
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.iv = exports.at = exports.k1 = void 0;
|
|
11
|
+
exports.k1 = '@angular/cli';
|
|
12
|
+
exports.at = 'QBHBbOdEO4CmBOC2d7jNmg==';
|
|
13
|
+
exports.iv = Buffer.from([
|
|
14
|
+
0x97, 0xf4, 0x62, 0x95, 0x3e, 0x12, 0x76, 0x84, 0x8a, 0x09, 0x4a, 0xc9, 0xeb, 0xa2, 0x84, 0x69,
|
|
15
|
+
]);
|
|
@@ -14,7 +14,9 @@ exports.createMcpServer = createMcpServer;
|
|
|
14
14
|
const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
|
|
15
15
|
const promises_1 = require("node:fs/promises");
|
|
16
16
|
const node_path_1 = __importDefault(require("node:path"));
|
|
17
|
+
const zod_1 = require("zod");
|
|
17
18
|
const version_1 = require("../../utilities/version");
|
|
19
|
+
const doc_search_1 = require("./tools/doc-search");
|
|
18
20
|
async function createMcpServer(context) {
|
|
19
21
|
const server = new mcp_js_1.McpServer({
|
|
20
22
|
name: 'angular-cli-server',
|
|
@@ -25,37 +27,82 @@ async function createMcpServer(context) {
|
|
|
25
27
|
},
|
|
26
28
|
});
|
|
27
29
|
server.registerResource('instructions', 'instructions://best-practices', {
|
|
28
|
-
title: 'Angular
|
|
29
|
-
description:
|
|
30
|
+
title: 'Angular Best Practices and Code Generation Guide',
|
|
31
|
+
description: "A comprehensive guide detailing Angular's best practices for code generation and development." +
|
|
32
|
+
' This guide should be used as a reference by an LLM to ensure any generated code' +
|
|
33
|
+
' adheres to modern Angular standards, including the use of standalone components,' +
|
|
34
|
+
' typed forms, modern control flow syntax, and other current conventions.',
|
|
30
35
|
mimeType: 'text/markdown',
|
|
31
36
|
}, async () => {
|
|
32
37
|
const text = await (0, promises_1.readFile)(node_path_1.default.join(__dirname, 'instructions', 'best-practices.md'), 'utf-8');
|
|
33
38
|
return { contents: [{ uri: 'instructions://best-practices', text }] };
|
|
34
39
|
});
|
|
35
40
|
server.registerTool('list_projects', {
|
|
36
|
-
title: 'List
|
|
37
|
-
description: '
|
|
38
|
-
'
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
title: 'List Angular Projects',
|
|
42
|
+
description: 'Lists the names of all applications and libraries defined within an Angular workspace. ' +
|
|
43
|
+
'It reads the `angular.json` configuration file to identify the projects. ',
|
|
44
|
+
annotations: {
|
|
45
|
+
readOnlyHint: true,
|
|
46
|
+
},
|
|
47
|
+
outputSchema: {
|
|
48
|
+
projects: zod_1.z.array(zod_1.z.object({
|
|
49
|
+
name: zod_1.z
|
|
50
|
+
.string()
|
|
51
|
+
.describe('The name of the project, as defined in the `angular.json` file.'),
|
|
52
|
+
type: zod_1.z
|
|
53
|
+
.enum(['application', 'library'])
|
|
54
|
+
.optional()
|
|
55
|
+
.describe(`The type of the project, either 'application' or 'library'.`),
|
|
56
|
+
root: zod_1.z
|
|
57
|
+
.string()
|
|
58
|
+
.describe('The root directory of the project, relative to the workspace root.'),
|
|
59
|
+
sourceRoot: zod_1.z
|
|
60
|
+
.string()
|
|
61
|
+
.describe(`The root directory of the project's source files, relative to the workspace root.`),
|
|
62
|
+
selectorPrefix: zod_1.z
|
|
63
|
+
.string()
|
|
64
|
+
.optional()
|
|
65
|
+
.describe('The prefix to use for component selectors.' +
|
|
66
|
+
` For example, a prefix of 'app' would result in selectors like '<app-my-component>'.`),
|
|
67
|
+
})),
|
|
68
|
+
},
|
|
69
|
+
}, async () => {
|
|
70
|
+
const { workspace } = context;
|
|
71
|
+
if (!workspace) {
|
|
41
72
|
return {
|
|
42
73
|
content: [
|
|
43
74
|
{
|
|
44
75
|
type: 'text',
|
|
45
|
-
text: '
|
|
76
|
+
text: 'No Angular workspace found.' +
|
|
77
|
+
' An `angular.json` file, which marks the root of a workspace,' +
|
|
78
|
+
' could not be located in the current directory or any of its parent directories.',
|
|
46
79
|
},
|
|
47
80
|
],
|
|
48
81
|
};
|
|
49
82
|
}
|
|
83
|
+
const projects = [];
|
|
84
|
+
// Convert to output format
|
|
85
|
+
for (const [name, project] of workspace.projects.entries()) {
|
|
86
|
+
projects.push({
|
|
87
|
+
name,
|
|
88
|
+
type: project.extensions['projectType'],
|
|
89
|
+
root: project.root,
|
|
90
|
+
sourceRoot: project.sourceRoot ?? node_path_1.default.posix.join(project.root, 'src'),
|
|
91
|
+
selectorPrefix: project.extensions['prefix'],
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
// The structuredContent field is newer and may not be supported by all hosts.
|
|
95
|
+
// A text representation of the content is also provided for compatibility.
|
|
50
96
|
return {
|
|
51
97
|
content: [
|
|
52
98
|
{
|
|
53
99
|
type: 'text',
|
|
54
|
-
text:
|
|
55
|
-
[...context.workspace.projects.keys()].join(','),
|
|
100
|
+
text: `Projects in the Angular workspace:\n${JSON.stringify(projects)}`,
|
|
56
101
|
},
|
|
57
102
|
],
|
|
103
|
+
structuredContent: { projects },
|
|
58
104
|
};
|
|
59
105
|
});
|
|
106
|
+
await (0, doc_search_1.registerDocSearchTool)(server);
|
|
60
107
|
return server;
|
|
61
108
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* Copyright Google LLC All Rights Reserved.
|
|
4
|
+
*
|
|
5
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
6
|
+
* found in the LICENSE file at https://angular.dev/license
|
|
7
|
+
*/
|
|
8
|
+
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
9
|
+
/**
|
|
10
|
+
* Registers a tool with the MCP server to search the Angular documentation.
|
|
11
|
+
*
|
|
12
|
+
* This tool uses Algolia to search the official Angular documentation.
|
|
13
|
+
*
|
|
14
|
+
* @param server The MCP server instance with which to register the tool.
|
|
15
|
+
*/
|
|
16
|
+
export declare function registerDocSearchTool(server: McpServer): Promise<void>;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* @license
|
|
4
|
+
* Copyright Google LLC All Rights Reserved.
|
|
5
|
+
*
|
|
6
|
+
* Use of this source code is governed by an MIT-style license that can be
|
|
7
|
+
* found in the LICENSE file at https://angular.dev/license
|
|
8
|
+
*/
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
21
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
22
|
+
}) : function(o, v) {
|
|
23
|
+
o["default"] = v;
|
|
24
|
+
});
|
|
25
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
26
|
+
var ownKeys = function(o) {
|
|
27
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
28
|
+
var ar = [];
|
|
29
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
30
|
+
return ar;
|
|
31
|
+
};
|
|
32
|
+
return ownKeys(o);
|
|
33
|
+
};
|
|
34
|
+
return function (mod) {
|
|
35
|
+
if (mod && mod.__esModule) return mod;
|
|
36
|
+
var result = {};
|
|
37
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
38
|
+
__setModuleDefault(result, mod);
|
|
39
|
+
return result;
|
|
40
|
+
};
|
|
41
|
+
})();
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
exports.registerDocSearchTool = registerDocSearchTool;
|
|
44
|
+
const node_crypto_1 = require("node:crypto");
|
|
45
|
+
const zod_1 = require("zod");
|
|
46
|
+
const constants_1 = require("../constants");
|
|
47
|
+
const ALGOLIA_APP_ID = 'L1XWT2UJ7F';
|
|
48
|
+
// https://www.algolia.com/doc/guides/security/api-keys/#search-only-api-key
|
|
49
|
+
// This is a search only, rate limited key. It is sent within the URL of the query request.
|
|
50
|
+
// This is not the actual key.
|
|
51
|
+
const ALGOLIA_API_E = '322d89dab5f2080fe09b795c93413c6a89222b13a447cdf3e6486d692717bc0c';
|
|
52
|
+
/**
|
|
53
|
+
* Registers a tool with the MCP server to search the Angular documentation.
|
|
54
|
+
*
|
|
55
|
+
* This tool uses Algolia to search the official Angular documentation.
|
|
56
|
+
*
|
|
57
|
+
* @param server The MCP server instance with which to register the tool.
|
|
58
|
+
*/
|
|
59
|
+
async function registerDocSearchTool(server) {
|
|
60
|
+
let client;
|
|
61
|
+
server.registerTool('search_documentation', {
|
|
62
|
+
title: 'Search Angular Documentation (angular.dev)',
|
|
63
|
+
description: 'Searches the official Angular documentation on https://angular.dev.' +
|
|
64
|
+
' This tool is useful for finding the most up-to-date information on Angular, including APIs, tutorials, and best practices.' +
|
|
65
|
+
' Use this when creating Angular specific code or answering questions that require knowledge of the latest Angular features.',
|
|
66
|
+
annotations: {
|
|
67
|
+
readOnlyHint: true,
|
|
68
|
+
},
|
|
69
|
+
inputSchema: {
|
|
70
|
+
query: zod_1.z
|
|
71
|
+
.string()
|
|
72
|
+
.describe('The search query to use when searching the Angular documentation.' +
|
|
73
|
+
' This should be a concise and specific query to get the most relevant results.'),
|
|
74
|
+
},
|
|
75
|
+
}, async ({ query }) => {
|
|
76
|
+
if (!client) {
|
|
77
|
+
const dcip = (0, node_crypto_1.createDecipheriv)('aes-256-gcm', (constants_1.k1 + ALGOLIA_APP_ID).padEnd(32, '^'), constants_1.iv).setAuthTag(Buffer.from(constants_1.at, 'base64'));
|
|
78
|
+
const { searchClient } = await Promise.resolve().then(() => __importStar(require('algoliasearch')));
|
|
79
|
+
client = searchClient(ALGOLIA_APP_ID, dcip.update(ALGOLIA_API_E, 'hex', 'utf-8') + dcip.final('utf-8'));
|
|
80
|
+
}
|
|
81
|
+
const { results } = await client.search(createSearchArguments(query));
|
|
82
|
+
// Convert results into text content entries instead of stringifying the entire object
|
|
83
|
+
const content = results.flatMap((result) => result.hits.map((hit) => {
|
|
84
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
85
|
+
const hierarchy = Object.values(hit.hierarchy).filter((x) => typeof x === 'string');
|
|
86
|
+
const title = hierarchy.pop();
|
|
87
|
+
const description = hierarchy.join(' > ');
|
|
88
|
+
return {
|
|
89
|
+
type: 'text',
|
|
90
|
+
text: `## ${title}\n${description}\nURL: ${hit.url}`,
|
|
91
|
+
};
|
|
92
|
+
}));
|
|
93
|
+
return { content };
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Creates the search arguments for an Algolia search.
|
|
98
|
+
*
|
|
99
|
+
* The arguments are based on the search implementation in `adev`.
|
|
100
|
+
*
|
|
101
|
+
* @param query The search query string.
|
|
102
|
+
* @returns The search arguments for the Algolia client.
|
|
103
|
+
*/
|
|
104
|
+
function createSearchArguments(query) {
|
|
105
|
+
// Search arguments are based on adev's search service:
|
|
106
|
+
// https://github.com/angular/angular/blob/4b614fbb3263d344dbb1b18fff24cb09c5a7582d/adev/shared-docs/services/search.service.ts#L58
|
|
107
|
+
return [
|
|
108
|
+
{
|
|
109
|
+
// TODO: Consider major version specific indices once available
|
|
110
|
+
indexName: 'angular_v17',
|
|
111
|
+
params: {
|
|
112
|
+
query,
|
|
113
|
+
attributesToRetrieve: [
|
|
114
|
+
'hierarchy.lvl0',
|
|
115
|
+
'hierarchy.lvl1',
|
|
116
|
+
'hierarchy.lvl2',
|
|
117
|
+
'hierarchy.lvl3',
|
|
118
|
+
'hierarchy.lvl4',
|
|
119
|
+
'hierarchy.lvl5',
|
|
120
|
+
'hierarchy.lvl6',
|
|
121
|
+
'content',
|
|
122
|
+
'type',
|
|
123
|
+
'url',
|
|
124
|
+
],
|
|
125
|
+
hitsPerPage: 10,
|
|
126
|
+
},
|
|
127
|
+
type: 'default',
|
|
128
|
+
},
|
|
129
|
+
];
|
|
130
|
+
}
|
package/src/utilities/version.js
CHANGED