@qikdev/mcp 6.7.5 → 6.7.7

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