@elizaos/prompts 2.0.0-alpha

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.
@@ -0,0 +1,306 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Plugin Prompt Generator Script
5
+ *
6
+ * Generates native code from .txt prompt templates for plugins.
7
+ * Can be used by any plugin to generate TypeScript, Python, and Rust exports.
8
+ *
9
+ * Usage:
10
+ * node generate-plugin-prompts.js <prompts-dir> <output-base-dir> [--target typescript|python|rust|all]
11
+ *
12
+ * Example:
13
+ * node generate-plugin-prompts.js ./prompts ./dist --target all
14
+ */
15
+
16
+ import fs from "node:fs";
17
+ import path from "node:path";
18
+ import { fileURLToPath } from "node:url";
19
+
20
+ const __filename = fileURLToPath(import.meta.url);
21
+ const __dirname = path.dirname(__filename);
22
+
23
+ /**
24
+ * Convert filename to constant name
25
+ * e.g., "should_respond.txt" -> "SHOULD_RESPOND_TEMPLATE"
26
+ */
27
+ function fileToConstName(filename) {
28
+ const name = path.basename(filename, ".txt");
29
+ return `${name.toUpperCase().replace(/-/g, "_")}_TEMPLATE`;
30
+ }
31
+
32
+ /**
33
+ * Convert filename to camelCase name for TypeScript exports
34
+ * e.g., "should_respond.txt" -> "shouldRespondTemplate"
35
+ */
36
+ function fileToCamelCase(filename) {
37
+ const name = path.basename(filename, ".txt");
38
+ const parts = name.split("_");
39
+ return (
40
+ parts[0] +
41
+ parts
42
+ .slice(1)
43
+ .map((p) => p.charAt(0).toUpperCase() + p.slice(1))
44
+ .join("") +
45
+ "Template"
46
+ );
47
+ }
48
+
49
+ /**
50
+ * Escape a string for use in TypeScript template literal
51
+ */
52
+ function escapeTypeScript(content) {
53
+ return content
54
+ .replace(/\\/g, "\\\\")
55
+ .replace(/`/g, "\\`")
56
+ .replace(/\$\{/g, "\\${");
57
+ }
58
+
59
+ /**
60
+ * Escape a string for use in Python triple-quoted string
61
+ */
62
+ function escapePython(content) {
63
+ return content.replace(/\\/g, "\\\\").replace(/"""/g, '\\"\\"\\"');
64
+ }
65
+
66
+ /**
67
+ * Escape a string for use in Rust raw string literal
68
+ * Rust raw strings r#"..."# don't need escaping except for the delimiter itself
69
+ */
70
+ function escapeRust(content) {
71
+ // Check if content contains "# - if so, we need more # in our delimiter
72
+ let hashCount = 1;
73
+ while (content.includes(`"${"#".repeat(hashCount)}`)) {
74
+ hashCount++;
75
+ }
76
+ return { content, hashCount };
77
+ }
78
+
79
+ /**
80
+ * Load all prompts from the prompts directory
81
+ */
82
+ function loadPrompts(promptsDir) {
83
+ if (!fs.existsSync(promptsDir)) {
84
+ console.warn(`Warning: Prompts directory does not exist: ${promptsDir}`);
85
+ return [];
86
+ }
87
+
88
+ const prompts = [];
89
+ const files = fs.readdirSync(promptsDir);
90
+
91
+ for (const file of files) {
92
+ if (!file.endsWith(".txt")) continue;
93
+
94
+ const filepath = path.join(promptsDir, file);
95
+ const content = fs.readFileSync(filepath, "utf-8");
96
+
97
+ prompts.push({
98
+ filename: file,
99
+ constName: fileToConstName(file),
100
+ camelName: fileToCamelCase(file),
101
+ content: content.trim(),
102
+ });
103
+ }
104
+
105
+ return prompts.sort((a, b) => a.constName.localeCompare(b.constName));
106
+ }
107
+
108
+ /**
109
+ * Generate TypeScript output
110
+ */
111
+ function generateTypeScript(prompts, outputBaseDir, sourcePath) {
112
+ const outputDir = path.join(outputBaseDir, "typescript");
113
+ fs.mkdirSync(outputDir, { recursive: true });
114
+
115
+ const relativeSourcePath = path
116
+ .relative(outputDir, sourcePath)
117
+ .replace(/\\/g, "/");
118
+
119
+ let output = `/**
120
+ * Auto-generated prompt templates
121
+ * DO NOT EDIT - Generated from ${relativeSourcePath}/*.txt
122
+ *
123
+ * These prompts use Handlebars-style template syntax:
124
+ * - {{variableName}} for simple substitution
125
+ * - {{#each items}}...{{/each}} for iteration
126
+ * - {{#if condition}}...{{/if}} for conditionals
127
+ */
128
+
129
+ `;
130
+
131
+ // Export each prompt as both const name and camelCase
132
+ for (const prompt of prompts) {
133
+ const escaped = escapeTypeScript(prompt.content);
134
+ output += `export const ${prompt.camelName} = \`${escaped}\`;\n\n`;
135
+ // Also export with uppercase name for backwards compatibility
136
+ output += `export const ${prompt.constName} = ${prompt.camelName};\n\n`;
137
+ }
138
+
139
+ fs.writeFileSync(path.join(outputDir, "prompts.ts"), output);
140
+
141
+ // Also generate a simple .d.ts file
142
+ let dts = `/**
143
+ * Auto-generated type definitions for prompts
144
+ */
145
+
146
+ `;
147
+ for (const prompt of prompts) {
148
+ dts += `export declare const ${prompt.camelName}: string;\n`;
149
+ dts += `export declare const ${prompt.constName}: string;\n`;
150
+ }
151
+
152
+ fs.writeFileSync(path.join(outputDir, "prompts.d.ts"), dts);
153
+
154
+ console.log(`Generated TypeScript output: ${outputDir}/prompts.ts`);
155
+ }
156
+
157
+ /**
158
+ * Generate Python output
159
+ */
160
+ function generatePython(prompts, outputBaseDir, sourcePath) {
161
+ const outputDir = path.join(outputBaseDir, "python");
162
+ fs.mkdirSync(outputDir, { recursive: true });
163
+
164
+ const relativeSourcePath = path
165
+ .relative(outputDir, sourcePath)
166
+ .replace(/\\/g, "/");
167
+
168
+ let output = `"""
169
+ Auto-generated prompt templates
170
+ DO NOT EDIT - Generated from ${relativeSourcePath}/*.txt
171
+
172
+ These prompts use Handlebars-style template syntax:
173
+ - {{variableName}} for simple substitution
174
+ - {{#each items}}...{{/each}} for iteration
175
+ - {{#if condition}}...{{/if}} for conditionals
176
+ """
177
+
178
+ from __future__ import annotations
179
+
180
+ `;
181
+
182
+ for (const prompt of prompts) {
183
+ const escaped = escapePython(prompt.content);
184
+ output += `${prompt.constName} = """${escaped}"""\n\n`;
185
+ }
186
+
187
+ // Add __all__ for explicit exports
188
+ output += `__all__ = [\n`;
189
+ for (const prompt of prompts) {
190
+ output += ` "${prompt.constName}",\n`;
191
+ }
192
+ output += `]\n`;
193
+
194
+ fs.writeFileSync(path.join(outputDir, "prompts.py"), output);
195
+
196
+ console.log(`Generated Python output: ${outputDir}/prompts.py`);
197
+
198
+ // Try to copy to Python package directory if it exists
199
+ // Look for python/elizaos_plugin_*/ directory structure
200
+ const pluginRoot = path.resolve(outputBaseDir, "../..");
201
+ const pythonDir = path.join(pluginRoot, "python");
202
+
203
+ if (fs.existsSync(pythonDir)) {
204
+ // Find elizaos_plugin_* directory
205
+ const pythonPkgDirs = fs
206
+ .readdirSync(pythonDir)
207
+ .filter(
208
+ (dir) =>
209
+ dir.startsWith("elizaos_plugin_") &&
210
+ fs.statSync(path.join(pythonDir, dir)).isDirectory(),
211
+ );
212
+
213
+ if (pythonPkgDirs.length > 0) {
214
+ const pythonPkgDir = path.join(pythonDir, pythonPkgDirs[0]);
215
+ const targetFile = path.join(pythonPkgDir, "_generated_prompts.py");
216
+
217
+ // Copy generated prompts to Python package as _generated_prompts.py
218
+ fs.copyFileSync(path.join(outputDir, "prompts.py"), targetFile);
219
+ console.log(`Copied Python prompts to ${targetFile}`);
220
+ }
221
+ }
222
+ }
223
+
224
+ /**
225
+ * Generate Rust output
226
+ */
227
+ function generateRust(prompts, outputBaseDir, sourcePath) {
228
+ const outputDir = path.join(outputBaseDir, "rust");
229
+ fs.mkdirSync(outputDir, { recursive: true });
230
+
231
+ const relativeSourcePath = path
232
+ .relative(outputDir, sourcePath)
233
+ .replace(/\\/g, "/");
234
+
235
+ let output = `//! Auto-generated prompt templates
236
+ //! DO NOT EDIT - Generated from ${relativeSourcePath}/*.txt
237
+ //!
238
+ //! These prompts use Handlebars-style template syntax:
239
+ //! - {{variableName}} for simple substitution
240
+ //! - {{#each items}}...{{/each}} for iteration
241
+ //! - {{#if condition}}...{{/if}} for conditionals
242
+
243
+ `;
244
+
245
+ for (const prompt of prompts) {
246
+ const { content, hashCount } = escapeRust(prompt.content);
247
+ const delimiter = "#".repeat(hashCount);
248
+ output += `pub const ${prompt.constName}: &str = r${delimiter}"${content}"${delimiter};\n\n`;
249
+ }
250
+
251
+ fs.writeFileSync(path.join(outputDir, "prompts.rs"), output);
252
+
253
+ console.log(`Generated Rust output: ${outputDir}/prompts.rs`);
254
+ }
255
+
256
+ /**
257
+ * Main entry point
258
+ */
259
+ function main() {
260
+ const args = process.argv.slice(2);
261
+
262
+ if (args.length < 2) {
263
+ console.error(
264
+ "Usage: generate-plugin-prompts.js <prompts-dir> <output-base-dir> [--target typescript|python|rust|all]",
265
+ );
266
+ process.exit(1);
267
+ }
268
+
269
+ const promptsDir = path.resolve(args[0]);
270
+ const outputBaseDir = path.resolve(args[1]);
271
+ const targetIndex = args.indexOf("--target");
272
+ const target = targetIndex !== -1 ? args[targetIndex + 1] : "all";
273
+
274
+ console.log(`Loading prompts from: ${promptsDir}`);
275
+ const prompts = loadPrompts(promptsDir);
276
+ console.log(`Found ${prompts.length} prompt templates`);
277
+
278
+ if (prompts.length === 0) {
279
+ console.warn("No prompts found. Exiting.");
280
+ return;
281
+ }
282
+
283
+ // Ensure output directory exists
284
+ fs.mkdirSync(outputBaseDir, { recursive: true });
285
+
286
+ switch (target) {
287
+ case "typescript":
288
+ generateTypeScript(prompts, outputBaseDir, promptsDir);
289
+ break;
290
+ case "python":
291
+ generatePython(prompts, outputBaseDir, promptsDir);
292
+ break;
293
+ case "rust":
294
+ generateRust(prompts, outputBaseDir, promptsDir);
295
+ break;
296
+ default:
297
+ generateTypeScript(prompts, outputBaseDir, promptsDir);
298
+ generatePython(prompts, outputBaseDir, promptsDir);
299
+ generateRust(prompts, outputBaseDir, promptsDir);
300
+ break;
301
+ }
302
+
303
+ console.log("Done!");
304
+ }
305
+
306
+ main();
@@ -0,0 +1,279 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Prompt Generator Script
5
+ *
6
+ * Generates native code from .txt prompt templates for:
7
+ * - TypeScript
8
+ * - Python
9
+ * - Rust
10
+ *
11
+ * Usage:
12
+ * node scripts/generate.js # Generate all targets
13
+ * node scripts/generate.js --target typescript
14
+ * node scripts/generate.js --target python
15
+ * node scripts/generate.js --target rust
16
+ */
17
+
18
+ import fs from "node:fs";
19
+ import path from "node:path";
20
+ import { fileURLToPath } from "node:url";
21
+
22
+ const __filename = fileURLToPath(import.meta.url);
23
+ const __dirname = path.dirname(__filename);
24
+ const ROOT_DIR = path.resolve(__dirname, "..");
25
+ const PROMPTS_DIR = path.join(ROOT_DIR, "prompts");
26
+ const DIST_DIR = path.join(ROOT_DIR, "dist");
27
+
28
+ /**
29
+ * Convert filename to constant name
30
+ * e.g., "should_respond.txt" -> "SHOULD_RESPOND_TEMPLATE"
31
+ */
32
+ function fileToConstName(filename) {
33
+ const name = path.basename(filename, ".txt");
34
+ return `${name.toUpperCase().replace(/-/g, "_")}_TEMPLATE`;
35
+ }
36
+
37
+ /**
38
+ * Convert filename to camelCase name for TypeScript exports
39
+ * e.g., "should_respond.txt" -> "shouldRespondTemplate"
40
+ */
41
+ function fileToCamelCase(filename) {
42
+ const name = path.basename(filename, ".txt");
43
+ const parts = name.split("_");
44
+ return (
45
+ parts[0] +
46
+ parts
47
+ .slice(1)
48
+ .map((p) => p.charAt(0).toUpperCase() + p.slice(1))
49
+ .join("") +
50
+ "Template"
51
+ );
52
+ }
53
+
54
+ /**
55
+ * Escape a string for use in TypeScript template literal
56
+ */
57
+ function escapeTypeScript(content) {
58
+ return content
59
+ .replace(/\\/g, "\\\\")
60
+ .replace(/`/g, "\\`")
61
+ .replace(/\$\{/g, "\\${");
62
+ }
63
+
64
+ /**
65
+ * Escape a string for use in Python triple-quoted string
66
+ */
67
+ function escapePython(content) {
68
+ return content.replace(/\\/g, "\\\\").replace(/"""/g, '\\"\\"\\"');
69
+ }
70
+
71
+ /**
72
+ * Escape a string for use in Rust raw string literal
73
+ * Rust raw strings r#"..."# don't need escaping except for the delimiter itself
74
+ */
75
+ function escapeRust(content) {
76
+ // Check if content contains "# - if so, we need more # in our delimiter
77
+ let hashCount = 1;
78
+ while (content.includes(`"${"#".repeat(hashCount)}`)) {
79
+ hashCount++;
80
+ }
81
+ return { content, hashCount };
82
+ }
83
+
84
+ /**
85
+ * Load all prompts from the prompts directory
86
+ */
87
+ function loadPrompts() {
88
+ const prompts = [];
89
+ const files = fs.readdirSync(PROMPTS_DIR);
90
+
91
+ for (const file of files) {
92
+ if (!file.endsWith(".txt")) continue;
93
+
94
+ const filepath = path.join(PROMPTS_DIR, file);
95
+ const content = fs.readFileSync(filepath, "utf-8");
96
+
97
+ prompts.push({
98
+ filename: file,
99
+ constName: fileToConstName(file),
100
+ camelName: fileToCamelCase(file),
101
+ content: content.trim(),
102
+ });
103
+ }
104
+
105
+ return prompts.sort((a, b) => a.constName.localeCompare(b.constName));
106
+ }
107
+
108
+ /**
109
+ * Generate TypeScript output
110
+ */
111
+ function generateTypeScript(prompts) {
112
+ const outputDir = path.join(DIST_DIR, "typescript");
113
+ fs.mkdirSync(outputDir, { recursive: true });
114
+
115
+ let output = `/**
116
+ * Auto-generated prompt templates for elizaOS
117
+ * DO NOT EDIT - Generated from packages/prompts/prompts/*.txt
118
+ *
119
+ * These prompts use Handlebars-style template syntax:
120
+ * - {{variableName}} for simple substitution
121
+ * - {{#each items}}...{{/each}} for iteration
122
+ * - {{#if condition}}...{{/if}} for conditionals
123
+ */
124
+
125
+ `;
126
+
127
+ // Export each prompt as both const name and camelCase
128
+ for (const prompt of prompts) {
129
+ const escaped = escapeTypeScript(prompt.content);
130
+ output += `export const ${prompt.camelName} = \`${escaped}\`;\n\n`;
131
+ // Also export with uppercase name for backwards compatibility
132
+ output += `export const ${prompt.constName} = ${prompt.camelName};\n\n`;
133
+ }
134
+
135
+ // Add a boolean footer export for backwards compatibility
136
+ output += `export const booleanFooter = "Respond with only a YES or a NO.";\n\n`;
137
+ output += `export const BOOLEAN_FOOTER = booleanFooter;\n`;
138
+
139
+ fs.writeFileSync(path.join(outputDir, "index.ts"), output);
140
+
141
+ // Also generate a simple .d.ts file
142
+ let dts = `/**
143
+ * Auto-generated type definitions for elizaOS prompts
144
+ */
145
+
146
+ `;
147
+ for (const prompt of prompts) {
148
+ dts += `export declare const ${prompt.camelName}: string;\n`;
149
+ dts += `export declare const ${prompt.constName}: string;\n`;
150
+ }
151
+ dts += `export declare const booleanFooter: string;\n`;
152
+ dts += `export declare const BOOLEAN_FOOTER: string;\n`;
153
+
154
+ fs.writeFileSync(path.join(outputDir, "index.d.ts"), dts);
155
+
156
+ console.log(`Generated TypeScript output: ${outputDir}/index.ts`);
157
+ }
158
+
159
+ /**
160
+ * Generate Python output
161
+ */
162
+ function generatePython(prompts) {
163
+ const outputDir = path.join(DIST_DIR, "python");
164
+ fs.mkdirSync(outputDir, { recursive: true });
165
+
166
+ let output = `"""
167
+ Auto-generated prompt templates for elizaOS Python runtime.
168
+ DO NOT EDIT - Generated from packages/prompts/prompts/*.txt
169
+
170
+ These prompts use Handlebars-style template syntax:
171
+ - {{variableName}} for simple substitution
172
+ - {{#each items}}...{{/each}} for iteration
173
+ - {{#if condition}}...{{/if}} for conditionals
174
+ """
175
+
176
+ from __future__ import annotations
177
+
178
+ `;
179
+
180
+ for (const prompt of prompts) {
181
+ const escaped = escapePython(prompt.content);
182
+ output += `${prompt.constName} = """${escaped}"""\n\n`;
183
+ }
184
+
185
+ // Add boolean footer
186
+ output += `BOOLEAN_FOOTER = "Respond with only a YES or a NO."\n`;
187
+
188
+ // Add __all__ for explicit exports
189
+ output += `\n__all__ = [\n`;
190
+ for (const prompt of prompts) {
191
+ output += ` "${prompt.constName}",\n`;
192
+ }
193
+ output += ` "BOOLEAN_FOOTER",\n`;
194
+ output += `]\n`;
195
+
196
+ fs.writeFileSync(path.join(outputDir, "prompts.py"), output);
197
+
198
+ // Create __init__.py for package import
199
+ fs.writeFileSync(
200
+ path.join(outputDir, "__init__.py"),
201
+ `"""elizaOS Prompts Package"""\nfrom .prompts import *\n`,
202
+ );
203
+
204
+ console.log(`Generated Python output: ${outputDir}/prompts.py`);
205
+ }
206
+
207
+ /**
208
+ * Generate Rust output
209
+ */
210
+ function generateRust(prompts) {
211
+ const outputDir = path.join(DIST_DIR, "rust");
212
+ fs.mkdirSync(outputDir, { recursive: true });
213
+
214
+ let output = `//! Auto-generated prompt templates for elizaOS Rust runtime.
215
+ //! DO NOT EDIT - Generated from packages/prompts/prompts/*.txt
216
+ //!
217
+ //! These prompts use Handlebars-style template syntax:
218
+ //! - {{variableName}} for simple substitution
219
+ //! - {{#each items}}...{{/each}} for iteration
220
+ //! - {{#if condition}}...{{/if}} for conditionals
221
+
222
+ `;
223
+
224
+ for (const prompt of prompts) {
225
+ const { content, hashCount } = escapeRust(prompt.content);
226
+ const delimiter = "#".repeat(hashCount);
227
+ output += `pub const ${prompt.constName}: &str = r${delimiter}"${content}"${delimiter};\n\n`;
228
+ }
229
+
230
+ // Add boolean footer
231
+ output += `pub const BOOLEAN_FOOTER: &str = "Respond with only a YES or a NO.";\n`;
232
+
233
+ fs.writeFileSync(path.join(outputDir, "prompts.rs"), output);
234
+
235
+ // Also create a mod.rs that re-exports
236
+ fs.writeFileSync(
237
+ path.join(outputDir, "mod.rs"),
238
+ `//! elizaOS Prompts Module\nmod prompts;\npub use prompts::*;\n`,
239
+ );
240
+
241
+ console.log(`Generated Rust output: ${outputDir}/prompts.rs`);
242
+ }
243
+
244
+ /**
245
+ * Main entry point
246
+ */
247
+ function main() {
248
+ const args = process.argv.slice(2);
249
+ const targetIndex = args.indexOf("--target");
250
+ const target = targetIndex !== -1 ? args[targetIndex + 1] : "all";
251
+
252
+ console.log("Loading prompts...");
253
+ const prompts = loadPrompts();
254
+ console.log(`Found ${prompts.length} prompt templates`);
255
+
256
+ // Ensure dist directory exists
257
+ fs.mkdirSync(DIST_DIR, { recursive: true });
258
+
259
+ switch (target) {
260
+ case "typescript":
261
+ generateTypeScript(prompts);
262
+ break;
263
+ case "python":
264
+ generatePython(prompts);
265
+ break;
266
+ case "rust":
267
+ generateRust(prompts);
268
+ break;
269
+ default:
270
+ generateTypeScript(prompts);
271
+ generatePython(prompts);
272
+ generateRust(prompts);
273
+ break;
274
+ }
275
+
276
+ console.log("Done!");
277
+ }
278
+
279
+ main();