mirror of
https://github.com/aljazceru/mcp-code.git
synced 2025-12-17 04:35:19 +01:00
195 lines
6.1 KiB
TypeScript
195 lines
6.1 KiB
TypeScript
import type { NDKEvent } from "@nostr-dev-kit/ndk";
|
|
import { knownUsers } from "../../users.js";
|
|
import type { CodeSnippet } from "../types/index.js";
|
|
|
|
/**
|
|
* Converts an NDKEvent into a CodeSnippet
|
|
* @param event NDKEvent of kind 1337
|
|
* @returns CodeSnippet object
|
|
*/
|
|
export function toSnippet(event: NDKEvent): CodeSnippet {
|
|
const title = event.tagValue("title") ?? event.tagValue("name");
|
|
const description = event.tagValue("description") ?? "";
|
|
const language = event.tagValue("l");
|
|
const tags = event.tags
|
|
.filter((tag) => tag[0] === "t" && tag[1] !== undefined)
|
|
.map((tag) => tag[1] as string);
|
|
|
|
return {
|
|
id: event.id,
|
|
title: title || "Untitled",
|
|
description,
|
|
code: event.content,
|
|
language: language || "text",
|
|
pubkey: event.pubkey,
|
|
createdAt: event.created_at || 0,
|
|
tags,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Converts a CodeSnippet to a formatted metadata string (without code)
|
|
* @param snippet CodeSnippet object
|
|
* @returns Formatted metadata string
|
|
*/
|
|
export function toMetadataString(snippet: CodeSnippet): string {
|
|
const { title, description, language, tags, id, pubkey, createdAt } = snippet;
|
|
const profile = knownUsers[pubkey]?.profile;
|
|
|
|
const returns = [
|
|
`ID: ${id}`,
|
|
`Title: ${title}`,
|
|
`Description: ${description}`,
|
|
`Language: ${language}`,
|
|
`Tags: ${tags.join(", ")}`,
|
|
`Created: ${new Date(createdAt * 1000).toISOString()}`,
|
|
`Pubkey: ${pubkey}`,
|
|
];
|
|
|
|
if (profile?.name) returns.push(`Author: ${profile.name}`);
|
|
|
|
return returns.join("\n");
|
|
}
|
|
|
|
/**
|
|
* Formats a single snippet for display
|
|
* @param snippet The snippet to format
|
|
* @returns Formatted string representation
|
|
*/
|
|
export function formatSnippet(snippet: CodeSnippet): string {
|
|
return formatSnippets([snippet]);
|
|
}
|
|
|
|
/**
|
|
* Formats snippets for display
|
|
* @param snippets Array of code snippets
|
|
* @returns Formatted string representation
|
|
*/
|
|
export function formatSnippets(snippets: CodeSnippet[]): string {
|
|
return snippets
|
|
.map((snippet) => {
|
|
const author = knownUsers[snippet.pubkey];
|
|
const keys: Record<string, string> = {
|
|
Title: snippet.title,
|
|
Language: snippet.language,
|
|
Tags: snippet.tags.join(", "),
|
|
Code: snippet.code,
|
|
};
|
|
if (author?.profile?.name) keys.Author = author.profile.name;
|
|
return Object.entries(keys)
|
|
.map(([key, value]) => `${key}: ${value}`)
|
|
.join("\n");
|
|
})
|
|
.join("\n\n---\n\n");
|
|
}
|
|
|
|
/**
|
|
* Formats partial match snippets for display
|
|
* @param snippets Array of code snippets
|
|
* @returns Formatted string representation
|
|
*/
|
|
export function formatPartialMatches(snippets: CodeSnippet[]): string {
|
|
if (snippets.length === 0) return "";
|
|
|
|
let text =
|
|
"\n\nSome other events not included in this result since they had less in common with your search, here is a list of the events that had partial matches:\n\n";
|
|
text += snippets
|
|
.map((snippet) => {
|
|
return ` * ${snippet.title}:\n Tags: ${snippet.tags.join(", ")}`;
|
|
})
|
|
.join("\n");
|
|
|
|
return text;
|
|
}
|
|
|
|
/**
|
|
* Parses metadata from a file content string
|
|
* @param fileContent String containing metadata and code sections
|
|
* @returns Object with metadata and code
|
|
*/
|
|
export function parseMetadataFromString(fileContent: string): {
|
|
metadata: { title: string; description: string; language: string; tags: string[] };
|
|
code: string
|
|
} {
|
|
// Match the metadata and code sections
|
|
const metadataRegex = /^---METADATA---([\s\S]*?)(?=^---CODE---$)(^---CODE---$)([\s\S]*)$/m;
|
|
const matches = fileContent.match(metadataRegex);
|
|
|
|
if (!matches || matches.length < 4) {
|
|
throw new Error("Invalid file format: metadata section not found");
|
|
}
|
|
|
|
const metadataSection = matches[1] || "";
|
|
let codeSection = matches[3] || "";
|
|
|
|
// Remove leading newline from code section if present
|
|
if (codeSection.startsWith("\n")) {
|
|
codeSection = codeSection.substring(1);
|
|
}
|
|
|
|
// Parse each field with proper multiline flag
|
|
const titleMatch = metadataSection.match(/^Title:\s*(.+)$/m);
|
|
const title = titleMatch?.[1] ? titleMatch[1].trim() : "";
|
|
|
|
// Extract description which can be multiline but should stop at Language: or Tags:
|
|
const descriptionLines = [];
|
|
let inDescription = false;
|
|
|
|
// Process line by line
|
|
const lines = metadataSection.split('\n');
|
|
for (const line of lines) {
|
|
if (line.trim().startsWith('Description:')) {
|
|
inDescription = true;
|
|
const content = line.replace(/^Description:\s*/, '').trim();
|
|
if (content) {
|
|
descriptionLines.push(content);
|
|
}
|
|
} else if (line.trim().startsWith('Language:') || line.trim().startsWith('Tags:')) {
|
|
inDescription = false;
|
|
} else if (inDescription) {
|
|
descriptionLines.push(line);
|
|
}
|
|
}
|
|
|
|
const description = descriptionLines.join('\n').trim();
|
|
|
|
const languageMatch = metadataSection.match(/^Language:\s*(.+)$/m);
|
|
const language = languageMatch?.[1] ? languageMatch[1].trim() : "";
|
|
|
|
const tagsMatch = metadataSection.match(/^Tags:\s*(.+)$/m);
|
|
const tagsString = tagsMatch?.[1] ? tagsMatch[1].trim() : "";
|
|
const tags = tagsString.split(',').map(tag => tag.trim()).filter(Boolean);
|
|
|
|
return {
|
|
metadata: {
|
|
title,
|
|
description,
|
|
language,
|
|
tags
|
|
},
|
|
code: codeSection
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Creates a string with metadata and code sections
|
|
*/
|
|
export function createMetadataString(
|
|
title: string,
|
|
description: string,
|
|
language: string,
|
|
tags: string[],
|
|
code: string
|
|
): string {
|
|
return `---METADATA---
|
|
# Edit the metadata below. Keep the format exactly as shown (Title:, Description:, Language:, Tags:)
|
|
# Description needs to be at least 140 characters and Tags need at least 5 entries
|
|
# Don't remove the ---METADATA--- and ---CODE--- markers!
|
|
|
|
Title: ${title}
|
|
Description: ${description}
|
|
Language: ${language}
|
|
Tags: ${tags.join(', ')}
|
|
---CODE---
|
|
${code}`;
|
|
}
|