Skip to content

Commit 97e86cd

Browse files
committed
feat: add markdown-based prompts support
- Add markdown prompt loader with frontmatter parsing using gray-matter - Add 3 TimescaleDB-focused prompts: hypertable analysis, migration, and setup - Update build process to copy .md files to dist directory - Integrate with updated boilerplate supporting prompts capability - Remove unused handlebars dependency Prompts included: - identifyHypertableCandidates: Analyze tables for hypertable conversion - postgresHypertableMigration: Guide for migrating to hypertables - timescaledbSetup: Complete TimescaleDB setup with compression and policies
1 parent 7339ee3 commit 97e86cd

File tree

9 files changed

+1984
-2
lines changed

9 files changed

+1984
-2
lines changed

package-lock.json

Lines changed: 109 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"dist"
1414
],
1515
"scripts": {
16-
"build": "tsc && shx chmod +x dist/*.js",
16+
"build": "tsc && shx cp -r src/prompts/md dist/prompts/ && shx chmod +x dist/*.js",
1717
"prepare": "npm run build",
1818
"watch": "tsc --watch",
1919
"watch:http": "tsx watch src/index.ts http",
@@ -39,6 +39,7 @@
3939
"ai": "^5.0.17",
4040
"dotenv": "^17.2.0",
4141
"express": "^5.1.0",
42+
"gray-matter": "^4.0.3",
4243
"pg": "^8.16.3",
4344
"zod": "^3.23.8"
4445
},

src/prompts/index.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { dirname, join } from 'path';
2+
import { fileURLToPath } from 'url';
3+
import { loadMarkdownPromptsFromDirectory } from './markdownLoader.js';
4+
import { ServerContext } from '../types.js';
5+
6+
const __dirname = dirname(fileURLToPath(import.meta.url));
7+
const markdownPromptsDir = join(__dirname, 'md');
8+
9+
export const promptFactories = [
10+
...loadMarkdownPromptsFromDirectory<ServerContext>(markdownPromptsDir),
11+
] as const;

src/prompts/markdownLoader.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { readFileSync, readdirSync } from 'fs';
2+
import { join } from 'path';
3+
import matter from 'gray-matter';
4+
import { z } from 'zod';
5+
import { PromptFactory } from '../shared/boilerplate/src/types.js';
6+
7+
const PromptArgumentSchema = z.object({
8+
name: z.string(),
9+
description: z.string().optional(),
10+
required: z.boolean().optional().default(false),
11+
});
12+
13+
const PromptFrontmatterSchema = z.object({
14+
name: z.string(),
15+
title: z.string().optional(),
16+
description: z.string().optional(),
17+
arguments: z.array(PromptArgumentSchema).optional().default([]),
18+
});
19+
20+
export const loadMarkdownPrompt = <Context extends Record<string, unknown>>(
21+
filePath: string,
22+
): PromptFactory<Context, any> => {
23+
const fileContent = readFileSync(filePath, 'utf-8');
24+
const { data, content } = matter(fileContent);
25+
26+
const frontmatter = PromptFrontmatterSchema.parse(data);
27+
28+
// Create zod schema from arguments
29+
const inputSchemaShape: Record<string, z.ZodTypeAny> = {};
30+
for (const arg of frontmatter.arguments) {
31+
let schema: z.ZodTypeAny = z.string();
32+
if (arg.description) {
33+
schema = schema.describe(arg.description);
34+
}
35+
if (!arg.required) {
36+
schema = schema.optional();
37+
}
38+
inputSchemaShape[arg.name] = schema;
39+
}
40+
41+
return () => ({
42+
name: frontmatter.name,
43+
config: {
44+
title: frontmatter.title,
45+
description: frontmatter.description,
46+
inputSchema: inputSchemaShape,
47+
},
48+
fn: async (args) => {
49+
return {
50+
description: frontmatter.description || frontmatter.title || frontmatter.name,
51+
messages: [
52+
{
53+
role: 'user',
54+
content: {
55+
type: 'text',
56+
text: content.trim(),
57+
},
58+
},
59+
],
60+
};
61+
},
62+
});
63+
};
64+
65+
export const loadMarkdownPromptsFromDirectory = <Context extends Record<string, unknown>>(
66+
dirPath: string,
67+
): PromptFactory<Context, any>[] => {
68+
const files = readdirSync(dirPath).filter((file: string) => file.endsWith('.md'));
69+
70+
return files.map((file: string) =>
71+
loadMarkdownPrompt<Context>(join(dirPath, file))
72+
);
73+
};

0 commit comments

Comments
 (0)