@provartesting/provardx-cli 1.5.0-beta.13 → 1.5.0-beta.15
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/commands/provar/mcp/start.d.ts +2 -0
- package/lib/commands/provar/mcp/start.js +14 -1
- package/lib/commands/provar/mcp/start.js.map +1 -1
- package/lib/mcp/docs/PROVAR_TEST_STEP_REFERENCE.md +2 -2
- package/lib/mcp/prompts/loopPrompts.js +18 -18
- package/lib/mcp/prompts/migrationPrompts.js +10 -10
- package/lib/mcp/server.d.ts +5 -0
- package/lib/mcp/server.js +14 -3
- package/lib/mcp/server.js.map +1 -1
- package/lib/mcp/tools/antTools.js +140 -129
- package/lib/mcp/tools/antTools.js.map +1 -1
- package/lib/mcp/tools/automationTools.js +125 -103
- package/lib/mcp/tools/automationTools.js.map +1 -1
- package/lib/mcp/tools/connectionTools.js +17 -13
- package/lib/mcp/tools/connectionTools.js.map +1 -1
- package/lib/mcp/tools/defectTools.js +35 -28
- package/lib/mcp/tools/defectTools.js.map +1 -1
- package/lib/mcp/tools/hierarchyValidate.d.ts +1 -1
- package/lib/mcp/tools/hierarchyValidate.js +127 -42
- package/lib/mcp/tools/hierarchyValidate.js.map +1 -1
- package/lib/mcp/tools/nitroXTools.js +161 -142
- package/lib/mcp/tools/nitroXTools.js.map +1 -1
- package/lib/mcp/tools/pageObjectGenerate.js +47 -40
- package/lib/mcp/tools/pageObjectGenerate.js.map +1 -1
- package/lib/mcp/tools/pageObjectValidate.js +136 -46
- package/lib/mcp/tools/pageObjectValidate.js.map +1 -1
- package/lib/mcp/tools/projectInspect.js +51 -30
- package/lib/mcp/tools/projectInspect.js.map +1 -1
- package/lib/mcp/tools/projectValidateFromPath.js +70 -49
- package/lib/mcp/tools/projectValidateFromPath.js.map +1 -1
- package/lib/mcp/tools/propertiesTools.js +64 -48
- package/lib/mcp/tools/propertiesTools.js.map +1 -1
- package/lib/mcp/tools/qualityHubApiTools.js +48 -44
- package/lib/mcp/tools/qualityHubApiTools.js.map +1 -1
- package/lib/mcp/tools/qualityHubTools.js +76 -52
- package/lib/mcp/tools/qualityHubTools.js.map +1 -1
- package/lib/mcp/tools/rcaTools.js +63 -55
- package/lib/mcp/tools/rcaTools.js.map +1 -1
- package/lib/mcp/tools/testCaseGenerate.js +46 -35
- package/lib/mcp/tools/testCaseGenerate.js.map +1 -1
- package/lib/mcp/tools/testCaseStepTools.js +38 -34
- package/lib/mcp/tools/testCaseStepTools.js.map +1 -1
- package/lib/mcp/tools/testCaseValidate.js +60 -41
- package/lib/mcp/tools/testCaseValidate.js.map +1 -1
- package/lib/mcp/tools/testPlanTools.js +198 -76
- package/lib/mcp/tools/testPlanTools.js.map +1 -1
- package/lib/mcp/tools/testPlanValidate.js +56 -18
- package/lib/mcp/tools/testPlanValidate.js.map +1 -1
- package/lib/mcp/tools/testSuiteValidate.js +37 -11
- package/lib/mcp/tools/testSuiteValidate.js.map +1 -1
- package/lib/mcp/update/updateChecker.d.ts +14 -0
- package/lib/mcp/update/updateChecker.js +228 -0
- package/lib/mcp/update/updateChecker.js.map +1 -0
- package/lib/services/projectValidation.d.ts +5 -2
- package/lib/services/projectValidation.js +83 -31
- package/lib/services/projectValidation.js.map +1 -1
- package/messages/sf.provar.auth.clear.md +1 -1
- package/messages/sf.provar.mcp.start.md +83 -48
- package/oclif.manifest.json +224 -212
- package/package.json +1 -1
|
@@ -90,16 +90,20 @@ function validateRootProperties(obj, issues) {
|
|
|
90
90
|
// NX001: componentId must be present and a valid UUID
|
|
91
91
|
if (obj['componentId'] === undefined || obj['componentId'] === null) {
|
|
92
92
|
issues.push({
|
|
93
|
-
rule_id: 'NX001',
|
|
93
|
+
rule_id: 'NX001',
|
|
94
|
+
severity: 'ERROR',
|
|
94
95
|
message: 'componentId is required.',
|
|
95
|
-
applies_to: 'root',
|
|
96
|
+
applies_to: 'root',
|
|
97
|
+
field: 'componentId',
|
|
96
98
|
});
|
|
97
99
|
}
|
|
98
100
|
else if (typeof obj['componentId'] !== 'string' || !UUID_RE.test(obj['componentId'])) {
|
|
99
101
|
issues.push({
|
|
100
|
-
rule_id: 'NX001',
|
|
102
|
+
rule_id: 'NX001',
|
|
103
|
+
severity: 'ERROR',
|
|
101
104
|
message: `componentId must be a valid UUID, got: "${String(obj['componentId'])}".`,
|
|
102
|
-
applies_to: 'root',
|
|
105
|
+
applies_to: 'root',
|
|
106
|
+
field: 'componentId',
|
|
103
107
|
});
|
|
104
108
|
}
|
|
105
109
|
// NX002: Root (no parentId) requires name, type, pageStructureElement, fieldDetailsElement
|
|
@@ -108,9 +112,11 @@ function validateRootProperties(obj, issues) {
|
|
|
108
112
|
for (const field of ['name', 'type', 'pageStructureElement', 'fieldDetailsElement']) {
|
|
109
113
|
if (obj[field] === undefined || obj[field] === null) {
|
|
110
114
|
issues.push({
|
|
111
|
-
rule_id: 'NX002',
|
|
115
|
+
rule_id: 'NX002',
|
|
116
|
+
severity: 'ERROR',
|
|
112
117
|
message: `Root component requires "${field}".`,
|
|
113
|
-
applies_to: 'root',
|
|
118
|
+
applies_to: 'root',
|
|
119
|
+
field,
|
|
114
120
|
suggestion: `Add a "${field}" property to the root component object.`,
|
|
115
121
|
});
|
|
116
122
|
}
|
|
@@ -119,18 +125,22 @@ function validateRootProperties(obj, issues) {
|
|
|
119
125
|
// NX003: tagName must not contain whitespace
|
|
120
126
|
if (typeof obj['tagName'] === 'string' && /\s/.test(obj['tagName'])) {
|
|
121
127
|
issues.push({
|
|
122
|
-
rule_id: 'NX003',
|
|
128
|
+
rule_id: 'NX003',
|
|
129
|
+
severity: 'ERROR',
|
|
123
130
|
message: 'tagName should not contain spaces.',
|
|
124
|
-
applies_to: 'root',
|
|
131
|
+
applies_to: 'root',
|
|
132
|
+
field: 'tagName',
|
|
125
133
|
suggestion: 'Remove whitespace from tagName.',
|
|
126
134
|
});
|
|
127
135
|
}
|
|
128
136
|
// NX010: bodyTagName (if present) must not contain whitespace
|
|
129
137
|
if (typeof obj['bodyTagName'] === 'string' && /\s/.test(obj['bodyTagName'])) {
|
|
130
138
|
issues.push({
|
|
131
|
-
rule_id: 'NX010',
|
|
139
|
+
rule_id: 'NX010',
|
|
140
|
+
severity: 'INFO',
|
|
132
141
|
message: 'bodyTagName should not contain spaces.',
|
|
133
|
-
applies_to: 'root',
|
|
142
|
+
applies_to: 'root',
|
|
143
|
+
field: 'bodyTagName',
|
|
134
144
|
suggestion: 'Remove whitespace from bodyTagName.',
|
|
135
145
|
});
|
|
136
146
|
}
|
|
@@ -177,7 +187,8 @@ function validateElement(el, issues) {
|
|
|
177
187
|
// NX007: Element should have type
|
|
178
188
|
if (!el['type']) {
|
|
179
189
|
issues.push({
|
|
180
|
-
rule_id: 'NX007',
|
|
190
|
+
rule_id: 'NX007',
|
|
191
|
+
severity: 'WARNING',
|
|
181
192
|
message: 'Element is missing required "type".',
|
|
182
193
|
applies_to: 'element',
|
|
183
194
|
suggestion: 'Add a "type" field to the element (e.g. "content" or "component::UUID").',
|
|
@@ -213,17 +224,21 @@ function validateInteraction(interaction, context, issues) {
|
|
|
213
224
|
for (const field of ['defaultInteraction', 'interactionType', 'name', 'testStepTitlePattern', 'title']) {
|
|
214
225
|
if (interaction[field] === undefined || interaction[field] === null) {
|
|
215
226
|
issues.push({
|
|
216
|
-
rule_id: 'NX004',
|
|
227
|
+
rule_id: 'NX004',
|
|
228
|
+
severity: 'ERROR',
|
|
217
229
|
message: `Interaction in ${context} missing required field "${field}".`,
|
|
218
|
-
applies_to: 'interaction',
|
|
230
|
+
applies_to: 'interaction',
|
|
231
|
+
field,
|
|
219
232
|
});
|
|
220
233
|
}
|
|
221
234
|
}
|
|
222
235
|
if (!Array.isArray(interaction['implementations']) || interaction['implementations'].length === 0) {
|
|
223
236
|
issues.push({
|
|
224
|
-
rule_id: 'NX004',
|
|
237
|
+
rule_id: 'NX004',
|
|
238
|
+
severity: 'ERROR',
|
|
225
239
|
message: `Interaction in ${context} must have at least one implementation.`,
|
|
226
|
-
applies_to: 'interaction',
|
|
240
|
+
applies_to: 'interaction',
|
|
241
|
+
field: 'implementations',
|
|
227
242
|
});
|
|
228
243
|
}
|
|
229
244
|
else {
|
|
@@ -235,9 +250,11 @@ function validateInteraction(interaction, context, issues) {
|
|
|
235
250
|
// NX009: name should match ^[A-Za-z0-9\s]*$
|
|
236
251
|
if (typeof interaction['name'] === 'string' && !INTERACTION_NAME_RE.test(interaction['name'])) {
|
|
237
252
|
issues.push({
|
|
238
|
-
rule_id: 'NX009',
|
|
253
|
+
rule_id: 'NX009',
|
|
254
|
+
severity: 'INFO',
|
|
239
255
|
message: `Interaction name "${interaction['name']}" should contain only alphanumeric characters and spaces.`,
|
|
240
|
-
applies_to: 'interaction',
|
|
256
|
+
applies_to: 'interaction',
|
|
257
|
+
field: 'name',
|
|
241
258
|
suggestion: 'Remove special characters from the interaction name.',
|
|
242
259
|
});
|
|
243
260
|
}
|
|
@@ -246,9 +263,11 @@ function validateImplementation(impl, context, issues) {
|
|
|
246
263
|
// NX005: must have javaScriptSnippet
|
|
247
264
|
if (!impl['javaScriptSnippet']) {
|
|
248
265
|
issues.push({
|
|
249
|
-
rule_id: 'NX005',
|
|
266
|
+
rule_id: 'NX005',
|
|
267
|
+
severity: 'ERROR',
|
|
250
268
|
message: `Implementation in ${context} missing required "javaScriptSnippet".`,
|
|
251
|
-
applies_to: 'implementation',
|
|
269
|
+
applies_to: 'implementation',
|
|
270
|
+
field: 'javaScriptSnippet',
|
|
252
271
|
});
|
|
253
272
|
}
|
|
254
273
|
}
|
|
@@ -256,9 +275,11 @@ function validateSelector(sel, issues) {
|
|
|
256
275
|
// NX006: must have xpath
|
|
257
276
|
if (!sel['xpath']) {
|
|
258
277
|
issues.push({
|
|
259
|
-
rule_id: 'NX006',
|
|
278
|
+
rule_id: 'NX006',
|
|
279
|
+
severity: 'ERROR',
|
|
260
280
|
message: 'Selector missing required "xpath".',
|
|
261
|
-
applies_to: 'selector',
|
|
281
|
+
applies_to: 'selector',
|
|
282
|
+
field: 'xpath',
|
|
262
283
|
suggestion: 'Add an "xpath" property to the selector.',
|
|
263
284
|
});
|
|
264
285
|
}
|
|
@@ -267,9 +288,11 @@ function validateParameter(param, context, issues) {
|
|
|
267
288
|
// NX008: comparisonType must be one of valid enum values
|
|
268
289
|
if (param['comparisonType'] !== undefined && !VALID_COMPARISON_TYPES.includes(String(param['comparisonType']))) {
|
|
269
290
|
issues.push({
|
|
270
|
-
rule_id: 'NX008',
|
|
291
|
+
rule_id: 'NX008',
|
|
292
|
+
severity: 'WARNING',
|
|
271
293
|
message: `Parameter in ${context} has invalid comparisonType "${String(param['comparisonType'])}". Must be one of: ${VALID_COMPARISON_TYPES.join(', ')}.`,
|
|
272
|
-
applies_to: 'parameter',
|
|
294
|
+
applies_to: 'parameter',
|
|
295
|
+
field: 'comparisonType',
|
|
273
296
|
suggestion: `Use one of: ${VALID_COMPARISON_TYPES.join(', ')}`,
|
|
274
297
|
});
|
|
275
298
|
}
|
|
@@ -338,40 +361,41 @@ function applyMergePatch(target, patch) {
|
|
|
338
361
|
}
|
|
339
362
|
// ── Tool Registrations ────────────────────────────────────────────────────────
|
|
340
363
|
export function registerNitroXDiscover(server) {
|
|
341
|
-
server.
|
|
342
|
-
'Discover
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
364
|
+
server.registerTool('provar_nitrox_discover', {
|
|
365
|
+
title: 'Discover NitroX Components',
|
|
366
|
+
description: [
|
|
367
|
+
'Discover Provar projects containing NitroX (Hybrid Model) page objects.',
|
|
368
|
+
'Scans directories for .testproject marker files, then inventories nitroX/ and nitroXPackages/ directories.',
|
|
369
|
+
"NitroX is Provar's Hybrid Model for locators — component-based page objects for LWC,",
|
|
370
|
+
'Screen Flow, Industry Components, Experience Cloud, and HTML5 components.',
|
|
371
|
+
'Results provide file paths and package info for use with provar_nitrox_read, validate, and generate.',
|
|
372
|
+
].join(' '),
|
|
373
|
+
inputSchema: {
|
|
374
|
+
search_roots: z
|
|
375
|
+
.array(z.string())
|
|
376
|
+
.optional()
|
|
377
|
+
.describe('Directories to scan (default: cwd; if empty, falls back to ~/git and ~/Provar)'),
|
|
378
|
+
max_depth: z
|
|
379
|
+
.number()
|
|
380
|
+
.int()
|
|
381
|
+
.min(1)
|
|
382
|
+
.max(20)
|
|
383
|
+
.default(6)
|
|
384
|
+
.describe('Maximum directory depth for .testproject search'),
|
|
385
|
+
include_packages: z
|
|
386
|
+
.boolean()
|
|
387
|
+
.default(true)
|
|
388
|
+
.describe('Include nitroXPackages/ package.json metadata in results'),
|
|
389
|
+
},
|
|
363
390
|
}, ({ search_roots, max_depth, include_packages }) => {
|
|
364
391
|
const requestId = makeRequestId();
|
|
365
|
-
log('info', '
|
|
392
|
+
log('info', 'provar_nitrox_discover', { requestId, search_roots, max_depth });
|
|
366
393
|
try {
|
|
367
394
|
let roots = search_roots && search_roots.length > 0 ? search_roots : [process.cwd()];
|
|
368
395
|
let projects = findProvarProjects(roots, max_depth);
|
|
369
396
|
// If no .testproject found in cwd, widen to home-dir defaults
|
|
370
397
|
if (projects.length === 0 && (!search_roots || search_roots.length === 0)) {
|
|
371
|
-
const fallbackRoots = [
|
|
372
|
-
path.join(os.homedir(), 'git'),
|
|
373
|
-
path.join(os.homedir(), 'Provar'),
|
|
374
|
-
];
|
|
398
|
+
const fallbackRoots = [path.join(os.homedir(), 'git'), path.join(os.homedir(), 'Provar')];
|
|
375
399
|
const fallbackProjects = findProvarProjects(fallbackRoots, max_depth);
|
|
376
400
|
if (fallbackProjects.length > 0) {
|
|
377
401
|
projects = fallbackProjects;
|
|
@@ -426,32 +450,36 @@ export function registerNitroXDiscover(server) {
|
|
|
426
450
|
catch (err) {
|
|
427
451
|
const error = err;
|
|
428
452
|
const errResult = makeError('DISCOVER_ERROR', error.message, requestId, false);
|
|
429
|
-
log('error', '
|
|
453
|
+
log('error', 'provar_nitrox_discover failed', { requestId, error: error.message });
|
|
430
454
|
return { isError: true, content: [{ type: 'text', text: JSON.stringify(errResult) }] };
|
|
431
455
|
}
|
|
432
456
|
});
|
|
433
457
|
}
|
|
434
458
|
export function registerNitroXRead(server, config) {
|
|
435
|
-
server.
|
|
436
|
-
'Read
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
.optional()
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
459
|
+
server.registerTool('provar_nitrox_read', {
|
|
460
|
+
title: 'Read NitroX Files',
|
|
461
|
+
description: [
|
|
462
|
+
'Read one or more NitroX .po.json (Hybrid Model page object) files and return their parsed content.',
|
|
463
|
+
'Use this to load examples before generating or validating.',
|
|
464
|
+
"Provide file_paths for specific files, or project_path to read all .po.json files from a project's nitroX/ directory.",
|
|
465
|
+
].join(' '),
|
|
466
|
+
inputSchema: {
|
|
467
|
+
file_paths: z.array(z.string()).optional().describe('Specific .po.json file paths to read'),
|
|
468
|
+
project_path: z
|
|
469
|
+
.string()
|
|
470
|
+
.optional()
|
|
471
|
+
.describe('Provar project path — reads all .po.json files from nitroX/ directory'),
|
|
472
|
+
max_files: z
|
|
473
|
+
.number()
|
|
474
|
+
.int()
|
|
475
|
+
.min(1)
|
|
476
|
+
.max(100)
|
|
477
|
+
.default(20)
|
|
478
|
+
.describe('Maximum number of files to return (prevents context overflow)'),
|
|
479
|
+
},
|
|
452
480
|
}, ({ file_paths, project_path, max_files }) => {
|
|
453
481
|
const requestId = makeRequestId();
|
|
454
|
-
log('info', '
|
|
482
|
+
log('info', 'provar_nitrox_read', {
|
|
455
483
|
requestId,
|
|
456
484
|
file_count: file_paths?.length,
|
|
457
485
|
project_path,
|
|
@@ -511,23 +539,27 @@ export function registerNitroXRead(server, config) {
|
|
|
511
539
|
catch (err) {
|
|
512
540
|
const error = err;
|
|
513
541
|
const errResult = makeError(error instanceof PathPolicyError ? error.code : 'READ_ERROR', error.message, requestId, false);
|
|
514
|
-
log('error', '
|
|
542
|
+
log('error', 'provar_nitrox_read failed', { requestId, error: error.message });
|
|
515
543
|
return { isError: true, content: [{ type: 'text', text: JSON.stringify(errResult) }] };
|
|
516
544
|
}
|
|
517
545
|
});
|
|
518
546
|
}
|
|
519
547
|
export function registerNitroXValidate(server, config) {
|
|
520
|
-
server.
|
|
521
|
-
'Validate
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
548
|
+
server.registerTool('provar_nitrox_validate', {
|
|
549
|
+
title: 'Validate NitroX Component',
|
|
550
|
+
description: [
|
|
551
|
+
'Validate a NitroX .po.json (Hybrid Model component page object) against schema rules.',
|
|
552
|
+
'Works for any NitroX-mapped component type: LWC, Screen Flow, Industry Components, Experience Cloud, HTML5.',
|
|
553
|
+
'Returns a quality score (0–100) and a list of issues with rule IDs (NX001–NX010), severity, and suggestions.',
|
|
554
|
+
'Score formula: 100 − (20 × errors) − (5 × warnings) − (1 × infos).',
|
|
555
|
+
].join(' '),
|
|
556
|
+
inputSchema: {
|
|
557
|
+
content: z.string().optional().describe('JSON string of the .po.json content to validate'),
|
|
558
|
+
file_path: z.string().optional().describe('Path to a .po.json file to validate'),
|
|
559
|
+
},
|
|
528
560
|
}, ({ content, file_path }) => {
|
|
529
561
|
const requestId = makeRequestId();
|
|
530
|
-
log('info', '
|
|
562
|
+
log('info', 'provar_nitrox_validate', { requestId, has_content: !!content, file_path });
|
|
531
563
|
try {
|
|
532
564
|
let source = content;
|
|
533
565
|
if (!source && file_path) {
|
|
@@ -565,7 +597,7 @@ export function registerNitroXValidate(server, config) {
|
|
|
565
597
|
catch (err) {
|
|
566
598
|
const error = err;
|
|
567
599
|
const errResult = makeError(error instanceof PathPolicyError ? error.code : 'VALIDATE_ERROR', error.message, requestId, false);
|
|
568
|
-
log('error', '
|
|
600
|
+
log('error', 'provar_nitrox_validate failed', { requestId, error: error.message });
|
|
569
601
|
return { isError: true, content: [{ type: 'text', text: JSON.stringify(errResult) }] };
|
|
570
602
|
}
|
|
571
603
|
});
|
|
@@ -578,50 +610,36 @@ const ParameterInputSchema = z.object({
|
|
|
578
610
|
});
|
|
579
611
|
const ElementInputSchema = z.object({
|
|
580
612
|
label: z.string().describe('Human-readable element label'),
|
|
581
|
-
type_ref: z
|
|
582
|
-
.string()
|
|
583
|
-
.describe('Component type reference (e.g. "component::UUID" or "content")'),
|
|
613
|
+
type_ref: z.string().describe('Component type reference (e.g. "component::UUID" or "content")'),
|
|
584
614
|
tag_name: z.string().optional().describe('Optional HTML/LWC tag name override'),
|
|
585
615
|
parameters: z.array(ParameterInputSchema).optional(),
|
|
586
616
|
selector_xpath: z.string().optional().describe('XPath selector for this element'),
|
|
587
617
|
});
|
|
588
618
|
export function registerNitroXGenerate(server, config) {
|
|
589
|
-
server.
|
|
590
|
-
'Generate
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
.string()
|
|
601
|
-
.
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
.
|
|
605
|
-
.
|
|
606
|
-
.describe('
|
|
607
|
-
|
|
608
|
-
.boolean()
|
|
609
|
-
|
|
610
|
-
.describe('Whether this is a field details element'),
|
|
611
|
-
parameters: z.array(ParameterInputSchema).optional().describe('Component parameters/qualifiers'),
|
|
612
|
-
elements: z.array(ElementInputSchema).optional().describe('Child elements'),
|
|
613
|
-
output_path: z
|
|
614
|
-
.string()
|
|
615
|
-
.optional()
|
|
616
|
-
.describe('File path to write (requires dry_run=false)'),
|
|
617
|
-
overwrite: z.boolean().default(false).describe('Overwrite if output_path already exists'),
|
|
618
|
-
dry_run: z
|
|
619
|
-
.boolean()
|
|
620
|
-
.default(true)
|
|
621
|
-
.describe('Return JSON without writing to disk (default)'),
|
|
619
|
+
server.registerTool('provar_nitrox_generate', {
|
|
620
|
+
title: 'Generate NitroX Components',
|
|
621
|
+
description: [
|
|
622
|
+
'Generate a new NitroX .po.json (Hybrid Model page object) from a component description.',
|
|
623
|
+
"Applicable to any component type supported by Provar's Hybrid Model:",
|
|
624
|
+
'LWC, Screen Flow, Industry Components, Experience Cloud, HTML5.',
|
|
625
|
+
'All componentId fields are assigned fresh UUIDs. Returns JSON content;',
|
|
626
|
+
'writes to disk only when dry_run=false.',
|
|
627
|
+
].join(' '),
|
|
628
|
+
inputSchema: {
|
|
629
|
+
name: z.string().describe('Path-like component name, e.g. /com/force/myapp/ButtonComponent'),
|
|
630
|
+
tag_name: z.string().describe('LWC or HTML tag name, e.g. lightning-button or c-my-component'),
|
|
631
|
+
type: z.enum(['Block', 'Page']).default('Block').describe('Component type'),
|
|
632
|
+
page_structure_element: z.boolean().default(true).describe('Whether this is a page structure element'),
|
|
633
|
+
field_details_element: z.boolean().default(false).describe('Whether this is a field details element'),
|
|
634
|
+
parameters: z.array(ParameterInputSchema).optional().describe('Component parameters/qualifiers'),
|
|
635
|
+
elements: z.array(ElementInputSchema).optional().describe('Child elements'),
|
|
636
|
+
output_path: z.string().optional().describe('File path to write (requires dry_run=false)'),
|
|
637
|
+
overwrite: z.boolean().default(false).describe('Overwrite if output_path already exists'),
|
|
638
|
+
dry_run: z.boolean().default(true).describe('Return JSON without writing to disk (default)'),
|
|
639
|
+
},
|
|
622
640
|
}, (input) => {
|
|
623
641
|
const requestId = makeRequestId();
|
|
624
|
-
log('info', '
|
|
642
|
+
log('info', 'provar_nitrox_generate', { requestId, name: input.name, dry_run: input.dry_run });
|
|
625
643
|
try {
|
|
626
644
|
const generated = buildNitroXJson({
|
|
627
645
|
name: input.name,
|
|
@@ -645,7 +663,7 @@ export function registerNitroXGenerate(server, config) {
|
|
|
645
663
|
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
646
664
|
fs.writeFileSync(filePath, content, 'utf-8');
|
|
647
665
|
written = true;
|
|
648
|
-
log('info', '
|
|
666
|
+
log('info', 'provar_nitrox_generate: wrote file', { requestId, filePath });
|
|
649
667
|
}
|
|
650
668
|
const result = { requestId, content, file_path: filePath, written, dry_run: input.dry_run };
|
|
651
669
|
return {
|
|
@@ -655,34 +673,35 @@ export function registerNitroXGenerate(server, config) {
|
|
|
655
673
|
}
|
|
656
674
|
catch (err) {
|
|
657
675
|
const error = err;
|
|
658
|
-
const errResult = makeError(error instanceof PathPolicyError ? error.code :
|
|
659
|
-
log('error', '
|
|
676
|
+
const errResult = makeError(error instanceof PathPolicyError ? error.code : error.code ?? 'GENERATE_ERROR', error.message, requestId, false);
|
|
677
|
+
log('error', 'provar_nitrox_generate failed', { requestId, error: error.message });
|
|
660
678
|
return { isError: true, content: [{ type: 'text', text: JSON.stringify(errResult) }] };
|
|
661
679
|
}
|
|
662
680
|
});
|
|
663
681
|
}
|
|
664
682
|
export function registerNitroXPatch(server, config) {
|
|
665
|
-
server.
|
|
666
|
-
'
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
.describe('
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
.describe('Return merged result without writing to disk (default)'),
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
+
server.registerTool('provar_nitrox_patch', {
|
|
684
|
+
title: 'Patch NitroX Component',
|
|
685
|
+
description: [
|
|
686
|
+
'Apply a JSON merge-patch (RFC 7396) to an existing NitroX .po.json file.',
|
|
687
|
+
'Reads the file, merges the patch (null values remove keys, other values replace or recurse into objects),',
|
|
688
|
+
'optionally validates the merged result, and writes back.',
|
|
689
|
+
'Use dry_run=true (default) to preview the merged output without writing.',
|
|
690
|
+
].join(' '),
|
|
691
|
+
inputSchema: {
|
|
692
|
+
file_path: z.string().describe('Path to the existing .po.json file to patch'),
|
|
693
|
+
patch: z
|
|
694
|
+
.record(z.unknown())
|
|
695
|
+
.describe('JSON merge-patch to apply (RFC 7396: null removes key, any other value replaces)'),
|
|
696
|
+
dry_run: z.boolean().default(true).describe('Return merged result without writing to disk (default)'),
|
|
697
|
+
validate_after: z
|
|
698
|
+
.boolean()
|
|
699
|
+
.default(true)
|
|
700
|
+
.describe('Run NX validation on merged result; blocks write if errors found'),
|
|
701
|
+
},
|
|
683
702
|
}, ({ file_path, patch, dry_run, validate_after }) => {
|
|
684
703
|
const requestId = makeRequestId();
|
|
685
|
-
log('info', '
|
|
704
|
+
log('info', 'provar_nitrox_patch', { requestId, file_path, dry_run });
|
|
686
705
|
try {
|
|
687
706
|
assertPathAllowed(file_path, config.allowedPaths);
|
|
688
707
|
const resolved = path.resolve(file_path);
|
|
@@ -717,7 +736,7 @@ export function registerNitroXPatch(server, config) {
|
|
|
717
736
|
if (!dry_run) {
|
|
718
737
|
fs.writeFileSync(resolved, content, 'utf-8');
|
|
719
738
|
written = true;
|
|
720
|
-
log('info', '
|
|
739
|
+
log('info', 'provar_nitrox_patch: wrote file', { requestId, filePath: resolved });
|
|
721
740
|
}
|
|
722
741
|
const result = {
|
|
723
742
|
requestId,
|
|
@@ -734,8 +753,8 @@ export function registerNitroXPatch(server, config) {
|
|
|
734
753
|
}
|
|
735
754
|
catch (err) {
|
|
736
755
|
const error = err;
|
|
737
|
-
const errResult = makeError(error instanceof PathPolicyError ? error.code :
|
|
738
|
-
log('error', '
|
|
756
|
+
const errResult = makeError(error instanceof PathPolicyError ? error.code : error.code ?? 'PATCH_ERROR', error.message, requestId, false);
|
|
757
|
+
log('error', 'provar_nitrox_patch failed', { requestId, error: error.message });
|
|
739
758
|
return { isError: true, content: [{ type: 'text', text: JSON.stringify(errResult) }] };
|
|
740
759
|
}
|
|
741
760
|
});
|