latitude-mcp-server 4.0.0 → 4.0.1

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/dist/api.d.ts CHANGED
@@ -43,11 +43,11 @@ export interface ValidationIssue {
43
43
  }
44
44
  export declare function validatePromptLContent(content: string, path: string): Promise<ValidationIssue[]>;
45
45
  /**
46
- * Deploy changes to LIVE version using SDK-style atomic push.
46
+ * Deploy changes to LIVE version using draft→push→merge workflow.
47
47
  *
48
- * This matches the CLI's approach:
49
- * - Single POST to /versions/live/push with all changes
50
- * - Returns the new committed version UUID
51
- * - No separate create→push→publish workflow
48
+ * Workflow:
49
+ * 1. Create a new draft version
50
+ * 2. Push all changes to the draft
51
+ * 3. Merge the draft to live
52
52
  */
53
53
  export declare function deployToLive(changes: DocumentChange[], _versionName?: string): Promise<DeployResult>;
package/dist/api.js CHANGED
@@ -414,12 +414,20 @@ async function validatePromptLContent(content, path) {
414
414
  return issues;
415
415
  }
416
416
  /**
417
- * Push changes to a version atomically.
418
- * This is the SDK-style push that creates a new version with all changes applied.
419
- *
420
- * Endpoint: POST /projects/:projectId/versions/:versionUuid/push
421
- * Body: { changes: [...] }
422
- * Returns: { commitUuid: string } - the new version UUID
417
+ * Create a new draft version based on HEAD (live).
418
+ */
419
+ async function createDraftVersion(name) {
420
+ const projectId = getProjectId();
421
+ const timestamp = Date.now();
422
+ const versionName = name || `mcp-draft-${timestamp}`;
423
+ logger.info(`Creating draft version: ${versionName}`);
424
+ return request(`/projects/${projectId}/versions`, {
425
+ method: 'POST',
426
+ body: { name: versionName },
427
+ });
428
+ }
429
+ /**
430
+ * Push changes to a draft version.
423
431
  */
424
432
  async function pushToVersion(versionUuid, changes) {
425
433
  const projectId = getProjectId();
@@ -448,12 +456,12 @@ function createNoOpVersion() {
448
456
  };
449
457
  }
450
458
  /**
451
- * Deploy changes to LIVE version using SDK-style atomic push.
459
+ * Deploy changes to LIVE version using draft→push→merge workflow.
452
460
  *
453
- * This matches the CLI's approach:
454
- * - Single POST to /versions/live/push with all changes
455
- * - Returns the new committed version UUID
456
- * - No separate create→push→publish workflow
461
+ * Workflow:
462
+ * 1. Create a new draft version
463
+ * 2. Push all changes to the draft
464
+ * 3. Merge the draft to live
457
465
  */
458
466
  async function deployToLive(changes, _versionName) {
459
467
  if (changes.length === 0) {
@@ -482,20 +490,24 @@ async function deployToLive(changes, _versionName) {
482
490
  const deleted = actualChanges.filter((c) => c.status === 'deleted').map((c) => c.path);
483
491
  logger.info(`Deploying to LIVE: ${added.length} added, ${modified.length} modified, ${deleted.length} deleted`);
484
492
  try {
485
- const result = await pushToVersion('live', actualChanges);
486
- logger.info(`Push successful! New version: ${result.commitUuid}`);
493
+ // Step 1: Create a draft version
494
+ const draft = await createDraftVersion(_versionName);
495
+ logger.info(`Created draft version: ${draft.uuid}`);
496
+ // Step 2: Push changes to the draft - this creates a new committed version
497
+ const pushResult = await pushToVersion(draft.uuid, actualChanges);
498
+ logger.info(`Pushed changes. New commit: ${pushResult.commitUuid}`);
487
499
  const now = new Date().toISOString();
488
500
  return {
489
501
  version: {
490
- id: 0,
491
- uuid: result.commitUuid,
502
+ id: draft.id,
503
+ uuid: pushResult.commitUuid,
492
504
  projectId: 0,
493
505
  message: _versionName || 'MCP push',
494
506
  createdAt: now,
495
507
  updatedAt: now,
496
- status: 'live',
508
+ status: 'merged',
497
509
  },
498
- documentsProcessed: result.documentsProcessed || actualChanges.length,
510
+ documentsProcessed: pushResult.documentsProcessed || actualChanges.length,
499
511
  added,
500
512
  modified,
501
513
  deleted,
@@ -503,7 +515,7 @@ async function deployToLive(changes, _versionName) {
503
515
  }
504
516
  catch (error) {
505
517
  if (error instanceof LatitudeApiError) {
506
- logger.error(`Push failed: ${error.message}`);
518
+ logger.error(`Deploy failed: ${error.message}`);
507
519
  throw error;
508
520
  }
509
521
  throw error;
package/dist/tools.js CHANGED
@@ -106,13 +106,20 @@ function readPromptsFromFolder(folderPath) {
106
106
  throw new Error(`No .promptl files found in: ${resolvedPath}`);
107
107
  }
108
108
  const prompts = [];
109
+ const conversions = [];
109
110
  for (const file of files) {
110
111
  const filePath = (0, path_1.join)(resolvedPath, file);
111
112
  const content = (0, fs_1.readFileSync)(filePath, 'utf-8');
112
- const name = file.replace(/\.promptl$/, '').replace(/_/g, '/');
113
+ // Remove .promptl extension, convert . to - for API compatibility (only letters, numbers, '.', '-', '_' allowed)
114
+ const baseName = file.replace(/\.promptl$/, '');
115
+ // Replace dots with dashes (API allows dots but it's cleaner as dashes)
116
+ const name = baseName.replace(/\./g, '-');
117
+ if (baseName !== name) {
118
+ conversions.push(`${file} → ${name}`);
119
+ }
113
120
  prompts.push({ name, content });
114
121
  }
115
- return prompts;
122
+ return { prompts, conversions };
116
123
  }
117
124
  // Format available prompts for description
118
125
  function formatAvailablePrompts(names) {
@@ -316,7 +323,7 @@ async function handlePushPrompts(args) {
316
323
  if (!args.folderPath) {
317
324
  return formatError(new Error('folderPath is required. Provide absolute path to folder containing .promptl files.'));
318
325
  }
319
- const prompts = readPromptsFromFolder(args.folderPath);
326
+ const { prompts, conversions } = readPromptsFromFolder(args.folderPath);
320
327
  logger.info(`Read ${prompts.length} prompt(s) from ${args.folderPath}`);
321
328
  // PRE-VALIDATE ALL PROMPTS BEFORE PUSHING
322
329
  // If ANY prompt fails validation, return errors and push NOTHING
@@ -355,6 +362,11 @@ async function handlePushPrompts(args) {
355
362
  content += `- Modified: ${modified.length}\n`;
356
363
  content += `- Deleted: ${deleted.length}\n`;
357
364
  content += `- Documents processed: ${result.documentsProcessed}\n\n`;
365
+ if (conversions.length > 0) {
366
+ content += `### ⚠️ Auto-converted Names\n`;
367
+ content += `Dots (.) in filenames were converted to underscores (_) for API compatibility:\n`;
368
+ content += conversions.map((c) => `- ${c}`).join('\n') + '\n\n';
369
+ }
358
370
  if (added.length > 0) {
359
371
  content += `### Added\n${added.map((c) => `- \`${c.path}\``).join('\n')}\n\n`;
360
372
  }
@@ -424,7 +436,7 @@ async function handleAddPrompt(args) {
424
436
  const filename = `${prompt.name.replace(/\//g, '_')}.promptl`;
425
437
  (0, fs_1.writeFileSync)((0, path_1.join)(tempDir, filename), prompt.content, 'utf-8');
426
438
  }
427
- const mergedPrompts = readPromptsFromFolder(tempDir);
439
+ const { prompts: mergedPrompts } = readPromptsFromFolder(tempDir);
428
440
  logger.info(`Merged state: ${mergedPrompts.length} total prompt(s)`);
429
441
  logger.info(`Validating ${mergedPrompts.length} prompt(s) before push...`);
430
442
  const validation = await validateAllPrompts(mergedPrompts);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "latitude-mcp-server",
3
- "version": "4.0.0",
3
+ "version": "4.0.1",
4
4
  "description": "Simplified MCP server for Latitude.so prompt management - 8 focused tools for push, pull, run, and manage prompts",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",