@angular/cli 21.1.0-next.0 → 21.1.0-next.2
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/code-examples.db +0 -0
- package/package.json +17 -17
- package/src/commands/add/cli.js +129 -87
- package/src/commands/add/cli.js.map +1 -1
- package/src/commands/mcp/cli.js +4 -1
- package/src/commands/mcp/cli.js.map +1 -1
- package/src/commands/mcp/{dev-server.d.ts → devserver.d.ts} +4 -4
- package/src/commands/mcp/{dev-server.js → devserver.js} +14 -14
- package/src/commands/mcp/devserver.js.map +1 -0
- package/src/commands/mcp/host.d.ts +27 -0
- package/src/commands/mcp/host.js +8 -0
- package/src/commands/mcp/host.js.map +1 -1
- package/src/commands/mcp/mcp-server.d.ts +50 -9
- package/src/commands/mcp/mcp-server.js +26 -9
- package/src/commands/mcp/mcp-server.js.map +1 -1
- package/src/commands/mcp/resources/ai-tutor.md +199 -3
- package/src/commands/mcp/tools/ai-tutor.d.ts +5 -1
- package/src/commands/mcp/tools/best-practices.d.ts +3 -1
- package/src/commands/mcp/tools/best-practices.js.map +1 -1
- package/src/commands/mcp/tools/build.d.ts +8 -19
- package/src/commands/mcp/tools/build.js +2 -2
- package/src/commands/mcp/tools/build.js.map +1 -1
- package/src/commands/mcp/tools/devserver/devserver-start.d.ts +30 -0
- package/src/commands/mcp/tools/devserver/{start-devserver.js → devserver-start.js} +26 -27
- package/src/commands/mcp/tools/devserver/devserver-start.js.map +1 -0
- package/src/commands/mcp/tools/devserver/devserver-stop.d.ts +39 -0
- package/src/commands/mcp/tools/devserver/{stop-devserver.js → devserver-stop.js} +13 -13
- package/src/commands/mcp/tools/devserver/{stop-devserver.js.map → devserver-stop.js.map} +1 -1
- package/src/commands/mcp/tools/devserver/devserver-wait-for-build.d.ts +41 -0
- package/src/commands/mcp/tools/devserver/{wait-for-devserver-build.js → devserver-wait-for-build.js} +16 -16
- package/src/commands/mcp/tools/devserver/{wait-for-devserver-build.js.map → devserver-wait-for-build.js.map} +1 -1
- package/src/commands/mcp/tools/doc-search.d.ts +1 -11
- package/src/commands/mcp/tools/examples/database-discovery.d.ts +34 -0
- package/src/commands/mcp/tools/examples/database-discovery.js +87 -0
- package/src/commands/mcp/tools/examples/database-discovery.js.map +1 -0
- package/src/commands/mcp/tools/examples/database.d.ts +35 -0
- package/src/commands/mcp/tools/examples/database.js +125 -0
- package/src/commands/mcp/tools/examples/database.js.map +1 -0
- package/src/commands/mcp/tools/examples/index.d.ts +26 -0
- package/src/commands/mcp/tools/examples/index.js +149 -0
- package/src/commands/mcp/tools/examples/index.js.map +1 -0
- package/src/commands/mcp/tools/examples/query-escaper.d.ts +20 -0
- package/src/commands/mcp/tools/examples/query-escaper.js +68 -0
- package/src/commands/mcp/tools/examples/query-escaper.js.map +1 -0
- package/src/commands/mcp/tools/examples/runtime-database.d.ts +10 -0
- package/src/commands/mcp/tools/examples/runtime-database.js +192 -0
- package/src/commands/mcp/tools/examples/runtime-database.js.map +1 -0
- package/src/commands/mcp/tools/examples/schemas.d.ts +29 -0
- package/src/commands/mcp/tools/examples/schemas.js +101 -0
- package/src/commands/mcp/tools/examples/schemas.js.map +1 -0
- package/src/commands/mcp/tools/examples/utils.d.ts +14 -0
- package/src/commands/mcp/tools/examples/utils.js +31 -0
- package/src/commands/mcp/tools/examples/utils.js.map +1 -0
- package/src/commands/mcp/tools/modernize.d.ts +8 -18
- package/src/commands/mcp/tools/modernize.js +1 -1
- package/src/commands/mcp/tools/modernize.js.map +1 -1
- package/src/commands/mcp/tools/onpush-zoneless-migration/zoneless-migration.d.ts +3 -1
- package/src/commands/mcp/tools/projects.d.ts +23 -64
- package/src/commands/mcp/tools/tool-registry.d.ts +6 -5
- package/src/commands/mcp/tools/tool-registry.js.map +1 -1
- package/src/commands/update/cli.d.ts +0 -24
- package/src/commands/update/cli.js +39 -447
- package/src/commands/update/cli.js.map +1 -1
- package/src/commands/update/utilities/cli-version.d.ts +48 -0
- package/src/commands/update/utilities/cli-version.js +196 -0
- package/src/commands/update/utilities/cli-version.js.map +1 -0
- package/src/commands/update/utilities/constants.d.ts +12 -0
- package/src/commands/update/utilities/constants.js +16 -0
- package/src/commands/update/utilities/constants.js.map +1 -0
- package/src/commands/update/utilities/git.d.ts +34 -0
- package/src/commands/update/utilities/git.js +115 -0
- package/src/commands/update/utilities/git.js.map +1 -0
- package/src/commands/update/utilities/migration.d.ts +32 -0
- package/src/commands/update/utilities/migration.js +257 -0
- package/src/commands/update/utilities/migration.js.map +1 -0
- package/src/package-managers/error.d.ts +12 -0
- package/src/package-managers/host.d.ts +6 -0
- package/src/package-managers/host.js +8 -3
- package/src/package-managers/host.js.map +1 -1
- package/src/package-managers/package-manager-descriptor.d.ts +33 -9
- package/src/package-managers/package-manager-descriptor.js +28 -8
- package/src/package-managers/package-manager-descriptor.js.map +1 -1
- package/src/package-managers/package-manager.d.ts +21 -3
- package/src/package-managers/package-manager.js +133 -10
- package/src/package-managers/package-manager.js.map +1 -1
- package/src/package-managers/parsers.d.ts +69 -4
- package/src/package-managers/parsers.js +231 -32
- package/src/package-managers/parsers.js.map +1 -1
- package/src/utilities/version.js +1 -1
- package/src/commands/mcp/dev-server.js.map +0 -1
- package/src/commands/mcp/tools/devserver/start-devserver.d.ts +0 -41
- package/src/commands/mcp/tools/devserver/start-devserver.js.map +0 -1
- package/src/commands/mcp/tools/devserver/stop-devserver.d.ts +0 -49
- package/src/commands/mcp/tools/devserver/wait-for-devserver-build.d.ts +0 -47
- package/src/commands/mcp/tools/examples.d.ts +0 -58
- package/src/commands/mcp/tools/examples.js +0 -654
- package/src/commands/mcp/tools/examples.js.map +0 -1
|
@@ -0,0 +1,125 @@
|
|
|
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.validateDatabaseSchema = validateDatabaseSchema;
|
|
11
|
+
exports.queryDatabase = queryDatabase;
|
|
12
|
+
const query_escaper_1 = require("./query-escaper");
|
|
13
|
+
const EXPECTED_SCHEMA_VERSION = 1;
|
|
14
|
+
/**
|
|
15
|
+
* Validates the schema version of the example database.
|
|
16
|
+
*
|
|
17
|
+
* @param db The database connection to validate.
|
|
18
|
+
* @param dbSource A string identifying the source of the database (e.g., 'bundled' or a version number).
|
|
19
|
+
* @throws An error if the schema version is missing or incompatible.
|
|
20
|
+
*/
|
|
21
|
+
function validateDatabaseSchema(db, dbSource) {
|
|
22
|
+
const schemaVersionResult = db
|
|
23
|
+
.prepare('SELECT value FROM metadata WHERE key = ?')
|
|
24
|
+
.get('schema_version');
|
|
25
|
+
const actualSchemaVersion = schemaVersionResult ? Number(schemaVersionResult.value) : undefined;
|
|
26
|
+
if (actualSchemaVersion !== EXPECTED_SCHEMA_VERSION) {
|
|
27
|
+
db.close();
|
|
28
|
+
let errorMessage;
|
|
29
|
+
if (actualSchemaVersion === undefined) {
|
|
30
|
+
errorMessage = 'The example database is missing a schema version and cannot be used.';
|
|
31
|
+
}
|
|
32
|
+
else if (actualSchemaVersion > EXPECTED_SCHEMA_VERSION) {
|
|
33
|
+
errorMessage =
|
|
34
|
+
`This project's example database (version ${actualSchemaVersion})` +
|
|
35
|
+
` is newer than what this version of the Angular CLI supports (version ${EXPECTED_SCHEMA_VERSION}).` +
|
|
36
|
+
' Please update your `@angular/cli` package to a newer version.';
|
|
37
|
+
}
|
|
38
|
+
else {
|
|
39
|
+
errorMessage =
|
|
40
|
+
`This version of the Angular CLI (expects schema version ${EXPECTED_SCHEMA_VERSION})` +
|
|
41
|
+
` requires a newer example database than the one found in this project (version ${actualSchemaVersion}).`;
|
|
42
|
+
}
|
|
43
|
+
throw new Error(`Incompatible example database schema from source '${dbSource}':\n${errorMessage}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function queryDatabase(dbs, input) {
|
|
47
|
+
const { query, keywords, required_packages, related_concepts, includeExperimental } = input;
|
|
48
|
+
// Build the query dynamically
|
|
49
|
+
const params = [];
|
|
50
|
+
let sql = `SELECT e.title, e.summary, e.keywords, e.required_packages, e.related_concepts, e.related_tools, e.content, ` +
|
|
51
|
+
// The `snippet` function generates a contextual snippet of the matched text.
|
|
52
|
+
// Column 6 is the `content` column. We highlight matches with asterisks and limit the snippet size.
|
|
53
|
+
"snippet(examples_fts, 6, '**', '**', '...', 15) AS snippet, " +
|
|
54
|
+
// The `bm25` function returns the relevance score of the match. The weights
|
|
55
|
+
// assigned to each column boost the ranking of documents where the search
|
|
56
|
+
// term appears in a more important field.
|
|
57
|
+
// Column order: title, summary, keywords, required_packages, related_concepts, related_tools, content
|
|
58
|
+
'bm25(examples_fts, 10.0, 5.0, 5.0, 1.0, 2.0, 1.0, 1.0) AS rank ' +
|
|
59
|
+
'FROM examples e JOIN examples_fts ON e.id = examples_fts.rowid';
|
|
60
|
+
const whereClauses = [];
|
|
61
|
+
// FTS query
|
|
62
|
+
if (query) {
|
|
63
|
+
whereClauses.push('examples_fts MATCH ?');
|
|
64
|
+
params.push((0, query_escaper_1.escapeSearchQuery)(query));
|
|
65
|
+
}
|
|
66
|
+
// JSON array filters
|
|
67
|
+
const addJsonFilter = (column, values) => {
|
|
68
|
+
if (values?.length) {
|
|
69
|
+
for (const value of values) {
|
|
70
|
+
whereClauses.push(`e.${column} LIKE ?`);
|
|
71
|
+
params.push(`%"${value}"%`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
addJsonFilter('keywords', keywords);
|
|
76
|
+
addJsonFilter('required_packages', required_packages);
|
|
77
|
+
addJsonFilter('related_concepts', related_concepts);
|
|
78
|
+
if (!includeExperimental) {
|
|
79
|
+
whereClauses.push('e.experimental = 0');
|
|
80
|
+
}
|
|
81
|
+
if (whereClauses.length > 0) {
|
|
82
|
+
sql += ` WHERE ${whereClauses.join(' AND ')}`;
|
|
83
|
+
}
|
|
84
|
+
// Query database and return results
|
|
85
|
+
const examples = [];
|
|
86
|
+
const textContent = [];
|
|
87
|
+
for (const db of dbs) {
|
|
88
|
+
const queryStatement = db.prepare(sql);
|
|
89
|
+
for (const exampleRecord of queryStatement.all(...params)) {
|
|
90
|
+
const record = exampleRecord;
|
|
91
|
+
const example = {
|
|
92
|
+
title: record['title'],
|
|
93
|
+
summary: record['summary'],
|
|
94
|
+
keywords: JSON.parse(record['keywords'] || '[]'),
|
|
95
|
+
required_packages: JSON.parse(record['required_packages'] || '[]'),
|
|
96
|
+
related_concepts: JSON.parse(record['related_concepts'] || '[]'),
|
|
97
|
+
related_tools: JSON.parse(record['related_tools'] || '[]'),
|
|
98
|
+
content: record['content'],
|
|
99
|
+
snippet: record['snippet'],
|
|
100
|
+
rank: record['rank'],
|
|
101
|
+
};
|
|
102
|
+
examples.push(example);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Order the combined results by relevance.
|
|
106
|
+
// The `bm25` algorithm returns a smaller number for a more relevant match.
|
|
107
|
+
examples.sort((a, b) => a.rank - b.rank);
|
|
108
|
+
// The `rank` field is an internal implementation detail for sorting and should not be
|
|
109
|
+
// returned to the user. We create a new array of examples without the `rank`.
|
|
110
|
+
const finalExamples = examples.map(({ rank, ...rest }) => rest);
|
|
111
|
+
for (const example of finalExamples) {
|
|
112
|
+
// Also create a more structured text output
|
|
113
|
+
let text = `## Example: ${example.title}\n**Summary:** ${example.summary}`;
|
|
114
|
+
if (example.snippet) {
|
|
115
|
+
text += `\n**Snippet:** ${example.snippet}`;
|
|
116
|
+
}
|
|
117
|
+
text += `\n\n---\n\n${example.content}`;
|
|
118
|
+
textContent.push({ type: 'text', text });
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
content: textContent,
|
|
122
|
+
structuredContent: { examples: finalExamples },
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=database.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database.js","sourceRoot":"","sources":["database.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
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 FIND_EXAMPLE_TOOL: import("../tool-registry").McpToolDeclaration<{
|
|
9
|
+
workspacePath: import("zod").ZodOptional<import("zod").ZodString>;
|
|
10
|
+
query: import("zod").ZodString;
|
|
11
|
+
keywords: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString>>;
|
|
12
|
+
required_packages: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString>>;
|
|
13
|
+
related_concepts: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString>>;
|
|
14
|
+
includeExperimental: import("zod").ZodDefault<import("zod").ZodOptional<import("zod").ZodBoolean>>;
|
|
15
|
+
}, {
|
|
16
|
+
examples: import("zod").ZodArray<import("zod").ZodObject<{
|
|
17
|
+
title: import("zod").ZodString;
|
|
18
|
+
summary: import("zod").ZodString;
|
|
19
|
+
keywords: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString>>;
|
|
20
|
+
required_packages: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString>>;
|
|
21
|
+
related_concepts: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString>>;
|
|
22
|
+
related_tools: import("zod").ZodOptional<import("zod").ZodArray<import("zod").ZodString>>;
|
|
23
|
+
content: import("zod").ZodString;
|
|
24
|
+
snippet: import("zod").ZodOptional<import("zod").ZodString>;
|
|
25
|
+
}, import("zod/v4/core").$strip>>;
|
|
26
|
+
}>;
|
|
@@ -0,0 +1,149 @@
|
|
|
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.FIND_EXAMPLE_TOOL = void 0;
|
|
44
|
+
const tool_registry_1 = require("../tool-registry");
|
|
45
|
+
const database_1 = require("./database");
|
|
46
|
+
const database_discovery_1 = require("./database-discovery");
|
|
47
|
+
const runtime_database_1 = require("./runtime-database");
|
|
48
|
+
const schemas_1 = require("./schemas");
|
|
49
|
+
const utils_1 = require("./utils");
|
|
50
|
+
exports.FIND_EXAMPLE_TOOL = (0, tool_registry_1.declareTool)({
|
|
51
|
+
name: 'find_examples',
|
|
52
|
+
title: 'Find Angular Code Examples',
|
|
53
|
+
description: `
|
|
54
|
+
<Purpose>
|
|
55
|
+
Augments your knowledge base with a curated database of official, best-practice code examples,
|
|
56
|
+
focusing on **modern, new, and recently updated** Angular features. This tool acts as a RAG
|
|
57
|
+
(Retrieval-Augmented Generation) source, providing ground-truth information on the latest Angular
|
|
58
|
+
APIs and patterns. You **MUST** use it to understand and apply current standards when working with
|
|
59
|
+
new or evolving features.
|
|
60
|
+
</Purpose>
|
|
61
|
+
<Use Cases>
|
|
62
|
+
* **Knowledge Augmentation:** Learning about new or updated Angular features (e.g., query: 'signal input' or 'deferrable views').
|
|
63
|
+
* **Modern Implementation:** Finding the correct modern syntax for features
|
|
64
|
+
(e.g., query: 'functional route guard' or 'http client with fetch').
|
|
65
|
+
* **Refactoring to Modern Patterns:** Upgrading older code by finding examples of new syntax
|
|
66
|
+
(e.g., query: 'built-in control flow' to replace "*ngIf").
|
|
67
|
+
* **Advanced Filtering:** Combining a full-text search with filters to narrow results.
|
|
68
|
+
(e.g., query: 'forms', required_packages: ['@angular/forms'], keywords: ['validation'])
|
|
69
|
+
</Use Cases>
|
|
70
|
+
<Operational Notes>
|
|
71
|
+
* **Project-Specific Use (Recommended):** For tasks inside a user's project, you **MUST** provide the
|
|
72
|
+
\`workspacePath\` argument to get examples that match the project's Angular version. Get this
|
|
73
|
+
path from \`list_projects\`.
|
|
74
|
+
* **General Use:** If no project context is available (e.g., for general questions or learning),
|
|
75
|
+
you can call the tool without the \`workspacePath\` argument. It will return the latest
|
|
76
|
+
generic examples.
|
|
77
|
+
* **Tool Selection:** This database primarily contains examples for new and recently updated Angular
|
|
78
|
+
features. For established, core features, the main documentation (via the
|
|
79
|
+
\`search_documentation\` tool) may be a better source of information.
|
|
80
|
+
* The examples in this database are the single source of truth for modern Angular coding patterns.
|
|
81
|
+
* The search query uses a powerful full-text search syntax (FTS5). Refer to the 'query'
|
|
82
|
+
parameter description for detailed syntax rules and examples.
|
|
83
|
+
* You can combine the main 'query' with optional filters like 'keywords', 'required_packages',
|
|
84
|
+
and 'related_concepts' to create highly specific searches.
|
|
85
|
+
</Operational Notes>`,
|
|
86
|
+
inputSchema: schemas_1.findExampleInputSchema.shape,
|
|
87
|
+
outputSchema: schemas_1.findExampleOutputSchema.shape,
|
|
88
|
+
isReadOnly: true,
|
|
89
|
+
isLocalOnly: true,
|
|
90
|
+
shouldRegister: ({ logger }) => {
|
|
91
|
+
// sqlite database support requires Node.js 22.16+
|
|
92
|
+
const [nodeMajor, nodeMinor] = process.versions.node.split('.', 2).map(Number);
|
|
93
|
+
if (nodeMajor < 22 || (nodeMajor === 22 && nodeMinor < 16)) {
|
|
94
|
+
logger.warn(`MCP tool 'find_examples' requires Node.js 22.16 (or higher). ` +
|
|
95
|
+
' Registration of this tool has been skipped.');
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
return true;
|
|
99
|
+
},
|
|
100
|
+
factory: createFindExampleHandler,
|
|
101
|
+
});
|
|
102
|
+
async function createFindExampleHandler({ logger, exampleDatabasePath, host }) {
|
|
103
|
+
const runtimeDb = process.env['NG_MCP_EXAMPLES_DIR']
|
|
104
|
+
? await (0, runtime_database_1.setupRuntimeExamples)(process.env['NG_MCP_EXAMPLES_DIR'], host)
|
|
105
|
+
: undefined;
|
|
106
|
+
(0, utils_1.suppressSqliteWarning)();
|
|
107
|
+
return async (input) => {
|
|
108
|
+
// If the dev-time override is present, use it and bypass all other logic.
|
|
109
|
+
if (runtimeDb) {
|
|
110
|
+
return (0, database_1.queryDatabase)([runtimeDb], input);
|
|
111
|
+
}
|
|
112
|
+
const resolvedDbs = [];
|
|
113
|
+
// First, try to get all available version-specific guides.
|
|
114
|
+
if (input.workspacePath) {
|
|
115
|
+
const versionSpecificDbs = await (0, database_discovery_1.getVersionSpecificExampleDatabases)(input.workspacePath, logger, host);
|
|
116
|
+
for (const db of versionSpecificDbs) {
|
|
117
|
+
resolvedDbs.push({ path: db.dbPath, source: db.source });
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
// If no version-specific guides were found for any reason, fall back to the bundled version.
|
|
121
|
+
if (resolvedDbs.length === 0 && exampleDatabasePath) {
|
|
122
|
+
resolvedDbs.push({ path: exampleDatabasePath, source: 'bundled' });
|
|
123
|
+
}
|
|
124
|
+
if (resolvedDbs.length === 0) {
|
|
125
|
+
// This should be prevented by the registration logic in mcp-server.ts
|
|
126
|
+
throw new Error('No example databases are available.');
|
|
127
|
+
}
|
|
128
|
+
const { DatabaseSync } = await Promise.resolve().then(() => __importStar(require('node:sqlite')));
|
|
129
|
+
const dbConnections = [];
|
|
130
|
+
for (const { path, source } of resolvedDbs) {
|
|
131
|
+
const db = new DatabaseSync(path, { readOnly: true });
|
|
132
|
+
try {
|
|
133
|
+
(0, database_1.validateDatabaseSchema)(db, source);
|
|
134
|
+
dbConnections.push(db);
|
|
135
|
+
}
|
|
136
|
+
catch (e) {
|
|
137
|
+
logger.warn(e.message);
|
|
138
|
+
// If a database is invalid, we should not query it, but we should not fail the whole tool.
|
|
139
|
+
// We will just skip this database and try to use the others.
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (dbConnections.length === 0) {
|
|
144
|
+
throw new Error('All available example databases were invalid. Cannot perform query.');
|
|
145
|
+
}
|
|
146
|
+
return (0, database_1.queryDatabase)(dbConnections, input);
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
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
|
+
/**
|
|
9
|
+
* Escapes a search query for FTS5 by tokenizing and quoting terms.
|
|
10
|
+
*
|
|
11
|
+
* This function processes a raw search string and prepares it for an FTS5 full-text search.
|
|
12
|
+
* It correctly handles quoted phrases, logical operators (AND, OR, NOT), parentheses,
|
|
13
|
+
* and prefix searches (ending with an asterisk), ensuring that individual search
|
|
14
|
+
* terms are properly quoted to be treated as literals by the search engine.
|
|
15
|
+
* This is primarily intended to avoid unintentional usage of FTS5 query syntax by consumers.
|
|
16
|
+
*
|
|
17
|
+
* @param query The raw search query string.
|
|
18
|
+
* @returns A sanitized query string suitable for FTS5.
|
|
19
|
+
*/
|
|
20
|
+
export declare function escapeSearchQuery(query: string): string;
|
|
@@ -0,0 +1,68 @@
|
|
|
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.escapeSearchQuery = escapeSearchQuery;
|
|
11
|
+
/**
|
|
12
|
+
* Escapes a search query for FTS5 by tokenizing and quoting terms.
|
|
13
|
+
*
|
|
14
|
+
* This function processes a raw search string and prepares it for an FTS5 full-text search.
|
|
15
|
+
* It correctly handles quoted phrases, logical operators (AND, OR, NOT), parentheses,
|
|
16
|
+
* and prefix searches (ending with an asterisk), ensuring that individual search
|
|
17
|
+
* terms are properly quoted to be treated as literals by the search engine.
|
|
18
|
+
* This is primarily intended to avoid unintentional usage of FTS5 query syntax by consumers.
|
|
19
|
+
*
|
|
20
|
+
* @param query The raw search query string.
|
|
21
|
+
* @returns A sanitized query string suitable for FTS5.
|
|
22
|
+
*/
|
|
23
|
+
function escapeSearchQuery(query) {
|
|
24
|
+
// This regex tokenizes the query string into parts:
|
|
25
|
+
// 1. Quoted phrases (e.g., "foo bar")
|
|
26
|
+
// 2. Parentheses ( and )
|
|
27
|
+
// 3. FTS5 operators (AND, OR, NOT, NEAR)
|
|
28
|
+
// 4. Words, which can include a trailing asterisk for prefix search (e.g., foo*)
|
|
29
|
+
const tokenizer = /"([^"]*)"|([()])|\b(AND|OR|NOT|NEAR)\b|([^\s()]+)/g;
|
|
30
|
+
let match;
|
|
31
|
+
const result = [];
|
|
32
|
+
let lastIndex = 0;
|
|
33
|
+
while ((match = tokenizer.exec(query)) !== null) {
|
|
34
|
+
// Add any whitespace or other characters between tokens
|
|
35
|
+
if (match.index > lastIndex) {
|
|
36
|
+
result.push(query.substring(lastIndex, match.index));
|
|
37
|
+
}
|
|
38
|
+
const [, quoted, parenthesis, operator, term] = match;
|
|
39
|
+
if (quoted !== undefined) {
|
|
40
|
+
// It's a quoted phrase, keep it as is.
|
|
41
|
+
result.push(`"${quoted}"`);
|
|
42
|
+
}
|
|
43
|
+
else if (parenthesis) {
|
|
44
|
+
// It's a parenthesis, keep it as is.
|
|
45
|
+
result.push(parenthesis);
|
|
46
|
+
}
|
|
47
|
+
else if (operator) {
|
|
48
|
+
// It's an operator, keep it as is.
|
|
49
|
+
result.push(operator);
|
|
50
|
+
}
|
|
51
|
+
else if (term) {
|
|
52
|
+
// It's a term that needs to be quoted.
|
|
53
|
+
if (term.endsWith('*')) {
|
|
54
|
+
result.push(`"${term.slice(0, -1)}"*`);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
result.push(`"${term}"`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
lastIndex = tokenizer.lastIndex;
|
|
61
|
+
}
|
|
62
|
+
// Add any remaining part of the string
|
|
63
|
+
if (lastIndex < query.length) {
|
|
64
|
+
result.push(query.substring(lastIndex));
|
|
65
|
+
}
|
|
66
|
+
return result.join('');
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=query-escaper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-escaper.js","sourceRoot":"","sources":["query-escaper.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG;;AAcH,8CA6CC;AAzDD;;;;;;;;;;;GAWG;AACH,SAAgB,iBAAiB,CAAC,KAAa;IAC7C,oDAAoD;IACpD,sCAAsC;IACtC,yBAAyB;IACzB,yCAAyC;IACzC,iFAAiF;IACjF,MAAM,SAAS,GAAG,oDAAoD,CAAC;IACvE,IAAI,KAAK,CAAC;IACV,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,SAAS,GAAG,CAAC,CAAC;IAElB,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QAChD,wDAAwD;QACxD,IAAI,KAAK,CAAC,KAAK,GAAG,SAAS,EAAE,CAAC;YAC5B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;QACvD,CAAC;QAED,MAAM,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,CAAC,GAAG,KAAK,CAAC;QAEtD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACzB,uCAAuC;YACvC,MAAM,CAAC,IAAI,CAAC,IAAI,MAAM,GAAG,CAAC,CAAC;QAC7B,CAAC;aAAM,IAAI,WAAW,EAAE,CAAC;YACvB,qCAAqC;YACrC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;QAC3B,CAAC;aAAM,IAAI,QAAQ,EAAE,CAAC;YACpB,mCAAmC;YACnC,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;QACxB,CAAC;aAAM,IAAI,IAAI,EAAE,CAAC;YAChB,uCAAuC;YACvC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBACvB,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;YACzC,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,CAAC;YAC3B,CAAC;QACH,CAAC;QACD,SAAS,GAAG,SAAS,CAAC,SAAS,CAAC;IAClC,CAAC;IAED,uCAAuC;IACvC,IAAI,SAAS,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC;QAC7B,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC;IAC1C,CAAC;IAED,OAAO,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACzB,CAAC"}
|
|
@@ -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
|
+
import type { DatabaseSync } from 'node:sqlite';
|
|
9
|
+
import type { McpToolContext } from '../tool-registry';
|
|
10
|
+
export declare function setupRuntimeExamples(examplesPath: string, host: McpToolContext['host']): Promise<DatabaseSync>;
|
|
@@ -0,0 +1,192 @@
|
|
|
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.setupRuntimeExamples = setupRuntimeExamples;
|
|
44
|
+
const node_path_1 = require("node:path");
|
|
45
|
+
const zod_1 = require("zod");
|
|
46
|
+
/**
|
|
47
|
+
* A simple YAML front matter parser.
|
|
48
|
+
*
|
|
49
|
+
* This function extracts the YAML block enclosed by `---` at the beginning of a string
|
|
50
|
+
* and parses it into a JavaScript object. It is not a full YAML parser and only
|
|
51
|
+
* supports simple key-value pairs and string arrays.
|
|
52
|
+
*
|
|
53
|
+
* @param content The string content to parse.
|
|
54
|
+
* @returns A record containing the parsed front matter data.
|
|
55
|
+
*/
|
|
56
|
+
function parseFrontmatter(content) {
|
|
57
|
+
const match = content.match(/^---\r?\n(.*?)\r?\n---/s);
|
|
58
|
+
if (!match) {
|
|
59
|
+
return {};
|
|
60
|
+
}
|
|
61
|
+
const frontmatter = match[1];
|
|
62
|
+
const data = {};
|
|
63
|
+
const lines = frontmatter.split(/\r?\n/);
|
|
64
|
+
let currentKey = '';
|
|
65
|
+
let isArray = false;
|
|
66
|
+
const arrayValues = [];
|
|
67
|
+
for (const line of lines) {
|
|
68
|
+
const keyValueMatch = line.match(/^([^:]+):\s*(.*)/);
|
|
69
|
+
if (keyValueMatch) {
|
|
70
|
+
if (currentKey && isArray) {
|
|
71
|
+
data[currentKey] = arrayValues.slice();
|
|
72
|
+
arrayValues.length = 0;
|
|
73
|
+
}
|
|
74
|
+
const [, key, value] = keyValueMatch;
|
|
75
|
+
currentKey = key.trim();
|
|
76
|
+
isArray = value.trim() === '';
|
|
77
|
+
if (!isArray) {
|
|
78
|
+
const trimmedValue = value.trim();
|
|
79
|
+
if (trimmedValue === 'true') {
|
|
80
|
+
data[currentKey] = true;
|
|
81
|
+
}
|
|
82
|
+
else if (trimmedValue === 'false') {
|
|
83
|
+
data[currentKey] = false;
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
data[currentKey] = trimmedValue;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
const arrayItemMatch = line.match(/^\s*-\s*(.*)/);
|
|
92
|
+
if (arrayItemMatch && currentKey && isArray) {
|
|
93
|
+
let value = arrayItemMatch[1].trim();
|
|
94
|
+
// Unquote if the value is quoted.
|
|
95
|
+
if ((value.startsWith("'") && value.endsWith("'")) ||
|
|
96
|
+
(value.startsWith('"') && value.endsWith('"'))) {
|
|
97
|
+
value = value.slice(1, -1);
|
|
98
|
+
}
|
|
99
|
+
arrayValues.push(value);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (currentKey && isArray) {
|
|
104
|
+
data[currentKey] = arrayValues;
|
|
105
|
+
}
|
|
106
|
+
return data;
|
|
107
|
+
}
|
|
108
|
+
async function setupRuntimeExamples(examplesPath, host) {
|
|
109
|
+
const { DatabaseSync } = await Promise.resolve().then(() => __importStar(require('node:sqlite')));
|
|
110
|
+
const db = new DatabaseSync(':memory:');
|
|
111
|
+
// Create a relational table to store the structured example data.
|
|
112
|
+
db.exec(`
|
|
113
|
+
CREATE TABLE metadata (
|
|
114
|
+
key TEXT PRIMARY KEY NOT NULL,
|
|
115
|
+
value TEXT NOT NULL
|
|
116
|
+
);
|
|
117
|
+
`);
|
|
118
|
+
db.exec(`
|
|
119
|
+
INSERT INTO metadata (key, value) VALUES
|
|
120
|
+
('schema_version', '1'),
|
|
121
|
+
('created_at', '${new Date().toISOString()}');
|
|
122
|
+
`);
|
|
123
|
+
db.exec(`
|
|
124
|
+
CREATE TABLE examples (
|
|
125
|
+
id INTEGER PRIMARY KEY,
|
|
126
|
+
title TEXT NOT NULL,
|
|
127
|
+
summary TEXT NOT NULL,
|
|
128
|
+
keywords TEXT,
|
|
129
|
+
required_packages TEXT,
|
|
130
|
+
related_concepts TEXT,
|
|
131
|
+
related_tools TEXT,
|
|
132
|
+
experimental INTEGER NOT NULL DEFAULT 0,
|
|
133
|
+
content TEXT NOT NULL
|
|
134
|
+
);
|
|
135
|
+
`);
|
|
136
|
+
// Create an FTS5 virtual table to provide full-text search capabilities.
|
|
137
|
+
db.exec(`
|
|
138
|
+
CREATE VIRTUAL TABLE examples_fts USING fts5(
|
|
139
|
+
title,
|
|
140
|
+
summary,
|
|
141
|
+
keywords,
|
|
142
|
+
required_packages,
|
|
143
|
+
related_concepts,
|
|
144
|
+
related_tools,
|
|
145
|
+
content,
|
|
146
|
+
content='examples',
|
|
147
|
+
content_rowid='id',
|
|
148
|
+
tokenize = 'porter ascii'
|
|
149
|
+
);
|
|
150
|
+
`);
|
|
151
|
+
// Create triggers to keep the FTS table synchronized with the examples table.
|
|
152
|
+
db.exec(`
|
|
153
|
+
CREATE TRIGGER examples_after_insert AFTER INSERT ON examples BEGIN
|
|
154
|
+
INSERT INTO examples_fts(rowid, title, summary, keywords, required_packages, related_concepts, related_tools, content)
|
|
155
|
+
VALUES (
|
|
156
|
+
new.id, new.title, new.summary, new.keywords, new.required_packages, new.related_concepts,
|
|
157
|
+
new.related_tools, new.content
|
|
158
|
+
);
|
|
159
|
+
END;
|
|
160
|
+
`);
|
|
161
|
+
const insertStatement = db.prepare('INSERT INTO examples(' +
|
|
162
|
+
'title, summary, keywords, required_packages, related_concepts, related_tools, experimental, content' +
|
|
163
|
+
') VALUES(?, ?, ?, ?, ?, ?, ?, ?);');
|
|
164
|
+
const frontmatterSchema = zod_1.z.object({
|
|
165
|
+
title: zod_1.z.string(),
|
|
166
|
+
summary: zod_1.z.string(),
|
|
167
|
+
keywords: zod_1.z.array(zod_1.z.string()).optional(),
|
|
168
|
+
required_packages: zod_1.z.array(zod_1.z.string()).optional(),
|
|
169
|
+
related_concepts: zod_1.z.array(zod_1.z.string()).optional(),
|
|
170
|
+
related_tools: zod_1.z.array(zod_1.z.string()).optional(),
|
|
171
|
+
experimental: zod_1.z.boolean().optional(),
|
|
172
|
+
});
|
|
173
|
+
db.exec('BEGIN TRANSACTION');
|
|
174
|
+
for await (const entry of host.glob('**/*.md', { cwd: examplesPath })) {
|
|
175
|
+
if (!entry.isFile()) {
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
const content = await host.readFile((0, node_path_1.join)(entry.parentPath, entry.name), 'utf-8');
|
|
179
|
+
const frontmatter = parseFrontmatter(content);
|
|
180
|
+
const validation = frontmatterSchema.safeParse(frontmatter);
|
|
181
|
+
if (!validation.success) {
|
|
182
|
+
// eslint-disable-next-line no-console
|
|
183
|
+
console.warn(`Skipping invalid example file ${entry.name}:`, validation.error.issues);
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
const { title, summary, keywords, required_packages, related_concepts, related_tools, experimental, } = validation.data;
|
|
187
|
+
insertStatement.run(title, summary, JSON.stringify(keywords ?? []), JSON.stringify(required_packages ?? []), JSON.stringify(related_concepts ?? []), JSON.stringify(related_tools ?? []), experimental ? 1 : 0, content);
|
|
188
|
+
}
|
|
189
|
+
db.exec('END TRANSACTION');
|
|
190
|
+
return db;
|
|
191
|
+
}
|
|
192
|
+
//# sourceMappingURL=runtime-database.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-database.js","sourceRoot":"","sources":["runtime-database.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
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 { z } from 'zod';
|
|
9
|
+
export declare const findExampleInputSchema: z.ZodObject<{
|
|
10
|
+
workspacePath: z.ZodOptional<z.ZodString>;
|
|
11
|
+
query: z.ZodString;
|
|
12
|
+
keywords: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
13
|
+
required_packages: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
14
|
+
related_concepts: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
15
|
+
includeExperimental: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
16
|
+
}, z.core.$strip>;
|
|
17
|
+
export type FindExampleInput = z.infer<typeof findExampleInputSchema>;
|
|
18
|
+
export declare const findExampleOutputSchema: z.ZodObject<{
|
|
19
|
+
examples: z.ZodArray<z.ZodObject<{
|
|
20
|
+
title: z.ZodString;
|
|
21
|
+
summary: z.ZodString;
|
|
22
|
+
keywords: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
23
|
+
required_packages: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
24
|
+
related_concepts: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
25
|
+
related_tools: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
26
|
+
content: z.ZodString;
|
|
27
|
+
snippet: z.ZodOptional<z.ZodString>;
|
|
28
|
+
}, z.core.$strip>>;
|
|
29
|
+
}, z.core.$strip>;
|