@vfarcic/dot-ai 0.173.0 → 0.175.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/README.md +1 -0
- package/dist/core/base-vector-service.d.ts +6 -0
- package/dist/core/base-vector-service.d.ts.map +1 -1
- package/dist/core/base-vector-service.js +13 -0
- package/dist/core/capability-tools.d.ts +38 -0
- package/dist/core/capability-tools.d.ts.map +1 -0
- package/dist/core/capability-tools.js +202 -0
- package/dist/core/resource-tools.d.ts +38 -0
- package/dist/core/resource-tools.d.ts.map +1 -0
- package/dist/core/resource-tools.js +216 -0
- package/dist/core/resource-vector-service.d.ts +12 -0
- package/dist/core/resource-vector-service.d.ts.map +1 -1
- package/dist/core/resource-vector-service.js +16 -5
- package/dist/core/tracing/qdrant-tracing.d.ts +1 -1
- package/dist/core/tracing/qdrant-tracing.d.ts.map +1 -1
- package/dist/core/user-prompts-loader.d.ts +66 -0
- package/dist/core/user-prompts-loader.d.ts.map +1 -0
- package/dist/core/user-prompts-loader.js +319 -0
- package/dist/core/vector-db-service.d.ts +6 -0
- package/dist/core/vector-db-service.d.ts.map +1 -1
- package/dist/core/vector-db-service.js +32 -0
- package/dist/interfaces/mcp.d.ts.map +1 -1
- package/dist/interfaces/mcp.js +10 -2
- package/dist/interfaces/resource-sync-handler.d.ts.map +1 -1
- package/dist/interfaces/resource-sync-handler.js +4 -1
- package/dist/interfaces/rest-api.d.ts +8 -0
- package/dist/interfaces/rest-api.d.ts.map +1 -1
- package/dist/interfaces/rest-api.js +98 -2
- package/dist/tools/prompts.d.ts +21 -3
- package/dist/tools/prompts.d.ts.map +1 -1
- package/dist/tools/prompts.js +166 -26
- package/dist/tools/query.d.ts +34 -0
- package/dist/tools/query.d.ts.map +1 -0
- package/dist/tools/query.js +209 -0
- package/package.json +1 -1
- package/prompts/query-system.md +15 -0
- package/scripts/crossplane.nu +8 -58
- package/scripts/dot-ai.nu +23 -9
- package/shared-prompts/prd-create.md +6 -2
- package/shared-prompts/prd-start.md +17 -3
|
@@ -10,6 +10,7 @@ exports.RestApiRouter = exports.HttpStatus = void 0;
|
|
|
10
10
|
const node_url_1 = require("node:url");
|
|
11
11
|
const openapi_generator_1 = require("./openapi-generator");
|
|
12
12
|
const resource_sync_handler_1 = require("./resource-sync-handler");
|
|
13
|
+
const prompts_1 = require("../tools/prompts");
|
|
13
14
|
/**
|
|
14
15
|
* HTTP status codes for REST responses
|
|
15
16
|
*/
|
|
@@ -118,6 +119,25 @@ class RestApiRouter {
|
|
|
118
119
|
await this.sendErrorResponse(res, requestId, HttpStatus.NOT_FOUND, 'NOT_FOUND', 'Unknown resources endpoint');
|
|
119
120
|
}
|
|
120
121
|
break;
|
|
122
|
+
case 'prompts':
|
|
123
|
+
if (req.method === 'GET') {
|
|
124
|
+
await this.handlePromptsListRequest(req, res, requestId);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
await this.sendErrorResponse(res, requestId, HttpStatus.METHOD_NOT_ALLOWED, 'METHOD_NOT_ALLOWED', 'Only GET method allowed for prompts list');
|
|
128
|
+
}
|
|
129
|
+
break;
|
|
130
|
+
case 'prompt':
|
|
131
|
+
if (req.method === 'POST' && pathMatch.promptName) {
|
|
132
|
+
await this.handlePromptsGetRequest(req, res, requestId, pathMatch.promptName, body);
|
|
133
|
+
}
|
|
134
|
+
else if (req.method !== 'POST') {
|
|
135
|
+
await this.sendErrorResponse(res, requestId, HttpStatus.METHOD_NOT_ALLOWED, 'METHOD_NOT_ALLOWED', 'Only POST method allowed for prompt get');
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
await this.sendErrorResponse(res, requestId, HttpStatus.BAD_REQUEST, 'BAD_REQUEST', 'Prompt name is required');
|
|
139
|
+
}
|
|
140
|
+
break;
|
|
121
141
|
default:
|
|
122
142
|
await this.sendErrorResponse(res, requestId, HttpStatus.NOT_FOUND, 'NOT_FOUND', 'Unknown API endpoint');
|
|
123
143
|
}
|
|
@@ -139,6 +159,8 @@ class RestApiRouter {
|
|
|
139
159
|
// /api/v1/tools/{toolName} -> tool execution
|
|
140
160
|
// /api/v1/openapi -> OpenAPI spec
|
|
141
161
|
// /api/v1/resources/sync -> resource sync from controller
|
|
162
|
+
// /api/v1/prompts -> prompts list
|
|
163
|
+
// /api/v1/prompts/{promptName} -> prompt get
|
|
142
164
|
const basePath = `${this.config.basePath}/${this.config.version}`;
|
|
143
165
|
if (!pathname.startsWith(basePath)) {
|
|
144
166
|
return null;
|
|
@@ -162,6 +184,16 @@ class RestApiRouter {
|
|
|
162
184
|
if (cleanPath === 'resources/sync') {
|
|
163
185
|
return { endpoint: 'resources', action: 'sync' };
|
|
164
186
|
}
|
|
187
|
+
// Handle prompts endpoints
|
|
188
|
+
if (cleanPath === 'prompts') {
|
|
189
|
+
return { endpoint: 'prompts' };
|
|
190
|
+
}
|
|
191
|
+
if (cleanPath.startsWith('prompts/')) {
|
|
192
|
+
const promptName = cleanPath.substring(8); // Remove 'prompts/'
|
|
193
|
+
if (promptName) {
|
|
194
|
+
return { endpoint: 'prompt', promptName };
|
|
195
|
+
}
|
|
196
|
+
}
|
|
165
197
|
return null;
|
|
166
198
|
}
|
|
167
199
|
/**
|
|
@@ -272,12 +304,13 @@ class RestApiRouter {
|
|
|
272
304
|
});
|
|
273
305
|
}
|
|
274
306
|
catch (error) {
|
|
307
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
275
308
|
this.logger.error('Tool execution failed', error instanceof Error ? error : new Error(String(error)), {
|
|
276
309
|
requestId,
|
|
277
310
|
toolName,
|
|
278
|
-
errorMessage
|
|
311
|
+
errorMessage
|
|
279
312
|
});
|
|
280
|
-
await this.sendErrorResponse(res, requestId, HttpStatus.INTERNAL_SERVER_ERROR, 'EXECUTION_ERROR',
|
|
313
|
+
await this.sendErrorResponse(res, requestId, HttpStatus.INTERNAL_SERVER_ERROR, 'EXECUTION_ERROR', errorMessage);
|
|
281
314
|
}
|
|
282
315
|
}
|
|
283
316
|
/**
|
|
@@ -346,6 +379,69 @@ class RestApiRouter {
|
|
|
346
379
|
await this.sendErrorResponse(res, requestId, HttpStatus.INTERNAL_SERVER_ERROR, 'SYNC_ERROR', 'Resource sync failed', { error: errorMessage });
|
|
347
380
|
}
|
|
348
381
|
}
|
|
382
|
+
/**
|
|
383
|
+
* Handle prompts list requests
|
|
384
|
+
*/
|
|
385
|
+
async handlePromptsListRequest(req, res, requestId) {
|
|
386
|
+
try {
|
|
387
|
+
this.logger.info('Processing prompts list request', { requestId });
|
|
388
|
+
const result = await (0, prompts_1.handlePromptsListRequest)({}, this.logger, requestId);
|
|
389
|
+
const response = {
|
|
390
|
+
success: true,
|
|
391
|
+
data: result,
|
|
392
|
+
meta: {
|
|
393
|
+
timestamp: new Date().toISOString(),
|
|
394
|
+
requestId,
|
|
395
|
+
version: this.config.version
|
|
396
|
+
}
|
|
397
|
+
};
|
|
398
|
+
await this.sendJsonResponse(res, HttpStatus.OK, response);
|
|
399
|
+
this.logger.info('Prompts list request completed', {
|
|
400
|
+
requestId,
|
|
401
|
+
promptCount: result.prompts?.length || 0
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
catch (error) {
|
|
405
|
+
this.logger.error('Prompts list request failed', error instanceof Error ? error : new Error(String(error)), {
|
|
406
|
+
requestId
|
|
407
|
+
});
|
|
408
|
+
await this.sendErrorResponse(res, requestId, HttpStatus.INTERNAL_SERVER_ERROR, 'PROMPTS_LIST_ERROR', 'Failed to list prompts');
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Handle prompt get requests
|
|
413
|
+
*/
|
|
414
|
+
async handlePromptsGetRequest(req, res, requestId, promptName, body) {
|
|
415
|
+
try {
|
|
416
|
+
this.logger.info('Processing prompt get request', { requestId, promptName });
|
|
417
|
+
const result = await (0, prompts_1.handlePromptsGetRequest)({ name: promptName, arguments: body?.arguments }, this.logger, requestId);
|
|
418
|
+
const response = {
|
|
419
|
+
success: true,
|
|
420
|
+
data: result,
|
|
421
|
+
meta: {
|
|
422
|
+
timestamp: new Date().toISOString(),
|
|
423
|
+
requestId,
|
|
424
|
+
version: this.config.version
|
|
425
|
+
}
|
|
426
|
+
};
|
|
427
|
+
await this.sendJsonResponse(res, HttpStatus.OK, response);
|
|
428
|
+
this.logger.info('Prompt get request completed', {
|
|
429
|
+
requestId,
|
|
430
|
+
promptName
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
catch (error) {
|
|
434
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
435
|
+
this.logger.error('Prompt get request failed', error instanceof Error ? error : new Error(String(error)), {
|
|
436
|
+
requestId,
|
|
437
|
+
promptName
|
|
438
|
+
});
|
|
439
|
+
// Check if it's a validation error (missing required arguments or prompt not found)
|
|
440
|
+
const isValidationError = errorMessage.includes('Missing required arguments') ||
|
|
441
|
+
errorMessage.includes('Prompt not found');
|
|
442
|
+
await this.sendErrorResponse(res, requestId, isValidationError ? HttpStatus.BAD_REQUEST : HttpStatus.INTERNAL_SERVER_ERROR, isValidationError ? 'VALIDATION_ERROR' : 'PROMPT_GET_ERROR', errorMessage);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
349
445
|
/**
|
|
350
446
|
* Set CORS headers
|
|
351
447
|
*/
|
package/dist/tools/prompts.d.ts
CHANGED
|
@@ -2,24 +2,42 @@
|
|
|
2
2
|
* MCP Prompts Handler - Manages shared prompt library
|
|
3
3
|
*/
|
|
4
4
|
import { Logger } from '../core/error-handling';
|
|
5
|
+
export interface PromptArgument {
|
|
6
|
+
name: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
required?: boolean;
|
|
9
|
+
}
|
|
5
10
|
export interface PromptMetadata {
|
|
6
11
|
name: string;
|
|
7
12
|
description: string;
|
|
8
13
|
category: string;
|
|
14
|
+
arguments?: PromptArgument[];
|
|
9
15
|
}
|
|
10
16
|
export interface Prompt {
|
|
11
17
|
name: string;
|
|
12
18
|
description: string;
|
|
13
19
|
content: string;
|
|
20
|
+
arguments?: PromptArgument[];
|
|
21
|
+
source: 'built-in' | 'user';
|
|
14
22
|
}
|
|
15
23
|
/**
|
|
16
24
|
* Loads and parses a prompt file with YAML frontmatter
|
|
17
25
|
*/
|
|
18
|
-
export declare function loadPromptFile(filePath: string): Prompt;
|
|
26
|
+
export declare function loadPromptFile(filePath: string, source?: 'built-in' | 'user'): Prompt;
|
|
27
|
+
/**
|
|
28
|
+
* Loads built-in prompts from the shared-prompts directory
|
|
29
|
+
*/
|
|
30
|
+
export declare function loadBuiltInPrompts(logger: Logger, baseDir?: string): Prompt[];
|
|
31
|
+
/**
|
|
32
|
+
* Merge built-in and user prompts with collision detection
|
|
33
|
+
* Built-in prompts take precedence over user prompts with the same name
|
|
34
|
+
*/
|
|
35
|
+
export declare function mergePrompts(builtInPrompts: Prompt[], userPrompts: Prompt[], logger: Logger): Prompt[];
|
|
19
36
|
/**
|
|
20
|
-
* Loads all prompts
|
|
37
|
+
* Loads all prompts (built-in + user) with collision detection
|
|
38
|
+
* This is the main entry point for loading prompts
|
|
21
39
|
*/
|
|
22
|
-
export declare function loadAllPrompts(logger: Logger, baseDir?: string): Prompt[]
|
|
40
|
+
export declare function loadAllPrompts(logger: Logger, baseDir?: string, forceRefresh?: boolean): Promise<Prompt[]>;
|
|
23
41
|
/**
|
|
24
42
|
* Handle prompts/list MCP request
|
|
25
43
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../src/tools/prompts.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAOhD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../../src/tools/prompts.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAOhD,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,cAAc,EAAE,CAAC;CAC9B;AAED,MAAM,WAAW,MAAM;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,cAAc,EAAE,CAAC;IAC7B,MAAM,EAAE,UAAU,GAAG,MAAM,CAAC;CAC7B;AA8ED;;GAEG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAE,UAAU,GAAG,MAAmB,GAAG,MAAM,CAmCjG;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAoC7E;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,cAAc,EAAE,MAAM,EAAE,EACxB,WAAW,EAAE,MAAM,EAAE,EACrB,MAAM,EAAE,MAAM,GACb,MAAM,EAAE,CAgBV;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE,MAAM,EAChB,YAAY,GAAE,OAAe,GAC5B,OAAO,CAAC,MAAM,EAAE,CAAC,CA0BnB;AAED;;GAEG;AACH,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,GAAG,EACT,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,GAAG,CAAC,CA4Cd;AAED;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,IAAI,EAAE,GAAG,EACT,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,GAAG,CAAC,CAiGd"}
|
package/dist/tools/prompts.js
CHANGED
|
@@ -37,16 +37,90 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
37
37
|
})();
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.loadPromptFile = loadPromptFile;
|
|
40
|
+
exports.loadBuiltInPrompts = loadBuiltInPrompts;
|
|
41
|
+
exports.mergePrompts = mergePrompts;
|
|
40
42
|
exports.loadAllPrompts = loadAllPrompts;
|
|
41
43
|
exports.handlePromptsListRequest = handlePromptsListRequest;
|
|
42
44
|
exports.handlePromptsGetRequest = handlePromptsGetRequest;
|
|
43
45
|
const fs = __importStar(require("fs"));
|
|
44
46
|
const path = __importStar(require("path"));
|
|
45
47
|
const error_handling_1 = require("../core/error-handling");
|
|
48
|
+
/**
|
|
49
|
+
* Parses YAML frontmatter with support for nested arguments array
|
|
50
|
+
*/
|
|
51
|
+
function parseYamlFrontmatter(yaml) {
|
|
52
|
+
const metadata = {};
|
|
53
|
+
const lines = yaml.split('\n');
|
|
54
|
+
let i = 0;
|
|
55
|
+
while (i < lines.length) {
|
|
56
|
+
const line = lines[i];
|
|
57
|
+
// Check for arguments array start
|
|
58
|
+
if (line.match(/^arguments:\s*$/)) {
|
|
59
|
+
const args = [];
|
|
60
|
+
i++;
|
|
61
|
+
// Parse array items (lines starting with " - ")
|
|
62
|
+
while (i < lines.length && lines[i].match(/^\s+-\s/)) {
|
|
63
|
+
const arg = { name: '' };
|
|
64
|
+
// First line of array item: " - name: value"
|
|
65
|
+
const firstLineMatch = lines[i].match(/^\s+-\s+(\w+):\s*(.*)$/);
|
|
66
|
+
if (firstLineMatch) {
|
|
67
|
+
const [, key, value] = firstLineMatch;
|
|
68
|
+
if (key === 'name') {
|
|
69
|
+
arg.name = value.trim().replace(/^["']|["']$/g, '');
|
|
70
|
+
}
|
|
71
|
+
else if (key === 'description') {
|
|
72
|
+
arg.description = value.trim().replace(/^["']|["']$/g, '');
|
|
73
|
+
}
|
|
74
|
+
else if (key === 'required') {
|
|
75
|
+
arg.required = value.trim().toLowerCase() === 'true';
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
i++;
|
|
79
|
+
// Continue parsing properties of this array item (lines starting with " ")
|
|
80
|
+
while (i < lines.length && lines[i].match(/^\s{4,}\w+:/)) {
|
|
81
|
+
const propMatch = lines[i].match(/^\s+(\w+):\s*(.*)$/);
|
|
82
|
+
if (propMatch) {
|
|
83
|
+
const [, key, value] = propMatch;
|
|
84
|
+
if (key === 'name') {
|
|
85
|
+
arg.name = value.trim().replace(/^["']|["']$/g, '');
|
|
86
|
+
}
|
|
87
|
+
else if (key === 'description') {
|
|
88
|
+
arg.description = value.trim().replace(/^["']|["']$/g, '');
|
|
89
|
+
}
|
|
90
|
+
else if (key === 'required') {
|
|
91
|
+
arg.required = value.trim().toLowerCase() === 'true';
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
i++;
|
|
95
|
+
}
|
|
96
|
+
if (arg.name) {
|
|
97
|
+
args.push(arg);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (args.length > 0) {
|
|
101
|
+
metadata.arguments = args;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
// Simple key-value pair
|
|
106
|
+
const match = line.match(/^([^:]+):\s*(.+)$/);
|
|
107
|
+
if (match) {
|
|
108
|
+
const [, key, value] = match;
|
|
109
|
+
const cleanValue = value.trim().replace(/^["']|["']$/g, '');
|
|
110
|
+
const trimmedKey = key.trim();
|
|
111
|
+
if (trimmedKey !== 'arguments') {
|
|
112
|
+
metadata[trimmedKey] = cleanValue;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
i++;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return metadata;
|
|
119
|
+
}
|
|
46
120
|
/**
|
|
47
121
|
* Loads and parses a prompt file with YAML frontmatter
|
|
48
122
|
*/
|
|
49
|
-
function loadPromptFile(filePath) {
|
|
123
|
+
function loadPromptFile(filePath, source = 'built-in') {
|
|
50
124
|
try {
|
|
51
125
|
const content = fs.readFileSync(filePath, 'utf8');
|
|
52
126
|
// Parse YAML frontmatter
|
|
@@ -55,18 +129,8 @@ function loadPromptFile(filePath) {
|
|
|
55
129
|
throw new Error(`Invalid prompt file format: missing YAML frontmatter in ${filePath}`);
|
|
56
130
|
}
|
|
57
131
|
const [, frontmatterYaml, promptContent] = frontmatterMatch;
|
|
58
|
-
//
|
|
59
|
-
const metadata =
|
|
60
|
-
const lines = frontmatterYaml.split('\n');
|
|
61
|
-
for (const line of lines) {
|
|
62
|
-
const match = line.match(/^([^:]+):\s*(.+)$/);
|
|
63
|
-
if (match) {
|
|
64
|
-
const [, key, value] = match;
|
|
65
|
-
// Remove quotes if present
|
|
66
|
-
const cleanValue = value.trim().replace(/^["']|["']$/g, '');
|
|
67
|
-
metadata[key.trim()] = cleanValue;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
132
|
+
// Parse YAML with support for arguments array
|
|
133
|
+
const metadata = parseYamlFrontmatter(frontmatterYaml);
|
|
70
134
|
if (!metadata.name || !metadata.description || !metadata.category) {
|
|
71
135
|
throw new Error(`Missing required metadata in ${filePath}: name, description, category`);
|
|
72
136
|
}
|
|
@@ -74,6 +138,8 @@ function loadPromptFile(filePath) {
|
|
|
74
138
|
name: metadata.name,
|
|
75
139
|
description: metadata.description,
|
|
76
140
|
content: promptContent.trim(),
|
|
141
|
+
arguments: metadata.arguments,
|
|
142
|
+
source,
|
|
77
143
|
};
|
|
78
144
|
}
|
|
79
145
|
catch (error) {
|
|
@@ -81,9 +147,9 @@ function loadPromptFile(filePath) {
|
|
|
81
147
|
}
|
|
82
148
|
}
|
|
83
149
|
/**
|
|
84
|
-
* Loads
|
|
150
|
+
* Loads built-in prompts from the shared-prompts directory
|
|
85
151
|
*/
|
|
86
|
-
function
|
|
152
|
+
function loadBuiltInPrompts(logger, baseDir) {
|
|
87
153
|
try {
|
|
88
154
|
const promptsDir = baseDir ?? path.join(__dirname, '..', '..', 'shared-prompts');
|
|
89
155
|
if (!fs.existsSync(promptsDir)) {
|
|
@@ -96,15 +162,15 @@ function loadAllPrompts(logger, baseDir) {
|
|
|
96
162
|
for (const file of promptFiles) {
|
|
97
163
|
try {
|
|
98
164
|
const filePath = path.join(promptsDir, file);
|
|
99
|
-
const prompt = loadPromptFile(filePath);
|
|
165
|
+
const prompt = loadPromptFile(filePath, 'built-in');
|
|
100
166
|
prompts.push(prompt);
|
|
101
|
-
logger.debug('Loaded prompt', { name: prompt.name, file });
|
|
167
|
+
logger.debug('Loaded built-in prompt', { name: prompt.name, file });
|
|
102
168
|
}
|
|
103
169
|
catch (error) {
|
|
104
170
|
logger.error(`Failed to load prompt file ${file}`, error);
|
|
105
171
|
}
|
|
106
172
|
}
|
|
107
|
-
logger.info('Loaded prompts from shared library', {
|
|
173
|
+
logger.info('Loaded built-in prompts from shared library', {
|
|
108
174
|
total: prompts.length,
|
|
109
175
|
promptsDir,
|
|
110
176
|
});
|
|
@@ -115,18 +181,71 @@ function loadAllPrompts(logger, baseDir) {
|
|
|
115
181
|
return [];
|
|
116
182
|
}
|
|
117
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* Merge built-in and user prompts with collision detection
|
|
186
|
+
* Built-in prompts take precedence over user prompts with the same name
|
|
187
|
+
*/
|
|
188
|
+
function mergePrompts(builtInPrompts, userPrompts, logger) {
|
|
189
|
+
const builtInNames = new Set(builtInPrompts.map(p => p.name));
|
|
190
|
+
const merged = [...builtInPrompts];
|
|
191
|
+
for (const userPrompt of userPrompts) {
|
|
192
|
+
if (builtInNames.has(userPrompt.name)) {
|
|
193
|
+
logger.warn('User prompt name collision with built-in prompt, skipping user prompt', {
|
|
194
|
+
name: userPrompt.name,
|
|
195
|
+
message: 'Built-in prompt takes precedence',
|
|
196
|
+
});
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
merged.push(userPrompt);
|
|
200
|
+
}
|
|
201
|
+
return merged;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Loads all prompts (built-in + user) with collision detection
|
|
205
|
+
* This is the main entry point for loading prompts
|
|
206
|
+
*/
|
|
207
|
+
async function loadAllPrompts(logger, baseDir, forceRefresh = false) {
|
|
208
|
+
// Load built-in prompts (synchronous)
|
|
209
|
+
const builtInPrompts = loadBuiltInPrompts(logger, baseDir);
|
|
210
|
+
// Load user prompts from git repository (async, graceful failure)
|
|
211
|
+
let userPrompts = [];
|
|
212
|
+
try {
|
|
213
|
+
const { loadUserPrompts } = await Promise.resolve().then(() => __importStar(require('../core/user-prompts-loader.js')));
|
|
214
|
+
userPrompts = await loadUserPrompts(logger, forceRefresh);
|
|
215
|
+
}
|
|
216
|
+
catch (error) {
|
|
217
|
+
logger.debug('User prompts loader not available or failed', {
|
|
218
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
// Merge with collision detection
|
|
222
|
+
const allPrompts = mergePrompts(builtInPrompts, userPrompts, logger);
|
|
223
|
+
logger.info('Loaded all prompts', {
|
|
224
|
+
builtIn: builtInPrompts.length,
|
|
225
|
+
user: userPrompts.length,
|
|
226
|
+
total: allPrompts.length,
|
|
227
|
+
collisions: builtInPrompts.length + userPrompts.length - allPrompts.length,
|
|
228
|
+
});
|
|
229
|
+
return allPrompts;
|
|
230
|
+
}
|
|
118
231
|
/**
|
|
119
232
|
* Handle prompts/list MCP request
|
|
120
233
|
*/
|
|
121
234
|
async function handlePromptsListRequest(args, logger, requestId) {
|
|
122
235
|
try {
|
|
123
236
|
logger.info('Processing prompts/list request', { requestId });
|
|
124
|
-
const prompts = loadAllPrompts(logger, process.env.NODE_ENV === 'test' ? args?.baseDir : undefined);
|
|
125
|
-
// Convert to MCP prompts/list response format
|
|
126
|
-
const promptList = prompts.map(prompt =>
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
237
|
+
const prompts = await loadAllPrompts(logger, process.env.NODE_ENV === 'test' ? args?.baseDir : undefined);
|
|
238
|
+
// Convert to MCP prompts/list response format (include arguments if present)
|
|
239
|
+
const promptList = prompts.map(prompt => {
|
|
240
|
+
const item = {
|
|
241
|
+
name: prompt.name,
|
|
242
|
+
description: prompt.description,
|
|
243
|
+
};
|
|
244
|
+
if (prompt.arguments && prompt.arguments.length > 0) {
|
|
245
|
+
item.arguments = prompt.arguments;
|
|
246
|
+
}
|
|
247
|
+
return item;
|
|
248
|
+
});
|
|
130
249
|
logger.info('Prompts list generated', {
|
|
131
250
|
requestId,
|
|
132
251
|
promptCount: promptList.length,
|
|
@@ -157,7 +276,7 @@ async function handlePromptsGetRequest(args, logger, requestId) {
|
|
|
157
276
|
if (!args.name) {
|
|
158
277
|
throw new Error('Missing required parameter: name');
|
|
159
278
|
}
|
|
160
|
-
const prompts = loadAllPrompts(logger, process.env.NODE_ENV === 'test' ? args?.baseDir : undefined);
|
|
279
|
+
const prompts = await loadAllPrompts(logger, process.env.NODE_ENV === 'test' ? args?.baseDir : undefined);
|
|
161
280
|
const prompt = prompts.find(p => p.name === args.name);
|
|
162
281
|
if (!prompt) {
|
|
163
282
|
throw error_handling_1.ErrorHandler.createError(error_handling_1.ErrorCategory.VALIDATION, error_handling_1.ErrorSeverity.MEDIUM, `Prompt not found: ${args.name}`, {
|
|
@@ -166,9 +285,30 @@ async function handlePromptsGetRequest(args, logger, requestId) {
|
|
|
166
285
|
requestId,
|
|
167
286
|
});
|
|
168
287
|
}
|
|
288
|
+
// Validate required arguments if prompt has arguments defined
|
|
289
|
+
const providedArgs = args.arguments || {};
|
|
290
|
+
if (prompt.arguments && prompt.arguments.length > 0) {
|
|
291
|
+
const missingRequired = prompt.arguments
|
|
292
|
+
.filter(arg => arg.required && !providedArgs[arg.name])
|
|
293
|
+
.map(arg => arg.name);
|
|
294
|
+
if (missingRequired.length > 0) {
|
|
295
|
+
throw error_handling_1.ErrorHandler.createError(error_handling_1.ErrorCategory.VALIDATION, error_handling_1.ErrorSeverity.MEDIUM, `Missing required arguments: ${missingRequired.join(', ')}`, {
|
|
296
|
+
operation: 'prompts_get',
|
|
297
|
+
component: 'PromptsHandler',
|
|
298
|
+
requestId,
|
|
299
|
+
input: { promptName: prompt.name, missingArguments: missingRequired },
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
// Substitute {{argumentName}} placeholders in content
|
|
304
|
+
let processedContent = prompt.content;
|
|
305
|
+
for (const [argName, argValue] of Object.entries(providedArgs)) {
|
|
306
|
+
processedContent = processedContent.replaceAll(`{{${argName}}}`, String(argValue));
|
|
307
|
+
}
|
|
169
308
|
logger.info('Prompt found and returned', {
|
|
170
309
|
requestId,
|
|
171
310
|
promptName: prompt.name,
|
|
311
|
+
argumentsProvided: Object.keys(providedArgs).length,
|
|
172
312
|
});
|
|
173
313
|
// Convert to MCP prompts/get response format
|
|
174
314
|
return {
|
|
@@ -178,7 +318,7 @@ async function handlePromptsGetRequest(args, logger, requestId) {
|
|
|
178
318
|
role: 'user',
|
|
179
319
|
content: {
|
|
180
320
|
type: 'text',
|
|
181
|
-
text:
|
|
321
|
+
text: processedContent,
|
|
182
322
|
},
|
|
183
323
|
},
|
|
184
324
|
],
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Query Tool - Natural Language Cluster Intelligence
|
|
3
|
+
*
|
|
4
|
+
* Provides natural language query interface to discover and understand
|
|
5
|
+
* cluster capabilities and resources.
|
|
6
|
+
*
|
|
7
|
+
* PRD #291: Cluster Query Tool - Natural Language Cluster Intelligence
|
|
8
|
+
*/
|
|
9
|
+
import { z } from 'zod';
|
|
10
|
+
export declare const QUERY_TOOL_NAME = "query";
|
|
11
|
+
export declare const QUERY_TOOL_DESCRIPTION = "Natural language query interface for Kubernetes cluster intelligence. Ask any questions about your cluster resources, capabilities, and status in plain English. Examples: \"What databases are running?\", \"Describe the nginx deployment\", \"Show me pods in the kube-system namespace\", \"What operators are installed?\", \"Is my-postgres healthy?\"";
|
|
12
|
+
export declare const QUERY_TOOL_INPUT_SCHEMA: {
|
|
13
|
+
intent: z.ZodString;
|
|
14
|
+
interaction_id: z.ZodOptional<z.ZodString>;
|
|
15
|
+
};
|
|
16
|
+
export interface QueryInput {
|
|
17
|
+
intent: string;
|
|
18
|
+
interaction_id?: string;
|
|
19
|
+
}
|
|
20
|
+
export interface QueryOutput {
|
|
21
|
+
success: boolean;
|
|
22
|
+
summary: string;
|
|
23
|
+
toolsUsed: string[];
|
|
24
|
+
iterations: number;
|
|
25
|
+
error?: {
|
|
26
|
+
code: string;
|
|
27
|
+
message: string;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Main query tool handler
|
|
32
|
+
*/
|
|
33
|
+
export declare function handleQueryTool(args: any): Promise<any>;
|
|
34
|
+
//# sourceMappingURL=query.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../src/tools/query.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAkBxB,eAAO,MAAM,eAAe,UAAU,CAAC;AACvC,eAAO,MAAM,sBAAsB,iWAAuV,CAAC;AAG3X,eAAO,MAAM,uBAAuB;;;CAGnC,CAAC;AAGF,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAGD,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAgED;;GAEG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAiH7D"}
|