@qikdev/mcp 6.7.2 → 6.7.6

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.
Files changed (45) hide show
  1. package/build/src/tools/content.d.ts +29 -0
  2. package/build/src/tools/content.d.ts.map +1 -0
  3. package/build/src/tools/content.js +153 -0
  4. package/build/src/tools/content.js.map +1 -0
  5. package/build/src/tools/create.d.ts.map +1 -1
  6. package/build/src/tools/create.js +48 -634
  7. package/build/src/tools/create.js.map +1 -1
  8. package/build/src/tools/duplicates.d.ts +27 -0
  9. package/build/src/tools/duplicates.d.ts.map +1 -0
  10. package/build/src/tools/duplicates.js +142 -0
  11. package/build/src/tools/duplicates.js.map +1 -0
  12. package/build/src/tools/index.d.ts.map +1 -1
  13. package/build/src/tools/index.js +66 -0
  14. package/build/src/tools/index.js.map +1 -1
  15. package/build/src/tools/list.d.ts.map +1 -1
  16. package/build/src/tools/list.js +79 -323
  17. package/build/src/tools/list.js.map +1 -1
  18. package/build/src/tools/merge.d.ts +34 -0
  19. package/build/src/tools/merge.d.ts.map +1 -0
  20. package/build/src/tools/merge.js +192 -0
  21. package/build/src/tools/merge.js.map +1 -0
  22. package/build/src/tools/profile-access.d.ts +47 -0
  23. package/build/src/tools/profile-access.d.ts.map +1 -0
  24. package/build/src/tools/profile-access.js +264 -0
  25. package/build/src/tools/profile-access.js.map +1 -0
  26. package/build/src/tools/profile-relationships.d.ts +43 -0
  27. package/build/src/tools/profile-relationships.d.ts.map +1 -0
  28. package/build/src/tools/profile-relationships.js +270 -0
  29. package/build/src/tools/profile-relationships.js.map +1 -0
  30. package/build/src/tools/scope.d.ts +47 -0
  31. package/build/src/tools/scope.d.ts.map +1 -0
  32. package/build/src/tools/scope.js +263 -0
  33. package/build/src/tools/scope.js.map +1 -0
  34. package/build/src/tools/search.d.ts +19 -0
  35. package/build/src/tools/search.d.ts.map +1 -0
  36. package/build/src/tools/search.js +84 -0
  37. package/build/src/tools/search.js.map +1 -0
  38. package/build/src/tools/smartlist.d.ts +26 -0
  39. package/build/src/tools/smartlist.d.ts.map +1 -0
  40. package/build/src/tools/smartlist.js +126 -0
  41. package/build/src/tools/smartlist.js.map +1 -0
  42. package/build/src/tools/update.d.ts.map +1 -1
  43. package/build/src/tools/update.js +44 -345
  44. package/build/src/tools/update.js.map +1 -1
  45. package/package.json +2 -2
@@ -1,23 +1,47 @@
1
1
  import { ConfigManager } from "../config.js";
2
- import { getUserSessionData, getAvailableScopes, getScopeTitle } from "./user.js";
3
- import { handleListContent } from "./list.js";
4
2
  export const createContentTool = {
5
3
  name: "create_content",
6
- description: "✨ Create new content items using Qik content types with guided field collection",
4
+ description: `Create new content items in Qik.
5
+
6
+ **Endpoint:** POST /content/:typeKey/create
7
+
8
+ **Required:**
9
+ - typeKey: The content type key (e.g., 'article', 'profile', 'event')
10
+
11
+ All other properties are passed directly to the API as the create payload.
12
+
13
+ **Tags Shorthand:**
14
+ Pass \`tags: ['green', 'red']\` as string names and the backend auto-creates tags.
15
+
16
+ **Scope:**
17
+ Set \`meta.scopes: ['scopeId']\` to specify which scope(s) the content belongs to.
18
+
19
+ **Example:**
20
+ \`\`\`json
21
+ {
22
+ "typeKey": "article",
23
+ "title": "My Article",
24
+ "tags": ["featured", "news"],
25
+ "meta": {
26
+ "scopes": ["scopeId123"]
27
+ }
28
+ }
29
+ \`\`\``,
7
30
  inputSchema: {
8
31
  type: "object",
9
32
  properties: {
10
33
  typeKey: {
11
34
  type: "string",
12
- description: "The content type key to create (e.g., 'article', 'incidentReportComment') OR a natural language description (e.g., 'incident report', 'blog post')"
35
+ description: "The content type key (e.g., 'article', 'profile')"
13
36
  },
14
- scope: {
15
- type: "string",
16
- description: "The scope ID to create the content in. If not provided and multiple scopes are available, you'll be prompted to select one."
37
+ tags: {
38
+ type: "array",
39
+ items: { type: "string" },
40
+ description: "Tag names - backend auto-creates tags and attaches IDs"
17
41
  }
18
42
  },
19
43
  required: ["typeKey"],
20
- additionalProperties: true // Allow any additional properties for field values
44
+ additionalProperties: true
21
45
  },
22
46
  };
23
47
  export async function handleCreateContent(args) {
@@ -27,408 +51,27 @@ export async function handleCreateContent(args) {
27
51
  if (!config) {
28
52
  throw new Error('Qik MCP server not configured. Run setup first.');
29
53
  }
30
- // Extract the natural language description
31
- const naturalLanguage = args.typeKey;
32
- if (!naturalLanguage || typeof naturalLanguage !== 'string') {
33
- return createErrorResponse('Please provide a description of what you want to create (e.g., "article", "incident report")');
34
- }
35
- // STEP 1: Translate natural language to content type
36
- // Check if this is a UI/interface creation request first
37
- const uiKeywords = ['website', 'web app', 'webapp', 'interface', 'ui', 'user interface', 'site', 'portal', 'blog', 'app'];
38
- const isUIRequest = uiKeywords.some(keyword => naturalLanguage.toLowerCase().includes(keyword));
39
- if (isUIRequest) {
40
- // Extract user arguments (everything except typeKey)
41
- const { typeKey, ...userArgs } = args;
42
- return await createUICreationSuggestion(naturalLanguage, userArgs);
43
- }
44
- const contentType = await findContentTypeFromNaturalLanguage(config, naturalLanguage);
45
- if (!contentType) {
46
- return createErrorResponse(`I couldn't find a content type matching "${naturalLanguage}". Please check available types using get_glossary.`);
47
- }
48
- // STEP 2: Get user session to check permissions and scopes
49
- const userSession = await getUserSessionData();
50
- const createPermission = `${contentType.key}.create`;
51
- const createParentTypePermission = contentType.definesType ? `${contentType.definesType}.create` : '---';
52
- const availableScopesForDefinition = getAvailableScopes(userSession, createPermission);
53
- const availableScopesForParentType = getAvailableScopes(userSession, createParentTypePermission);
54
- const availableScopes = [
55
- ...availableScopesForDefinition,
56
- ...availableScopesForParentType
57
- ];
58
- if (availableScopes.length === 0) {
59
- return createErrorResponse(`You don't have permission to create ${contentType.title} content. Required permission: ${createPermission}`);
60
- }
61
- // STEP 3: Check if we have the create example payload
62
- if (!contentType.examples || !contentType.examples.create) {
63
- return createErrorResponse(`No create example found for ${contentType.title}. Cannot proceed without API example.`);
64
- }
65
- // STEP 4: Extract field values from user input (everything except typeKey, scope, scopeId)
66
- const { typeKey, scope, scopeId, ...userFieldValues } = args;
67
- // STEP 5: Handle scope selection
68
- const selectedScopeId = scope || scopeId;
69
- // If no scope provided and multiple scopes available, ask user to select
70
- if (!selectedScopeId && availableScopes.length > 1) {
71
- return createScopeSelectionPrompt(contentType.title, availableScopes, userSession, naturalLanguage);
72
- }
73
- // Use selected scope or default to first available
74
- const targetScopeId = selectedScopeId || availableScopes[0];
75
- // Validate the selected scope is actually available
76
- if (!availableScopes.includes(targetScopeId)) {
77
- return createErrorResponse(`Invalid scope "${targetScopeId}". Available scopes: ${availableScopes.map(id => `${getScopeTitle(userSession, id)} (${id})`).join(', ')}`);
54
+ const { typeKey, ...payload } = args;
55
+ if (!typeKey) {
56
+ throw new Error('typeKey is required');
78
57
  }
79
- // STEP 6: Build the exact payload using the API example as template
80
- const buildResult = await buildExactPayloadFromExample(contentType.examples.create, contentType.fields || [], userFieldValues, targetScopeId, contentType, naturalLanguage);
81
- // Check if the result is a reference search response
82
- if ('content' in buildResult) {
83
- return buildResult;
84
- }
85
- const { payload, missingFields } = buildResult;
86
- // STEP 7: If fields are missing, ask user for them
87
- if (missingFields.length > 0) {
88
- // Check if any missing fields are reference fields that need special handling
89
- const referenceFieldsNeedingHelp = missingFields.filter(field => field.type === 'reference' && !userFieldValues[field.path] && !userFieldValues[field.path.split('.').pop()]);
90
- if (referenceFieldsNeedingHelp.length > 0) {
91
- return createReferenceFieldPrompt(contentType.title, referenceFieldsNeedingHelp, missingFields, targetScopeId, naturalLanguage);
92
- }
93
- return createFieldCollectionPrompt(contentType.title, missingFields, contentType.examples.create, targetScopeId, naturalLanguage);
94
- }
95
- // STEP 8: Create the content using the exact payload
96
- const createdContent = await createContentViaAPI(config, payload, contentType.key);
97
- return createSuccessResponse(contentType.title, createdContent, getScopeTitle(userSession, targetScopeId));
98
- }
99
- catch (error) {
100
- return createErrorResponse(`Failed to create content: ${error.message}`);
101
- }
102
- }
103
- // STEP 1: Find content type from natural language
104
- async function findContentTypeFromNaturalLanguage(config, naturalLanguage) {
105
- // Get the glossary index
106
- const indexResponse = await fetch(`${config.apiUrl || 'https://api.qik.dev'}/glossary/ai`, {
107
- headers: {
108
- 'Authorization': `Bearer ${config.accessToken}`,
109
- 'Content-Type': 'application/json',
110
- },
111
- });
112
- if (!indexResponse.ok) {
113
- throw new Error(`Failed to fetch glossary: HTTP ${indexResponse.status}`);
114
- }
115
- const glossary = await indexResponse.json();
116
- // Find matching content type
117
- const searchTerm = naturalLanguage.toLowerCase().trim();
118
- let matchedType = null;
119
- // Try exact key match first
120
- matchedType = glossary.find(ct => ct.key === searchTerm);
121
- // Try fuzzy matching on title, plural, and key
122
- if (!matchedType) {
123
- matchedType = glossary.find(ct => {
124
- const title = (ct.title || '').toLowerCase();
125
- const plural = (ct.plural || '').toLowerCase();
126
- const key = (ct.key || '').toLowerCase();
127
- return title.includes(searchTerm) ||
128
- plural.includes(searchTerm) ||
129
- key.includes(searchTerm) ||
130
- searchTerm.includes(title) ||
131
- searchTerm.includes(plural) ||
132
- searchTerm.includes(key);
58
+ const response = await fetch(`${config.apiUrl || 'https://api.qik.dev'}/content/${typeKey}/create`, {
59
+ method: 'POST',
60
+ headers: {
61
+ 'Authorization': `Bearer ${config.accessToken}`,
62
+ 'Content-Type': 'application/json',
63
+ },
64
+ body: JSON.stringify(payload)
133
65
  });
134
- }
135
- if (!matchedType) {
136
- return null;
137
- }
138
- // Fetch detailed documentation with examples
139
- const detailResponse = await fetch(matchedType.urls.documentation.url, {
140
- headers: {
141
- 'Authorization': `Bearer ${config.accessToken}`,
142
- 'Content-Type': 'application/json',
143
- },
144
- });
145
- if (!detailResponse.ok) {
146
- throw new Error(`Failed to fetch content type details: HTTP ${detailResponse.status}`);
147
- }
148
- const detailData = await detailResponse.json();
149
- return {
150
- ...detailData,
151
- title: matchedType.title,
152
- plural: matchedType.plural,
153
- urls: matchedType.urls
154
- };
155
- }
156
- // STEP 5: Build exact payload using the API example as template
157
- async function buildExactPayloadFromExample(examplePayload, fields, userValues, defaultScope, contentType, naturalLanguage) {
158
- // Start with the structure from the example payload but clear out example values
159
- const payload = JSON.parse(JSON.stringify(examplePayload));
160
- const missingFields = [];
161
- // Clear out all example values, keeping only the structure
162
- clearExampleValues(payload, fields);
163
- // Set system-managed fields first
164
- setValueAtPath(payload, 'meta.scopes', [defaultScope]);
165
- // Set default values for fields that have them
166
- fields.forEach(field => {
167
- if (!field.path || field.readOnly === true || field.type === 'group') {
168
- return;
169
- }
170
- // Apply system defaults for common fields
171
- if (field.path === 'meta.status' && field.defaultValues && field.defaultValues.length > 0) {
172
- setValueAtPath(payload, field.path, field.defaultValues[0]);
173
- }
174
- else if (field.path === 'meta.security' && field.defaultValues && field.defaultValues.length > 0) {
175
- setValueAtPath(payload, field.path, field.defaultValues[0]);
176
- }
177
- else if (field.path === 'meta.created' && field.type === 'date') {
178
- setValueAtPath(payload, field.path, new Date().toISOString());
179
- }
180
- });
181
- // Apply user-provided values, handling reference fields specially
182
- for (const [key, value] of Object.entries(userValues)) {
183
- // Try to find the field definition for this key
184
- const field = fields.find(f => f.path === key ||
185
- f.title === key ||
186
- f.path.split('.').pop() === key);
187
- if (field && !field.readOnly) {
188
- // Handle reference fields that might need search assistance
189
- if (field.type === 'reference' && typeof value === 'string' && value.startsWith('search for:')) {
190
- // This is a search request for a reference field - we'll handle this in the missing fields check
191
- continue;
192
- }
193
- setValueAtPath(payload, field.path, value);
194
- }
195
- else {
196
- // If no field definition found, try to set it directly
197
- setValueAtPath(payload, key, value);
198
- }
199
- }
200
- // Check for required fields that still need values, and handle reference field searches
201
- for (const field of fields) {
202
- if (!field.path || field.readOnly === true || field.type === 'group') {
203
- continue;
204
- }
205
- // Check if this is a reference field with a search request
206
- if (field.type === 'reference') {
207
- const userValue = getUserValueForField(userValues, field);
208
- if (typeof userValue === 'string' && userValue.startsWith('search for:')) {
209
- const searchTerm = userValue.replace('search for:', '').trim();
210
- if (searchTerm) {
211
- // Perform the reference search
212
- const referenceResult = await handleReferenceFieldSearch(field, searchTerm, contentType.title, defaultScope, naturalLanguage);
213
- if (referenceResult) {
214
- return referenceResult;
215
- }
216
- }
217
- }
218
- }
219
- if (field.required === true) {
220
- const currentValue = getValueAtPath(payload, field.path);
221
- // If the field is empty/null/undefined, mark as missing
222
- if (currentValue === null || currentValue === undefined || currentValue === '') {
223
- missingFields.push(field);
224
- }
225
- }
226
- }
227
- return { payload, missingFields };
228
- }
229
- // Helper function to clear example values while preserving structure
230
- function clearExampleValues(obj, fields, currentPath = '') {
231
- if (obj === null || typeof obj !== 'object') {
232
- return;
233
- }
234
- if (Array.isArray(obj)) {
235
- // For arrays, clear them but keep the structure
236
- obj.length = 0; // Clear the array but keep it as an array
237
- return;
238
- }
239
- // For objects, recursively clear values
240
- for (const key in obj) {
241
- const fullPath = currentPath ? `${currentPath}.${key}` : key;
242
- if (typeof obj[key] === 'object' && obj[key] !== null && !Array.isArray(obj[key])) {
243
- // Recursively clear nested objects
244
- clearExampleValues(obj[key], fields, fullPath);
245
- }
246
- else if (Array.isArray(obj[key])) {
247
- // Clear arrays but keep them as arrays
248
- obj[key] = [];
249
- }
250
- else {
251
- // Clear primitive values - don't preserve system fields at this stage
252
- // We'll set system fields later in the process
253
- if (typeof obj[key] === 'string') {
254
- obj[key] = undefined;
255
- }
256
- else if (typeof obj[key] === 'number') {
257
- obj[key] = undefined;
258
- }
259
- else if (typeof obj[key] === 'boolean') {
260
- obj[key] = undefined;
261
- }
262
- else {
263
- obj[key] = undefined;
264
- }
265
- }
266
- }
267
- }
268
- // Helper function to get value at a specific path
269
- function getValueAtPath(obj, path) {
270
- const parts = path.split('.');
271
- let current = obj;
272
- for (const part of parts) {
273
- if (current === null || current === undefined) {
274
- return undefined;
275
- }
276
- current = current[part];
277
- }
278
- return current;
279
- }
280
- // Get user value for a specific field
281
- function getUserValueForField(userValues, field) {
282
- // Try exact field path first
283
- if (userValues[field.path] !== undefined) {
284
- return userValues[field.path];
285
- }
286
- // Try field title
287
- if (field.title && userValues[field.title] !== undefined) {
288
- return userValues[field.title];
289
- }
290
- // Try last part of path (e.g., "title" for "meta.title")
291
- const pathParts = field.path.split('.');
292
- const lastPart = pathParts[pathParts.length - 1];
293
- if (userValues[lastPart] !== undefined) {
294
- return userValues[lastPart];
295
- }
296
- return undefined;
297
- }
298
- // Set value at specific path in object
299
- function setValueAtPath(obj, path, value) {
300
- const parts = path.split('.');
301
- let current = obj;
302
- for (let i = 0; i < parts.length - 1; i++) {
303
- const part = parts[i];
304
- if (!current[part]) {
305
- current[part] = {};
66
+ if (!response.ok) {
67
+ const errorText = await response.text();
68
+ throw new Error(`HTTP ${response.status}: ${errorText}`);
306
69
  }
307
- current = current[part];
308
- }
309
- const finalPart = parts[parts.length - 1];
310
- current[finalPart] = value;
311
- }
312
- // Create content via API
313
- async function createContentViaAPI(config, payload, contentTypeKey) {
314
- const response = await fetch(`${config.apiUrl || 'https://api.qik.dev'}/content/${contentTypeKey}/create`, {
315
- method: 'POST',
316
- headers: {
317
- 'Authorization': `Bearer ${config.accessToken}`,
318
- 'Content-Type': 'application/json',
319
- },
320
- body: JSON.stringify(payload)
321
- });
322
- if (!response.ok) {
323
- const errorText = await response.text();
324
- throw new Error(`HTTP ${response.status} - ${errorText}`);
325
- }
326
- return await response.json();
327
- }
328
- // Helper functions for responses
329
- function createErrorResponse(message) {
330
- return {
331
- content: [{
332
- type: "text",
333
- text: JSON.stringify({ error: message }, null, 2)
334
- }]
335
- };
336
- }
337
- function createScopeSelectionPrompt(contentTitle, availableScopes, userSession, naturalLanguage) {
338
- const scopeOptions = availableScopes.map(scopeId => ({
339
- id: scopeId,
340
- title: getScopeTitle(userSession, scopeId)
341
- }));
342
- return {
343
- content: [{
344
- type: "text",
345
- text: JSON.stringify({
346
- needsScopeSelection: true,
347
- contentTitle,
348
- availableScopes: scopeOptions,
349
- typeKey: naturalLanguage
350
- }, null, 2)
351
- }]
352
- };
353
- }
354
- function createFieldCollectionPrompt(contentTitle, missingFields, examplePayload, scopeId, naturalLanguage) {
355
- return {
356
- content: [{
357
- type: "text",
358
- text: JSON.stringify({
359
- needsFields: true,
360
- contentTitle,
361
- missingFields: missingFields.map(field => ({
362
- path: field.path,
363
- title: field.title,
364
- type: field.type,
365
- description: field.description,
366
- referenceType: field.referenceType,
367
- options: field.options
368
- })),
369
- typeKey: naturalLanguage || contentTitle.toLowerCase(),
370
- scope: scopeId
371
- }, null, 2)
372
- }]
373
- };
374
- }
375
- function createReferenceFieldPrompt(contentTitle, referenceFields, allMissingFields, scopeId, naturalLanguage) {
376
- const nonReferenceFields = allMissingFields.filter(field => field.type !== 'reference');
377
- return {
378
- content: [{
379
- type: "text",
380
- text: JSON.stringify({
381
- needsReferenceFields: true,
382
- contentTitle,
383
- referenceFields: referenceFields.map(field => ({
384
- path: field.path,
385
- title: field.title,
386
- description: field.description,
387
- referenceType: field.referenceType
388
- })),
389
- otherMissingFields: nonReferenceFields.map(field => ({
390
- path: field.path,
391
- title: field.title,
392
- type: field.type,
393
- description: field.description
394
- })),
395
- typeKey: naturalLanguage || contentTitle.toLowerCase(),
396
- scope: scopeId
397
- }, null, 2)
398
- }]
399
- };
400
- }
401
- // Handle reference field search and selection
402
- async function handleReferenceFieldSearch(field, searchTerm, contentTitle, scopeId, naturalLanguage) {
403
- try {
404
- if (!field.referenceType) {
405
- return {
406
- content: [{
407
- type: "text",
408
- text: JSON.stringify({ error: `Cannot search for ${field.path} references - no reference type specified in field definition.` }, null, 2)
409
- }]
410
- };
411
- }
412
- // Use the list function to search for references
413
- const listArgs = {
414
- typeKey: field.referenceType,
415
- search: searchTerm,
416
- page: { size: 10, index: 1 }
417
- };
418
- const searchResults = await handleListContent(listArgs);
70
+ const data = await response.json();
419
71
  return {
420
72
  content: [{
421
73
  type: "text",
422
- text: JSON.stringify({
423
- referenceSearch: true,
424
- field: field.path,
425
- fieldTitle: field.title,
426
- referenceType: field.referenceType,
427
- searchTerm,
428
- results: JSON.parse(searchResults.content[0]?.text || '{}'),
429
- typeKey: naturalLanguage,
430
- scope: scopeId
431
- }, null, 2)
74
+ text: JSON.stringify(data, null, 2)
432
75
  }]
433
76
  };
434
77
  }
@@ -436,238 +79,9 @@ async function handleReferenceFieldSearch(field, searchTerm, contentTitle, scope
436
79
  return {
437
80
  content: [{
438
81
  type: "text",
439
- text: JSON.stringify({ error: `Failed to search for ${field.path} references: ${error.message}` }, null, 2)
82
+ text: JSON.stringify({ error: error.message }, null, 2)
440
83
  }]
441
84
  };
442
85
  }
443
86
  }
444
- async function createUICreationSuggestion(naturalLanguage, userArgs = {}) {
445
- // Determine interface type based on keywords
446
- let interfaceType = 'website';
447
- let description = 'a website interface';
448
- if (naturalLanguage.toLowerCase().includes('web app') || naturalLanguage.toLowerCase().includes('webapp') || naturalLanguage.toLowerCase().includes('app')) {
449
- interfaceType = 'web-app';
450
- description = 'a web application interface';
451
- }
452
- else if (naturalLanguage.toLowerCase().includes('portal')) {
453
- interfaceType = 'portal';
454
- description = 'a portal interface';
455
- }
456
- else if (naturalLanguage.toLowerCase().includes('blog')) {
457
- interfaceType = 'blog';
458
- description = 'a blog interface';
459
- }
460
- // Step 1: Initial request - ask guided questions
461
- if (!userArgs.interfaceTitle && !userArgs.confirmPayload) {
462
- return createInterfaceQuestionPrompt(description, interfaceType, naturalLanguage);
463
- }
464
- // Step 2: Generate payload from collected information
465
- if (userArgs.interfaceTitle && !userArgs.confirmPayload) {
466
- const generatedPayload = generateInterfacePayload(userArgs, interfaceType);
467
- return createPayloadReviewPrompt(generatedPayload, naturalLanguage, userArgs);
468
- }
469
- // Step 3: User confirmed payload - create the interface
470
- if (userArgs.confirmPayload) {
471
- return await handleConfirmedInterfaceCreation(naturalLanguage, userArgs, interfaceType);
472
- }
473
- // Invalid state
474
- return createErrorResponse('Invalid interface creation state. Please start the process again.');
475
- }
476
- function createSuccessResponse(contentTitle, createdContent, scopeTitle) {
477
- return {
478
- content: [{
479
- type: "text",
480
- text: JSON.stringify(createdContent, null, 2)
481
- }]
482
- };
483
- }
484
- // Interface creation helper functions
485
- function createInterfaceQuestionPrompt(description, interfaceType, naturalLanguage) {
486
- return {
487
- content: [{
488
- type: "text",
489
- text: JSON.stringify({
490
- needsInterfaceDetails: true,
491
- description,
492
- interfaceType,
493
- typeKey: naturalLanguage,
494
- requiredFields: ['interfaceTitle'],
495
- optionalFields: ['pageCount', 'pages', 'primaryColor']
496
- }, null, 2)
497
- }]
498
- };
499
- }
500
- function generateInterfacePayload(args, interfaceType) {
501
- const title = args.interfaceTitle || 'New Interface';
502
- const pageCount = args.pageCount || 3;
503
- const primaryColor = args.primaryColor || '#3570c9';
504
- // Generate default pages based on interface type and count
505
- let pages = args.pages;
506
- if (!pages || !Array.isArray(pages)) {
507
- pages = generateDefaultPages(interfaceType, pageCount);
508
- }
509
- // Basic interface payload structure
510
- const payload = {
511
- title: title,
512
- meta: {
513
- scopes: [] // Will be filled by scope selection
514
- },
515
- seo: {
516
- title: `${title} - Default SEO Title`,
517
- description: `${title} - Default SEO Description`
518
- },
519
- menus: [
520
- {
521
- title: "Primary Menu",
522
- name: "primaryMenu",
523
- items: pages.map((pageName, index) => ({
524
- title: pageName,
525
- type: "route",
526
- route: index === 0 ? "home" : pageName.toLowerCase().replace(/\s+/g, ''),
527
- url: "",
528
- target: "",
529
- items: []
530
- }))
531
- },
532
- {
533
- title: "Footer Menu",
534
- name: "footerMenu",
535
- items: [
536
- {
537
- title: "Privacy Policy",
538
- type: "route",
539
- route: "privacyPolicy",
540
- url: "",
541
- target: "",
542
- items: []
543
- },
544
- {
545
- title: "Terms and Conditions",
546
- type: "route",
547
- route: "termsAndConditions",
548
- url: "",
549
- target: "",
550
- items: []
551
- }
552
- ]
553
- }
554
- ],
555
- services: [],
556
- components: [],
557
- styles: {
558
- pre: `:root {\n --primary: ${primaryColor};\n --text: #777;\n --headings: #222;\n --outer-width: 1800px;\n --inner-width: 1400px;\n --btn-border-radius: 3em;\n}\n\nbody, html {\n font-size: clamp(13px, 1.5vw, 23px);\n font-family: 'Poppins', sans-serif;\n scroll-behavior: smooth;\n background: #eee;\n color: var(--text);\n}\n\n@import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,400;0,700;1,400&display=swap');`,
559
- post: `.layout-app {\n background: #fff;\n max-width: var(--outer-width);\n margin: auto;\n box-shadow: 0 0 15px rgba(0, 0, 0, 0.05);\n}\n\n.v-wrap {\n padding: 4em 0;\n}\n\n.h-wrap {\n margin: auto;\n padding: 0 20px;\n max-width: var(--inner-width);\n}\n\nh1, h2, h3, h4, h5, h6 {\n letter-spacing: -0.03em;\n line-height: 1.2;\n margin-top: 0;\n margin-bottom: 0.5em;\n color: var(--headings);\n}`,
560
- includes: [],
561
- themes: [
562
- {
563
- title: "Primary",
564
- uuid: generateUUID(),
565
- body: `background: var(--primary);\ncolor: #fff;\n\nh1, h2, h3, h4, h5 {\n color: #fff;\n}`
566
- }
567
- ]
568
- },
569
- header: { sections: [] },
570
- slots: [],
571
- layout: "",
572
- routes: pages.map((pageName, index) => ({
573
- sections: [],
574
- title: pageName,
575
- path: index === 0 ? "/" : `/${pageName.toLowerCase().replace(/\s+/g, '-')}`,
576
- seo: {
577
- title: `${pageName} - ${title}`,
578
- description: `${pageName} page for ${title}`,
579
- sitemapDisabled: false,
580
- sitemapPriority: index === 0 ? "1.0" : "0.5",
581
- sitemapChangeFrequency: ""
582
- },
583
- name: index === 0 ? "home" : pageName.toLowerCase().replace(/\s+/g, ''),
584
- type: "route",
585
- headerDisabled: false,
586
- footerDisabled: false,
587
- advancedOptions: false,
588
- routes: []
589
- })),
590
- footer: { sections: [] },
591
- usage: [],
592
- versioning: { latest: [] }
593
- };
594
- return payload;
595
- }
596
- function generateDefaultPages(interfaceType, pageCount) {
597
- const pageTemplates = {
598
- website: ['Home', 'About', 'Services', 'Contact', 'Portfolio', 'Blog'],
599
- 'web-app': ['Dashboard', 'Profile', 'Settings', 'Help', 'Reports', 'Analytics'],
600
- portal: ['Dashboard', 'Users', 'Reports', 'Settings', 'Profile', 'Help'],
601
- blog: ['Home', 'Articles', 'Categories', 'About', 'Contact', 'Archive']
602
- };
603
- const template = pageTemplates[interfaceType] || pageTemplates.website;
604
- return template.slice(0, Math.max(pageCount, 1));
605
- }
606
- function createPayloadReviewPrompt(payload, naturalLanguage, userArgs) {
607
- const preview = {
608
- title: payload.title,
609
- pages: payload.routes.map((route) => ({
610
- title: route.title,
611
- path: route.path
612
- })),
613
- primaryColor: payload.styles?.pre?.match(/--primary:\s*([^;]+)/)?.[1] || '#3570c9',
614
- menus: payload.menus.map((menu) => ({
615
- name: menu.name,
616
- items: menu.items.map((item) => item.title)
617
- }))
618
- };
619
- return {
620
- content: [{
621
- type: "text",
622
- text: JSON.stringify({
623
- needsConfirmation: true,
624
- preview,
625
- typeKey: naturalLanguage,
626
- interfaceTitle: userArgs.interfaceTitle
627
- }, null, 2)
628
- }]
629
- };
630
- }
631
- function generateUUID() {
632
- return Math.random().toString(36).substr(2, 10);
633
- }
634
- async function handleConfirmedInterfaceCreation(naturalLanguage, userArgs, interfaceType) {
635
- try {
636
- const configManager = new ConfigManager();
637
- const config = await configManager.loadConfig();
638
- if (!config) {
639
- throw new Error('Qik MCP server not configured. Run setup first.');
640
- }
641
- // Generate the interface payload
642
- const interfacePayload = generateInterfacePayload(userArgs, interfaceType);
643
- // Get user session for scope handling
644
- const userSession = await getUserSessionData();
645
- // Handle scope selection for interface creation
646
- const selectedScopeId = userArgs.scope || userArgs.scopeId;
647
- // Get available scopes for interface creation (assuming interface.create permission)
648
- const interfaceCreatePermission = 'interface.create';
649
- const availableScopes = getAvailableScopes(userSession, interfaceCreatePermission);
650
- if (availableScopes.length === 0) {
651
- return createErrorResponse(`You don't have permission to create interfaces. Required permission: ${interfaceCreatePermission}`);
652
- }
653
- // If no scope provided and multiple scopes available, ask user to select
654
- if (!selectedScopeId && availableScopes.length > 1) {
655
- return createScopeSelectionPrompt('interface', availableScopes, userSession, naturalLanguage);
656
- }
657
- // Use selected scope or default to first available
658
- const targetScopeId = selectedScopeId || availableScopes[0];
659
- // Validate the selected scope is actually available
660
- if (!availableScopes.includes(targetScopeId)) {
661
- return createErrorResponse(`Invalid scope "${targetScopeId}". Available scopes: ${availableScopes.map(id => `${getScopeTitle(userSession, id)} (${id})`).join(', ')}`);
662
- }
663
- // Set the scope in the payload
664
- interfacePayload.meta.scopes = [targetScopeId];
665
- // Create the interface using the API
666
- const createdInterface = await createContentViaAPI(config, interfacePayload, 'interface');
667
- return createSuccessResponse('Interface', createdInterface, getScopeTitle(userSession, targetScopeId));
668
- }
669
- catch (error) {
670
- return createErrorResponse(`Failed to create interface: ${error.message}`);
671
- }
672
- }
673
87
  //# sourceMappingURL=create.js.map