@riotprompt/riotprompt 0.0.11 → 0.0.13
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/LICENSE +1 -1
- package/README.md +121 -21
- package/dist/builder.js +3 -0
- package/dist/builder.js.map +1 -1
- package/dist/cli.cjs +1519 -0
- package/dist/cli.d.ts +8 -0
- package/dist/config.d.ts +7 -0
- package/dist/conversation-logger.js +6 -1
- package/dist/conversation-logger.js.map +1 -1
- package/dist/execution/anthropic.d.ts +5 -0
- package/dist/execution/anthropic.js +46 -0
- package/dist/execution/anthropic.js.map +1 -0
- package/dist/execution/gemini.d.ts +5 -0
- package/dist/execution/gemini.js +78 -0
- package/dist/execution/gemini.js.map +1 -0
- package/dist/execution/index.d.ts +10 -0
- package/dist/execution/index.js +53 -0
- package/dist/execution/index.js.map +1 -0
- package/dist/execution/openai.d.ts +5 -0
- package/dist/execution/openai.js +44 -0
- package/dist/execution/openai.js.map +1 -0
- package/dist/execution/provider.d.ts +18 -0
- package/dist/loader.js +3 -0
- package/dist/loader.js.map +1 -1
- package/dist/model-config.d.ts +1 -0
- package/dist/model-config.js +19 -18
- package/dist/model-config.js.map +1 -1
- package/dist/override.js +3 -0
- package/dist/override.js.map +1 -1
- package/dist/recipes.js +3 -0
- package/dist/recipes.js.map +1 -1
- package/dist/riotprompt.cjs +612 -77
- package/dist/riotprompt.cjs.map +1 -1
- package/dist/riotprompt.d.ts +3 -0
- package/dist/riotprompt.js +6 -0
- package/dist/riotprompt.js.map +1 -1
- package/dist/serializer.d.ts +5 -0
- package/dist/serializer.js +220 -0
- package/dist/serializer.js.map +1 -0
- package/dist/writer.d.ts +2 -0
- package/dist/writer.js +91 -0
- package/dist/writer.js.map +1 -0
- package/guide/architecture.md +51 -0
- package/guide/configuration.md +51 -0
- package/guide/development.md +62 -0
- package/guide/index.md +55 -0
- package/guide/usage.md +99 -0
- package/package.json +14 -3
- package/vite.config.cli.ts +49 -0
- package/BUG-ANALYSIS.md +0 -523
- package/CODE-REVIEW-SUMMARY.md +0 -330
- package/FIXES-APPLIED.md +0 -437
package/dist/cli.cjs
ADDED
|
@@ -0,0 +1,1519 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
4
|
+
require("dotenv/config");
|
|
5
|
+
const commander = require("commander");
|
|
6
|
+
const cardigantime = require("@theunwalked/cardigantime");
|
|
7
|
+
const zod = require("zod");
|
|
8
|
+
const fs$1 = require("fs/promises");
|
|
9
|
+
const path = require("path");
|
|
10
|
+
const crypto = require("crypto");
|
|
11
|
+
require("tiktoken");
|
|
12
|
+
const fastXmlParser = require("fast-xml-parser");
|
|
13
|
+
const OpenAI = require("openai");
|
|
14
|
+
const Anthropic = require("@anthropic-ai/sdk");
|
|
15
|
+
const generativeAi = require("@google/generative-ai");
|
|
16
|
+
const fs = require("fs");
|
|
17
|
+
const glob = require("glob");
|
|
18
|
+
function _interopNamespaceDefault(e) {
|
|
19
|
+
const n = Object.create(null, { [Symbol.toStringTag]: { value: "Module" } });
|
|
20
|
+
if (e) {
|
|
21
|
+
for (const k in e) {
|
|
22
|
+
if (k !== "default") {
|
|
23
|
+
const d = Object.getOwnPropertyDescriptor(e, k);
|
|
24
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
25
|
+
enumerable: true,
|
|
26
|
+
get: () => e[k]
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
n.default = e;
|
|
32
|
+
return Object.freeze(n);
|
|
33
|
+
}
|
|
34
|
+
const fs__namespace$1 = /* @__PURE__ */ _interopNamespaceDefault(fs$1);
|
|
35
|
+
const path__namespace = /* @__PURE__ */ _interopNamespaceDefault(path);
|
|
36
|
+
const fs__namespace = /* @__PURE__ */ _interopNamespaceDefault(fs);
|
|
37
|
+
const ConfigSchema = zod.z.object({
|
|
38
|
+
defaultModel: zod.z.string().default("gpt-4").describe("Default model to use for formatting"),
|
|
39
|
+
promptsDir: zod.z.string().default(".").describe("Directory containing prompts"),
|
|
40
|
+
outputDir: zod.z.string().optional().describe("Directory to output formatted prompts")
|
|
41
|
+
});
|
|
42
|
+
const ParametersSchema = zod.z.record(zod.z.string(), zod.z.union([zod.z.string(), zod.z.number(), zod.z.boolean(), zod.z.array(zod.z.union([zod.z.string(), zod.z.number(), zod.z.boolean()]))]));
|
|
43
|
+
const apply = (text, parameters) => {
|
|
44
|
+
if (!parameters) {
|
|
45
|
+
return text;
|
|
46
|
+
}
|
|
47
|
+
const trimmedParams = {};
|
|
48
|
+
Object.keys(parameters).forEach((key) => {
|
|
49
|
+
trimmedParams[key.trim()] = parameters[key];
|
|
50
|
+
});
|
|
51
|
+
return text.replace(/\{\{([^{}]+)\}\}/g, (match, p1) => {
|
|
52
|
+
const paramKey = p1.trim();
|
|
53
|
+
const parameter = trimmedParams[paramKey];
|
|
54
|
+
if (parameter === void 0) {
|
|
55
|
+
return match;
|
|
56
|
+
} else if (typeof parameter === "string") {
|
|
57
|
+
return parameter;
|
|
58
|
+
} else if (typeof parameter === "number") {
|
|
59
|
+
return parameter.toString();
|
|
60
|
+
} else if (typeof parameter === "boolean") {
|
|
61
|
+
return parameter.toString();
|
|
62
|
+
} else if (Array.isArray(parameter)) {
|
|
63
|
+
return parameter.join(", ");
|
|
64
|
+
} else {
|
|
65
|
+
return match;
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
};
|
|
69
|
+
zod.z.object({
|
|
70
|
+
text: zod.z.string(),
|
|
71
|
+
weight: zod.z.number().optional()
|
|
72
|
+
});
|
|
73
|
+
const WeightedOptionsSchema = zod.z.object({
|
|
74
|
+
weight: zod.z.number().optional(),
|
|
75
|
+
parameters: ParametersSchema.optional()
|
|
76
|
+
});
|
|
77
|
+
const create$6 = (text, options = {}) => {
|
|
78
|
+
const weightedOptions = WeightedOptionsSchema.parse(options);
|
|
79
|
+
const parameterizedText = apply(text, weightedOptions.parameters);
|
|
80
|
+
return {
|
|
81
|
+
text: parameterizedText,
|
|
82
|
+
weight: weightedOptions.weight
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
const create$5 = (text, options = {}) => {
|
|
86
|
+
const weightedOptions = WeightedOptionsSchema.parse(options);
|
|
87
|
+
return create$6(text, weightedOptions);
|
|
88
|
+
};
|
|
89
|
+
const SectionOptionsSchema = zod.z.object({
|
|
90
|
+
title: zod.z.string().optional(),
|
|
91
|
+
weight: zod.z.number().optional(),
|
|
92
|
+
itemWeight: zod.z.number().optional(),
|
|
93
|
+
parameters: ParametersSchema.optional().default({})
|
|
94
|
+
});
|
|
95
|
+
const create$4 = (options = {}) => {
|
|
96
|
+
const items = [];
|
|
97
|
+
const sectionOptions = SectionOptionsSchema.parse(options);
|
|
98
|
+
const sectionItemOptions = WeightedOptionsSchema.parse({
|
|
99
|
+
...sectionOptions,
|
|
100
|
+
weight: sectionOptions.itemWeight
|
|
101
|
+
});
|
|
102
|
+
const append = (item, options2 = {}) => {
|
|
103
|
+
let itemOptions = WeightedOptionsSchema.parse(options2);
|
|
104
|
+
itemOptions = { ...sectionItemOptions, ...itemOptions };
|
|
105
|
+
if (Array.isArray(item)) {
|
|
106
|
+
item.forEach((item2) => {
|
|
107
|
+
append(item2, options2);
|
|
108
|
+
});
|
|
109
|
+
} else {
|
|
110
|
+
if (typeof item === "string") {
|
|
111
|
+
items.push(create$6(item, itemOptions));
|
|
112
|
+
} else {
|
|
113
|
+
items.push(item);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return section;
|
|
117
|
+
};
|
|
118
|
+
const prepend = (item, options2 = {}) => {
|
|
119
|
+
let itemOptions = WeightedOptionsSchema.parse(options2);
|
|
120
|
+
itemOptions = { ...sectionItemOptions, ...itemOptions };
|
|
121
|
+
if (Array.isArray(item)) {
|
|
122
|
+
item.forEach((item2) => {
|
|
123
|
+
prepend(item2, options2);
|
|
124
|
+
});
|
|
125
|
+
} else {
|
|
126
|
+
if (typeof item === "string") {
|
|
127
|
+
items.unshift(create$6(item, itemOptions));
|
|
128
|
+
} else {
|
|
129
|
+
items.unshift(item);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return section;
|
|
133
|
+
};
|
|
134
|
+
const insert = (index, item, options2 = {}) => {
|
|
135
|
+
let itemOptions = WeightedOptionsSchema.parse(options2);
|
|
136
|
+
itemOptions = { ...sectionItemOptions, ...itemOptions };
|
|
137
|
+
if (Array.isArray(item)) {
|
|
138
|
+
item.forEach((item2) => {
|
|
139
|
+
insert(index, item2, options2);
|
|
140
|
+
});
|
|
141
|
+
} else {
|
|
142
|
+
if (typeof item === "string") {
|
|
143
|
+
items.splice(index, 0, create$6(item, itemOptions));
|
|
144
|
+
} else {
|
|
145
|
+
items.splice(index, 0, item);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return section;
|
|
149
|
+
};
|
|
150
|
+
const remove = (index) => {
|
|
151
|
+
items.splice(index, 1);
|
|
152
|
+
return section;
|
|
153
|
+
};
|
|
154
|
+
const replace = (index, item, options2 = {}) => {
|
|
155
|
+
let itemOptions = WeightedOptionsSchema.parse(options2);
|
|
156
|
+
itemOptions = { ...sectionItemOptions, ...itemOptions };
|
|
157
|
+
if (typeof item === "string") {
|
|
158
|
+
items[index] = create$6(item, itemOptions);
|
|
159
|
+
} else {
|
|
160
|
+
items[index] = item;
|
|
161
|
+
}
|
|
162
|
+
return section;
|
|
163
|
+
};
|
|
164
|
+
const add = (item, options2 = {}) => {
|
|
165
|
+
let itemOptions = WeightedOptionsSchema.parse(options2);
|
|
166
|
+
itemOptions = { ...sectionItemOptions, ...itemOptions };
|
|
167
|
+
return append(item, itemOptions);
|
|
168
|
+
};
|
|
169
|
+
const toJSON2 = () => {
|
|
170
|
+
return {
|
|
171
|
+
title: section.title,
|
|
172
|
+
items: items.map((item) => {
|
|
173
|
+
return typeof item === "object" && item !== null && "toJSON" in item && typeof item.toJSON === "function" ? item.toJSON() : item;
|
|
174
|
+
}),
|
|
175
|
+
weight: section.weight
|
|
176
|
+
};
|
|
177
|
+
};
|
|
178
|
+
const section = {
|
|
179
|
+
title: sectionOptions.title,
|
|
180
|
+
items,
|
|
181
|
+
weight: sectionOptions.weight,
|
|
182
|
+
add,
|
|
183
|
+
append,
|
|
184
|
+
prepend,
|
|
185
|
+
insert,
|
|
186
|
+
remove,
|
|
187
|
+
replace,
|
|
188
|
+
toJSON: toJSON2
|
|
189
|
+
};
|
|
190
|
+
return section;
|
|
191
|
+
};
|
|
192
|
+
const create$3 = ({
|
|
193
|
+
persona,
|
|
194
|
+
instructions,
|
|
195
|
+
contents,
|
|
196
|
+
contexts
|
|
197
|
+
}) => {
|
|
198
|
+
return {
|
|
199
|
+
persona,
|
|
200
|
+
instructions,
|
|
201
|
+
contents,
|
|
202
|
+
contexts
|
|
203
|
+
};
|
|
204
|
+
};
|
|
205
|
+
const LIBRARY_NAME = "riotprompt";
|
|
206
|
+
const DEFAULT_IGNORE_PATTERNS = [
|
|
207
|
+
"^\\..*",
|
|
208
|
+
// Hidden files (e.g., .git, .DS_Store)
|
|
209
|
+
"\\.(jpg|jpeg|png|gif|bmp|svg|webp|ico)$",
|
|
210
|
+
// Image files
|
|
211
|
+
"\\.(mp3|wav|ogg|aac|flac)$",
|
|
212
|
+
// Audio files
|
|
213
|
+
"\\.(mp4|mov|avi|mkv|webm)$",
|
|
214
|
+
// Video files
|
|
215
|
+
"\\.(pdf|doc|docx|xls|xlsx|ppt|pptx)$",
|
|
216
|
+
// Document files
|
|
217
|
+
"\\.(zip|tar|gz|rar|7z)$"
|
|
218
|
+
// Compressed files
|
|
219
|
+
];
|
|
220
|
+
const DEFAULT_SECTION_SEPARATOR = "tag";
|
|
221
|
+
const DEFAULT_SECTION_INDENTATION = true;
|
|
222
|
+
const DEFAULT_SECTION_TITLE_PROPERTY = "title";
|
|
223
|
+
const DEFAULT_FORMAT_OPTIONS = {
|
|
224
|
+
sectionSeparator: DEFAULT_SECTION_SEPARATOR,
|
|
225
|
+
sectionIndentation: DEFAULT_SECTION_INDENTATION,
|
|
226
|
+
sectionTitleProperty: DEFAULT_SECTION_TITLE_PROPERTY,
|
|
227
|
+
sectionDepth: 0
|
|
228
|
+
};
|
|
229
|
+
const DEFAULT_LOGGER = {
|
|
230
|
+
name: "default",
|
|
231
|
+
debug: (message, ...args) => console.debug(message, ...args),
|
|
232
|
+
info: (message, ...args) => console.info(message, ...args),
|
|
233
|
+
warn: (message, ...args) => console.warn(message, ...args),
|
|
234
|
+
error: (message, ...args) => console.error(message, ...args),
|
|
235
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
236
|
+
verbose: (message, ...args) => {
|
|
237
|
+
},
|
|
238
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
239
|
+
silly: (message, ...args) => {
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
const wrapLogger = (toWrap, name) => {
|
|
243
|
+
const requiredMethods = ["debug", "info", "warn", "error", "verbose", "silly"];
|
|
244
|
+
const missingMethods = requiredMethods.filter((method) => typeof toWrap[method] !== "function");
|
|
245
|
+
if (missingMethods.length > 0) {
|
|
246
|
+
throw new Error(`Logger is missing required methods: ${missingMethods.join(", ")}`);
|
|
247
|
+
}
|
|
248
|
+
const log = (level, message, ...args) => {
|
|
249
|
+
message = `[${LIBRARY_NAME}] ${name ? `[${name}]` : ""}: ${message}`;
|
|
250
|
+
if (level === "debug") toWrap.debug(message, ...args);
|
|
251
|
+
else if (level === "info") toWrap.info(message, ...args);
|
|
252
|
+
else if (level === "warn") toWrap.warn(message, ...args);
|
|
253
|
+
else if (level === "error") toWrap.error(message, ...args);
|
|
254
|
+
else if (level === "verbose") toWrap.verbose(message, ...args);
|
|
255
|
+
else if (level === "silly") toWrap.silly(message, ...args);
|
|
256
|
+
};
|
|
257
|
+
return {
|
|
258
|
+
name: "wrapped",
|
|
259
|
+
debug: (message, ...args) => log("debug", message, ...args),
|
|
260
|
+
info: (message, ...args) => log("info", message, ...args),
|
|
261
|
+
warn: (message, ...args) => log("warn", message, ...args),
|
|
262
|
+
error: (message, ...args) => log("error", message, ...args),
|
|
263
|
+
verbose: (message, ...args) => log("verbose", message, ...args),
|
|
264
|
+
silly: (message, ...args) => log("silly", message, ...args)
|
|
265
|
+
};
|
|
266
|
+
};
|
|
267
|
+
class ModelRegistry {
|
|
268
|
+
configs;
|
|
269
|
+
cache;
|
|
270
|
+
logger;
|
|
271
|
+
constructor(logger) {
|
|
272
|
+
this.configs = [];
|
|
273
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
274
|
+
this.logger = wrapLogger(logger || DEFAULT_LOGGER, "ModelRegistry");
|
|
275
|
+
this.registerDefaults();
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Register default model configurations
|
|
279
|
+
*/
|
|
280
|
+
registerDefaults() {
|
|
281
|
+
this.register({
|
|
282
|
+
pattern: /.*/,
|
|
283
|
+
// Matches anything
|
|
284
|
+
personaRole: "system",
|
|
285
|
+
encoding: "gpt-4o",
|
|
286
|
+
supportsToolCalls: true,
|
|
287
|
+
family: "unknown",
|
|
288
|
+
description: "Default fallback configuration"
|
|
289
|
+
});
|
|
290
|
+
this.register({
|
|
291
|
+
pattern: /^claude/i,
|
|
292
|
+
personaRole: "system",
|
|
293
|
+
encoding: "cl100k_base",
|
|
294
|
+
supportsToolCalls: true,
|
|
295
|
+
family: "claude",
|
|
296
|
+
description: "Claude family models"
|
|
297
|
+
});
|
|
298
|
+
this.register({
|
|
299
|
+
pattern: /^o\d+/i,
|
|
300
|
+
// Matches o1, o2, o3, o4, etc.
|
|
301
|
+
personaRole: "developer",
|
|
302
|
+
encoding: "gpt-4o",
|
|
303
|
+
supportsToolCalls: true,
|
|
304
|
+
family: "o-series",
|
|
305
|
+
description: "O-series reasoning models"
|
|
306
|
+
});
|
|
307
|
+
this.register({
|
|
308
|
+
pattern: /^gpt-4/i,
|
|
309
|
+
personaRole: "system",
|
|
310
|
+
encoding: "gpt-4o",
|
|
311
|
+
supportsToolCalls: true,
|
|
312
|
+
family: "gpt-4",
|
|
313
|
+
description: "GPT-4 family models"
|
|
314
|
+
});
|
|
315
|
+
this.logger.debug("Registered default model configurations");
|
|
316
|
+
}
|
|
317
|
+
/**
|
|
318
|
+
* Register a model configuration
|
|
319
|
+
* Configs are checked in registration order (first match wins)
|
|
320
|
+
* New configs are added to the beginning of the list (high priority)
|
|
321
|
+
*/
|
|
322
|
+
register(config) {
|
|
323
|
+
if (!config.pattern && !config.exactMatch) {
|
|
324
|
+
throw new Error("Model config must have either pattern or exactMatch");
|
|
325
|
+
}
|
|
326
|
+
this.configs.unshift(config);
|
|
327
|
+
this.cache.clear();
|
|
328
|
+
this.logger.debug("Registered model config", {
|
|
329
|
+
family: config.family,
|
|
330
|
+
pattern: config.pattern?.source,
|
|
331
|
+
exactMatch: config.exactMatch
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Get configuration for a model
|
|
336
|
+
*/
|
|
337
|
+
getConfig(model) {
|
|
338
|
+
if (this.cache.has(model)) {
|
|
339
|
+
return this.cache.get(model);
|
|
340
|
+
}
|
|
341
|
+
for (const config of this.configs) {
|
|
342
|
+
if (config.exactMatch && config.exactMatch === model) {
|
|
343
|
+
this.cache.set(model, config);
|
|
344
|
+
return config;
|
|
345
|
+
}
|
|
346
|
+
if (config.pattern && config.pattern.test(model)) {
|
|
347
|
+
this.cache.set(model, config);
|
|
348
|
+
return config;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
throw new Error(`No configuration found for model: ${model}`);
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Get persona role for a model
|
|
355
|
+
*/
|
|
356
|
+
getPersonaRole(model) {
|
|
357
|
+
return this.getConfig(model).personaRole;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Get tokenizer encoding for a model
|
|
361
|
+
*/
|
|
362
|
+
getEncoding(model) {
|
|
363
|
+
return this.getConfig(model).encoding;
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Check if model supports tool calls
|
|
367
|
+
*/
|
|
368
|
+
supportsToolCalls(model) {
|
|
369
|
+
return this.getConfig(model).supportsToolCalls ?? true;
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Get model family
|
|
373
|
+
*/
|
|
374
|
+
getFamily(model) {
|
|
375
|
+
return this.getConfig(model).family;
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Clear all registered configs and reset to defaults
|
|
379
|
+
*/
|
|
380
|
+
reset() {
|
|
381
|
+
this.configs = [];
|
|
382
|
+
this.cache.clear();
|
|
383
|
+
this.registerDefaults();
|
|
384
|
+
this.logger.debug("Reset model configurations to defaults");
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Clear cache (useful if configs are modified)
|
|
388
|
+
*/
|
|
389
|
+
clearCache() {
|
|
390
|
+
this.cache.clear();
|
|
391
|
+
this.logger.debug("Cleared model configuration cache");
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Get all registered configurations
|
|
395
|
+
*/
|
|
396
|
+
getAllConfigs() {
|
|
397
|
+
return [...this.configs];
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
let globalRegistry = null;
|
|
401
|
+
function getModelRegistry(logger) {
|
|
402
|
+
if (!globalRegistry) {
|
|
403
|
+
globalRegistry = new ModelRegistry(logger);
|
|
404
|
+
}
|
|
405
|
+
return globalRegistry;
|
|
406
|
+
}
|
|
407
|
+
function getPersonaRole$1(model) {
|
|
408
|
+
return getModelRegistry().getPersonaRole(model);
|
|
409
|
+
}
|
|
410
|
+
const getPersonaRole = (model) => {
|
|
411
|
+
return getPersonaRole$1(model);
|
|
412
|
+
};
|
|
413
|
+
const createRequest = (model) => {
|
|
414
|
+
const messages = [];
|
|
415
|
+
return {
|
|
416
|
+
model,
|
|
417
|
+
messages,
|
|
418
|
+
addMessage: (message) => {
|
|
419
|
+
messages.push(message);
|
|
420
|
+
}
|
|
421
|
+
};
|
|
422
|
+
};
|
|
423
|
+
const clean = (obj) => {
|
|
424
|
+
return Object.fromEntries(
|
|
425
|
+
Object.entries(obj).filter(([_, v]) => v !== void 0)
|
|
426
|
+
);
|
|
427
|
+
};
|
|
428
|
+
const stringifyJSON = function(obj, visited = /* @__PURE__ */ new Set()) {
|
|
429
|
+
const arrOfKeyVals = [];
|
|
430
|
+
const arrVals = [];
|
|
431
|
+
let objKeys = [];
|
|
432
|
+
if (typeof obj === "number" || typeof obj === "boolean" || obj === null)
|
|
433
|
+
return "" + obj;
|
|
434
|
+
else if (typeof obj === "string")
|
|
435
|
+
return '"' + obj + '"';
|
|
436
|
+
if (obj instanceof Object && visited.has(obj)) {
|
|
437
|
+
return '"(circular)"';
|
|
438
|
+
} else if (Array.isArray(obj)) {
|
|
439
|
+
if (obj.length === 0)
|
|
440
|
+
return "[]";
|
|
441
|
+
else {
|
|
442
|
+
visited.add(obj);
|
|
443
|
+
obj.forEach(function(el) {
|
|
444
|
+
arrVals.push(stringifyJSON(el, visited));
|
|
445
|
+
});
|
|
446
|
+
return "[" + arrVals + "]";
|
|
447
|
+
}
|
|
448
|
+
} else if (obj instanceof Object) {
|
|
449
|
+
visited.add(obj);
|
|
450
|
+
objKeys = Object.keys(obj);
|
|
451
|
+
objKeys.forEach(function(key) {
|
|
452
|
+
const keyOut = '"' + key + '":';
|
|
453
|
+
const keyValOut = obj[key];
|
|
454
|
+
if (keyValOut instanceof Function || keyValOut === void 0)
|
|
455
|
+
return;
|
|
456
|
+
else if (typeof keyValOut === "string")
|
|
457
|
+
arrOfKeyVals.push(keyOut + '"' + keyValOut + '"');
|
|
458
|
+
else if (typeof keyValOut === "boolean" || typeof keyValOut === "number" || keyValOut === null)
|
|
459
|
+
arrOfKeyVals.push(keyOut + keyValOut);
|
|
460
|
+
else if (keyValOut instanceof Object) {
|
|
461
|
+
arrOfKeyVals.push(keyOut + stringifyJSON(keyValOut, visited));
|
|
462
|
+
}
|
|
463
|
+
});
|
|
464
|
+
return "{" + arrOfKeyVals + "}";
|
|
465
|
+
}
|
|
466
|
+
return "";
|
|
467
|
+
};
|
|
468
|
+
const SectionSeparatorSchema = zod.z.enum(["tag", "markdown"]);
|
|
469
|
+
const SectionTitlePropertySchema = zod.z.enum(["title", "name"]);
|
|
470
|
+
const FormatOptionsSchema = zod.z.object({
|
|
471
|
+
sectionSeparator: SectionSeparatorSchema,
|
|
472
|
+
sectionIndentation: zod.z.boolean(),
|
|
473
|
+
sectionTitleProperty: SectionTitlePropertySchema,
|
|
474
|
+
sectionTitlePrefix: zod.z.string().optional(),
|
|
475
|
+
sectionTitleSeparator: zod.z.string().optional(),
|
|
476
|
+
sectionDepth: zod.z.number().default(0)
|
|
477
|
+
});
|
|
478
|
+
const OptionSchema = zod.z.object({
|
|
479
|
+
logger: zod.z.any().optional().default(DEFAULT_LOGGER),
|
|
480
|
+
formatOptions: FormatOptionsSchema.partial().optional().default(DEFAULT_FORMAT_OPTIONS)
|
|
481
|
+
});
|
|
482
|
+
function isSection(obj) {
|
|
483
|
+
return obj && typeof obj === "object" && "items" in obj && Array.isArray(obj.items);
|
|
484
|
+
}
|
|
485
|
+
function isWeighted(obj) {
|
|
486
|
+
return obj && typeof obj === "object" && "text" in obj;
|
|
487
|
+
}
|
|
488
|
+
const create$2 = (formatterOptions) => {
|
|
489
|
+
const options = OptionSchema.parse({});
|
|
490
|
+
const logger = wrapLogger(options.logger, "Formatter");
|
|
491
|
+
let formatOptions = DEFAULT_FORMAT_OPTIONS;
|
|
492
|
+
if (options?.formatOptions) {
|
|
493
|
+
formatOptions = {
|
|
494
|
+
...formatOptions,
|
|
495
|
+
...clean(options.formatOptions)
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
const formatPersona = (model, persona) => {
|
|
499
|
+
logger.silly(`Formatting persona`);
|
|
500
|
+
if (persona) {
|
|
501
|
+
const formattedPersona = formatSection(persona);
|
|
502
|
+
return {
|
|
503
|
+
role: getPersonaRole(model),
|
|
504
|
+
content: `${formattedPersona}`
|
|
505
|
+
};
|
|
506
|
+
} else {
|
|
507
|
+
throw new Error("Persona is required");
|
|
508
|
+
}
|
|
509
|
+
};
|
|
510
|
+
const format = (item, sectionDepth) => {
|
|
511
|
+
logger.silly(`Formatting ${isSection(item) ? "section" : "item"} Item: %s`, stringifyJSON(item));
|
|
512
|
+
const currentSectionDepth = sectionDepth ?? formatOptions.sectionDepth;
|
|
513
|
+
logger.silly(` Current section depth: ${currentSectionDepth}`);
|
|
514
|
+
let result = "";
|
|
515
|
+
if (isSection(item)) {
|
|
516
|
+
result = formatSection(item, currentSectionDepth + 1);
|
|
517
|
+
} else if (isWeighted(item)) {
|
|
518
|
+
result = item.text;
|
|
519
|
+
} else {
|
|
520
|
+
result = "";
|
|
521
|
+
}
|
|
522
|
+
return result;
|
|
523
|
+
};
|
|
524
|
+
const formatSection = (section, sectionDepth) => {
|
|
525
|
+
logger.silly(`Formatting section`);
|
|
526
|
+
const currentSectionDepth = sectionDepth ?? formatOptions.sectionDepth;
|
|
527
|
+
logger.silly(` Current section depth: ${currentSectionDepth}`);
|
|
528
|
+
if (section) {
|
|
529
|
+
const formattedItems = section.items.map((item) => format(item, currentSectionDepth)).join("\n\n");
|
|
530
|
+
if (formatOptions.sectionSeparator === "tag") {
|
|
531
|
+
return `<${section.title ?? "section"}>
|
|
532
|
+
${formattedItems}
|
|
533
|
+
</${section.title ?? "section"}>`;
|
|
534
|
+
} else {
|
|
535
|
+
const headingLevel = currentSectionDepth;
|
|
536
|
+
const hashes = "#".repeat(headingLevel);
|
|
537
|
+
logger.silly(` Heading level: ${headingLevel}`);
|
|
538
|
+
logger.silly(` Section title: ${section.title}`);
|
|
539
|
+
return `${hashes} ${formatOptions.sectionTitlePrefix ? `${formatOptions.sectionTitlePrefix} ${formatOptions.sectionTitleSeparator} ` : ""}${section.title}
|
|
540
|
+
|
|
541
|
+
${formattedItems}`;
|
|
542
|
+
}
|
|
543
|
+
} else {
|
|
544
|
+
return "";
|
|
545
|
+
}
|
|
546
|
+
};
|
|
547
|
+
const formatArray = (items, sectionDepth) => {
|
|
548
|
+
logger.silly(`Formatting array`);
|
|
549
|
+
const currentSectionDepth = sectionDepth ?? formatOptions.sectionDepth;
|
|
550
|
+
return items.map((item) => format(item, currentSectionDepth)).join("\n\n");
|
|
551
|
+
};
|
|
552
|
+
const formatPrompt = (model, prompt) => {
|
|
553
|
+
logger.silly("Formatting prompt");
|
|
554
|
+
const chatRequest = createRequest(model);
|
|
555
|
+
if (prompt.persona) {
|
|
556
|
+
[prompt.persona].forEach((persona) => {
|
|
557
|
+
chatRequest.addMessage(formatPersona(model, persona));
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
let formattedAreas = formatSection(prompt.instructions) + "\n\n";
|
|
561
|
+
if (prompt.contents) {
|
|
562
|
+
formattedAreas += formatSection(prompt.contents) + "\n\n";
|
|
563
|
+
}
|
|
564
|
+
if (prompt.contexts) {
|
|
565
|
+
formattedAreas += formatSection(prompt.contexts) + "\n\n";
|
|
566
|
+
}
|
|
567
|
+
chatRequest.addMessage({
|
|
568
|
+
role: "user",
|
|
569
|
+
content: formattedAreas
|
|
570
|
+
});
|
|
571
|
+
return chatRequest;
|
|
572
|
+
};
|
|
573
|
+
return {
|
|
574
|
+
formatPersona,
|
|
575
|
+
format,
|
|
576
|
+
formatPrompt,
|
|
577
|
+
formatArray
|
|
578
|
+
};
|
|
579
|
+
};
|
|
580
|
+
zod.z.object({
|
|
581
|
+
logger: zod.z.any().optional().default(DEFAULT_LOGGER),
|
|
582
|
+
parameters: ParametersSchema.optional().default({})
|
|
583
|
+
});
|
|
584
|
+
const create$1 = (params) => {
|
|
585
|
+
const log = params.log || console.log;
|
|
586
|
+
const exists = async (path2) => {
|
|
587
|
+
try {
|
|
588
|
+
await fs__namespace.promises.stat(path2);
|
|
589
|
+
return true;
|
|
590
|
+
} catch (error) {
|
|
591
|
+
return false;
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
const isDirectory2 = async (path2) => {
|
|
595
|
+
const stats = await fs__namespace.promises.stat(path2);
|
|
596
|
+
if (!stats.isDirectory()) {
|
|
597
|
+
log(`${path2} is not a directory`);
|
|
598
|
+
return false;
|
|
599
|
+
}
|
|
600
|
+
return true;
|
|
601
|
+
};
|
|
602
|
+
const isFile = async (path2) => {
|
|
603
|
+
const stats = await fs__namespace.promises.stat(path2);
|
|
604
|
+
if (!stats.isFile()) {
|
|
605
|
+
log(`${path2} is not a file`);
|
|
606
|
+
return false;
|
|
607
|
+
}
|
|
608
|
+
return true;
|
|
609
|
+
};
|
|
610
|
+
const isReadable = async (path2) => {
|
|
611
|
+
try {
|
|
612
|
+
await fs__namespace.promises.access(path2, fs__namespace.constants.R_OK);
|
|
613
|
+
} catch (error) {
|
|
614
|
+
log(`${path2} is not readable: %s %s`, error.message, error.stack);
|
|
615
|
+
return false;
|
|
616
|
+
}
|
|
617
|
+
return true;
|
|
618
|
+
};
|
|
619
|
+
const isWritable = async (path2) => {
|
|
620
|
+
try {
|
|
621
|
+
await fs__namespace.promises.access(path2, fs__namespace.constants.W_OK);
|
|
622
|
+
} catch (error) {
|
|
623
|
+
log(`${path2} is not writable: %s %s`, error.message, error.stack);
|
|
624
|
+
return false;
|
|
625
|
+
}
|
|
626
|
+
return true;
|
|
627
|
+
};
|
|
628
|
+
const isFileReadable = async (path2) => {
|
|
629
|
+
return await exists(path2) && await isFile(path2) && await isReadable(path2);
|
|
630
|
+
};
|
|
631
|
+
const isDirectoryWritable = async (path2) => {
|
|
632
|
+
return await exists(path2) && await isDirectory2(path2) && await isWritable(path2);
|
|
633
|
+
};
|
|
634
|
+
const isDirectoryReadable = async (path2) => {
|
|
635
|
+
return await exists(path2) && await isDirectory2(path2) && await isReadable(path2);
|
|
636
|
+
};
|
|
637
|
+
const createDirectory = async (path2) => {
|
|
638
|
+
try {
|
|
639
|
+
await fs__namespace.promises.mkdir(path2, { recursive: true });
|
|
640
|
+
} catch (mkdirError) {
|
|
641
|
+
throw new Error(`Failed to create output directory ${path2}: ${mkdirError.message} ${mkdirError.stack}`);
|
|
642
|
+
}
|
|
643
|
+
};
|
|
644
|
+
const readFile = async (path2, encoding) => {
|
|
645
|
+
return await fs__namespace.promises.readFile(path2, { encoding });
|
|
646
|
+
};
|
|
647
|
+
const writeFile = async (path2, data, encoding) => {
|
|
648
|
+
await fs__namespace.promises.writeFile(path2, data, { encoding });
|
|
649
|
+
};
|
|
650
|
+
const forEachFileIn = async (directory, callback, options = { pattern: "*.*" }) => {
|
|
651
|
+
try {
|
|
652
|
+
let filesProcessed = 0;
|
|
653
|
+
const files = await glob.glob(options.pattern, { cwd: directory, nodir: true });
|
|
654
|
+
for (const file of files) {
|
|
655
|
+
await callback(path.join(directory, file));
|
|
656
|
+
filesProcessed++;
|
|
657
|
+
if (options.limit && filesProcessed >= options.limit) {
|
|
658
|
+
log(`Reached limit of ${options.limit} files, stopping`);
|
|
659
|
+
break;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
} catch (err) {
|
|
663
|
+
throw new Error(`Failed to glob pattern ${options.pattern} in ${directory}: ${err.message}`);
|
|
664
|
+
}
|
|
665
|
+
};
|
|
666
|
+
const readStream = async (path2) => {
|
|
667
|
+
return fs__namespace.createReadStream(path2);
|
|
668
|
+
};
|
|
669
|
+
const hashFile = async (path2, length) => {
|
|
670
|
+
const file = await readFile(path2, "utf8");
|
|
671
|
+
return crypto.createHash("sha256").update(file).digest("hex").slice(0, length);
|
|
672
|
+
};
|
|
673
|
+
const listFiles = async (directory) => {
|
|
674
|
+
return await fs__namespace.promises.readdir(directory);
|
|
675
|
+
};
|
|
676
|
+
return {
|
|
677
|
+
exists,
|
|
678
|
+
isDirectory: isDirectory2,
|
|
679
|
+
isFile,
|
|
680
|
+
isReadable,
|
|
681
|
+
isWritable,
|
|
682
|
+
isFileReadable,
|
|
683
|
+
isDirectoryWritable,
|
|
684
|
+
isDirectoryReadable,
|
|
685
|
+
createDirectory,
|
|
686
|
+
readFile,
|
|
687
|
+
readStream,
|
|
688
|
+
writeFile,
|
|
689
|
+
forEachFileIn,
|
|
690
|
+
hashFile,
|
|
691
|
+
listFiles
|
|
692
|
+
};
|
|
693
|
+
};
|
|
694
|
+
const OptionsSchema = zod.z.object({
|
|
695
|
+
logger: zod.z.any().optional().default(DEFAULT_LOGGER),
|
|
696
|
+
ignorePatterns: zod.z.array(zod.z.string()).optional().default(DEFAULT_IGNORE_PATTERNS),
|
|
697
|
+
parameters: ParametersSchema.optional().default({})
|
|
698
|
+
});
|
|
699
|
+
function extractFirstHeader(markdownText) {
|
|
700
|
+
const headerRegex = /^(#{1,6})\s+(.+?)(?:\n|$)/m;
|
|
701
|
+
const match = markdownText.match(headerRegex);
|
|
702
|
+
if (match && match[2]) {
|
|
703
|
+
return match[2].trim();
|
|
704
|
+
}
|
|
705
|
+
return null;
|
|
706
|
+
}
|
|
707
|
+
function removeFirstHeader(markdownText) {
|
|
708
|
+
const headerRegex = /^(#{1,6})\s+(.+?)(?:\n|$)/m;
|
|
709
|
+
const match = markdownText.match(headerRegex);
|
|
710
|
+
if (match) {
|
|
711
|
+
return markdownText.replace(headerRegex, "").trim();
|
|
712
|
+
}
|
|
713
|
+
return markdownText;
|
|
714
|
+
}
|
|
715
|
+
const create = (loaderOptions) => {
|
|
716
|
+
const options = OptionsSchema.parse({});
|
|
717
|
+
const parameters = options.parameters;
|
|
718
|
+
const logger = wrapLogger(options.logger, "Loader");
|
|
719
|
+
const ignorePatterns = options.ignorePatterns;
|
|
720
|
+
const loadOptions = (sectionOptions = {}) => {
|
|
721
|
+
const currentOptions = SectionOptionsSchema.parse(sectionOptions);
|
|
722
|
+
return {
|
|
723
|
+
...currentOptions,
|
|
724
|
+
parameters: {
|
|
725
|
+
...parameters,
|
|
726
|
+
...currentOptions.parameters
|
|
727
|
+
}
|
|
728
|
+
};
|
|
729
|
+
};
|
|
730
|
+
const load = async (contextDirectories = [], options2 = {}) => {
|
|
731
|
+
const sectionOptions = loadOptions(options2);
|
|
732
|
+
logger.debug(`Loading context from ${contextDirectories}`);
|
|
733
|
+
const contextSections = [];
|
|
734
|
+
if (!contextDirectories || contextDirectories.length === 0) {
|
|
735
|
+
logger.debug(`No context directories provided, returning empty context`);
|
|
736
|
+
return contextSections;
|
|
737
|
+
}
|
|
738
|
+
const storage = create$1({ log: logger.debug });
|
|
739
|
+
for (const contextDir of contextDirectories) {
|
|
740
|
+
try {
|
|
741
|
+
const dirName = path.basename(contextDir);
|
|
742
|
+
logger.debug(`Processing context directory ${dirName}`);
|
|
743
|
+
let mainContextSection;
|
|
744
|
+
const contextFile = path.join(contextDir, "context.md");
|
|
745
|
+
if (await storage.exists(contextFile)) {
|
|
746
|
+
logger.debug(`Found context.md file in ${contextDir}`);
|
|
747
|
+
const mainContextContent = await storage.readFile(contextFile, "utf8");
|
|
748
|
+
const firstHeader = extractFirstHeader(mainContextContent);
|
|
749
|
+
const sectionTitle = firstHeader || dirName;
|
|
750
|
+
mainContextSection = create$4({ ...sectionOptions, title: sectionTitle });
|
|
751
|
+
if (firstHeader) {
|
|
752
|
+
mainContextSection.add(removeFirstHeader(mainContextContent), { ...sectionOptions });
|
|
753
|
+
} else {
|
|
754
|
+
mainContextSection.add(mainContextContent, { ...sectionOptions });
|
|
755
|
+
}
|
|
756
|
+
} else {
|
|
757
|
+
mainContextSection = create$4({ ...sectionOptions, title: dirName });
|
|
758
|
+
}
|
|
759
|
+
const files = await storage.listFiles(contextDir);
|
|
760
|
+
const ignorePatternsRegex = ignorePatterns.map((pattern) => {
|
|
761
|
+
try {
|
|
762
|
+
return new RegExp(pattern, "i");
|
|
763
|
+
} catch (error) {
|
|
764
|
+
logger.error(`Invalid ignore pattern: ${pattern}`, { error });
|
|
765
|
+
return /(?!)/;
|
|
766
|
+
}
|
|
767
|
+
});
|
|
768
|
+
const filteredFiles = files.filter((file) => {
|
|
769
|
+
const fullPath = path.join(contextDir, file);
|
|
770
|
+
return !ignorePatternsRegex.some((regex) => regex.test(file) || regex.test(fullPath));
|
|
771
|
+
});
|
|
772
|
+
for (const file of filteredFiles) {
|
|
773
|
+
if (file === "context.md") continue;
|
|
774
|
+
logger.debug(`Processing file ${file} in ${contextDir}`);
|
|
775
|
+
const filePath = path.join(contextDir, file);
|
|
776
|
+
if (await storage.isFile(filePath)) {
|
|
777
|
+
const fileContent = await storage.readFile(filePath, "utf8");
|
|
778
|
+
let sectionName = file;
|
|
779
|
+
let contentToAdd = fileContent;
|
|
780
|
+
if (file.endsWith(".md")) {
|
|
781
|
+
const fileHeader = extractFirstHeader(fileContent);
|
|
782
|
+
if (fileHeader) {
|
|
783
|
+
sectionName = fileHeader;
|
|
784
|
+
contentToAdd = removeFirstHeader(fileContent);
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
const fileSection = create$4({ ...sectionOptions, title: sectionName });
|
|
788
|
+
fileSection.add(contentToAdd, { ...sectionOptions });
|
|
789
|
+
mainContextSection.add(fileSection, { ...sectionOptions });
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
contextSections.push(mainContextSection);
|
|
793
|
+
} catch (error) {
|
|
794
|
+
logger.error(`Error processing context directory ${contextDir}: ${error}`);
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
return contextSections;
|
|
798
|
+
};
|
|
799
|
+
return {
|
|
800
|
+
load
|
|
801
|
+
};
|
|
802
|
+
};
|
|
803
|
+
zod.z.object({
|
|
804
|
+
logger: zod.z.any().optional().default(DEFAULT_LOGGER),
|
|
805
|
+
configDirs: zod.z.array(zod.z.string()).default(["./overrides"]),
|
|
806
|
+
overrides: zod.z.boolean().default(false),
|
|
807
|
+
parameters: ParametersSchema.optional().default({})
|
|
808
|
+
});
|
|
809
|
+
zod.z.object({
|
|
810
|
+
logger: zod.z.any().optional().default(DEFAULT_LOGGER),
|
|
811
|
+
basePath: zod.z.string(),
|
|
812
|
+
overridePaths: zod.z.array(zod.z.string()).optional().default(["./"]),
|
|
813
|
+
overrides: zod.z.boolean().optional().default(false),
|
|
814
|
+
parameters: ParametersSchema.optional().default({})
|
|
815
|
+
});
|
|
816
|
+
zod.z.object({
|
|
817
|
+
model: zod.z.string(),
|
|
818
|
+
formatter: zod.z.any().optional(),
|
|
819
|
+
trackContext: zod.z.boolean().optional().default(true),
|
|
820
|
+
deduplicateContext: zod.z.boolean().optional().default(true)
|
|
821
|
+
});
|
|
822
|
+
zod.z.object({
|
|
823
|
+
name: zod.z.string().min(1),
|
|
824
|
+
description: zod.z.string().min(1),
|
|
825
|
+
parameters: zod.z.object({
|
|
826
|
+
type: zod.z.literal("object"),
|
|
827
|
+
properties: zod.z.record(zod.z.string(), zod.z.any()).default({}),
|
|
828
|
+
// Allow any parameter structure
|
|
829
|
+
required: zod.z.array(zod.z.string()).optional()
|
|
830
|
+
}).passthrough(),
|
|
831
|
+
// Allow additional fields
|
|
832
|
+
execute: zod.z.custom(
|
|
833
|
+
(val) => typeof val === "function",
|
|
834
|
+
{ message: "execute must be a function" }
|
|
835
|
+
),
|
|
836
|
+
category: zod.z.string().optional(),
|
|
837
|
+
cost: zod.z.enum(["cheap", "moderate", "expensive"]).optional(),
|
|
838
|
+
examples: zod.z.array(zod.z.object({
|
|
839
|
+
scenario: zod.z.string(),
|
|
840
|
+
params: zod.z.any(),
|
|
841
|
+
expectedResult: zod.z.string()
|
|
842
|
+
})).optional()
|
|
843
|
+
}).passthrough();
|
|
844
|
+
const ContentItemSchema = zod.z.union([
|
|
845
|
+
zod.z.string(),
|
|
846
|
+
// Simple string content
|
|
847
|
+
zod.z.object({
|
|
848
|
+
content: zod.z.string(),
|
|
849
|
+
title: zod.z.string().optional(),
|
|
850
|
+
weight: zod.z.number().optional()
|
|
851
|
+
}),
|
|
852
|
+
zod.z.object({
|
|
853
|
+
path: zod.z.string(),
|
|
854
|
+
title: zod.z.string().optional(),
|
|
855
|
+
weight: zod.z.number().optional()
|
|
856
|
+
}),
|
|
857
|
+
zod.z.object({
|
|
858
|
+
directories: zod.z.array(zod.z.string()),
|
|
859
|
+
title: zod.z.string().optional(),
|
|
860
|
+
weight: zod.z.number().optional()
|
|
861
|
+
})
|
|
862
|
+
]);
|
|
863
|
+
zod.z.object({
|
|
864
|
+
// Core settings
|
|
865
|
+
basePath: zod.z.string(),
|
|
866
|
+
logger: zod.z.any().optional().default(DEFAULT_LOGGER),
|
|
867
|
+
overridePaths: zod.z.array(zod.z.string()).optional().default(["./"]),
|
|
868
|
+
overrides: zod.z.boolean().optional().default(false),
|
|
869
|
+
parameters: ParametersSchema.optional().default({}),
|
|
870
|
+
// Content sections
|
|
871
|
+
persona: ContentItemSchema.optional(),
|
|
872
|
+
instructions: zod.z.array(ContentItemSchema).optional().default([]),
|
|
873
|
+
content: zod.z.array(ContentItemSchema).optional().default([]),
|
|
874
|
+
context: zod.z.array(ContentItemSchema).optional().default([]),
|
|
875
|
+
// Templates and inheritance
|
|
876
|
+
extends: zod.z.string().optional(),
|
|
877
|
+
// Extend another recipe
|
|
878
|
+
template: zod.z.string().optional(),
|
|
879
|
+
// Generic template name
|
|
880
|
+
// Tool integration
|
|
881
|
+
tools: zod.z.any().optional(),
|
|
882
|
+
// Tool[] | ToolRegistry
|
|
883
|
+
toolGuidance: zod.z.union([
|
|
884
|
+
zod.z.enum(["auto", "minimal", "detailed"]),
|
|
885
|
+
zod.z.object({
|
|
886
|
+
strategy: zod.z.enum(["adaptive", "prescriptive", "minimal"]),
|
|
887
|
+
includeExamples: zod.z.boolean().optional(),
|
|
888
|
+
explainWhenToUse: zod.z.boolean().optional(),
|
|
889
|
+
includeCategories: zod.z.boolean().optional(),
|
|
890
|
+
customInstructions: zod.z.string().optional()
|
|
891
|
+
})
|
|
892
|
+
]).optional(),
|
|
893
|
+
toolCategories: zod.z.array(zod.z.string()).optional()
|
|
894
|
+
});
|
|
895
|
+
const toJSON = (prompt) => {
|
|
896
|
+
return JSON.stringify(prompt, null, 2);
|
|
897
|
+
};
|
|
898
|
+
const escapeXML = (str) => {
|
|
899
|
+
return str.replace(/[<>&'"]/g, (c) => {
|
|
900
|
+
switch (c) {
|
|
901
|
+
case "<":
|
|
902
|
+
return "<";
|
|
903
|
+
case ">":
|
|
904
|
+
return ">";
|
|
905
|
+
case "&":
|
|
906
|
+
return "&";
|
|
907
|
+
case "'":
|
|
908
|
+
return "'";
|
|
909
|
+
case '"':
|
|
910
|
+
return """;
|
|
911
|
+
default:
|
|
912
|
+
return c;
|
|
913
|
+
}
|
|
914
|
+
});
|
|
915
|
+
};
|
|
916
|
+
const itemToXML = (item) => {
|
|
917
|
+
if (typeof item === "string") {
|
|
918
|
+
return `<item>${escapeXML(item)}</item>`;
|
|
919
|
+
}
|
|
920
|
+
if (item && typeof item === "object" && "items" in item) {
|
|
921
|
+
return sectionToXML(item);
|
|
922
|
+
}
|
|
923
|
+
if (item && typeof item === "object" && "text" in item) {
|
|
924
|
+
const weightAttr = item.weight !== void 0 && item.weight !== null ? ` weight="${item.weight}"` : "";
|
|
925
|
+
return `<item${weightAttr}>${escapeXML(item.text)}</item>`;
|
|
926
|
+
}
|
|
927
|
+
return "";
|
|
928
|
+
};
|
|
929
|
+
const sectionToXML = (section, tagName = "section") => {
|
|
930
|
+
const titleAttr = section.title ? ` title="${escapeXML(section.title)}"` : "";
|
|
931
|
+
const weightAttr = section.weight ? ` weight="${section.weight}"` : "";
|
|
932
|
+
let xml = `<${tagName}${titleAttr}${weightAttr}>`;
|
|
933
|
+
if (section.items && Array.isArray(section.items)) {
|
|
934
|
+
xml += section.items.map((item) => itemToXML(item)).join("");
|
|
935
|
+
}
|
|
936
|
+
xml += `</${tagName}>`;
|
|
937
|
+
return xml;
|
|
938
|
+
};
|
|
939
|
+
const toXML = (prompt) => {
|
|
940
|
+
let xml = '<?xml version="1.0" encoding="UTF-8"?>\n<prompt>';
|
|
941
|
+
if (prompt.persona) {
|
|
942
|
+
xml += sectionToXML(prompt.persona, "persona");
|
|
943
|
+
}
|
|
944
|
+
if (prompt.instructions) {
|
|
945
|
+
xml += sectionToXML(prompt.instructions, "instructions");
|
|
946
|
+
}
|
|
947
|
+
if (prompt.contents) {
|
|
948
|
+
xml += sectionToXML(prompt.contents, "contents");
|
|
949
|
+
}
|
|
950
|
+
if (prompt.contexts) {
|
|
951
|
+
xml += sectionToXML(prompt.contexts, "contexts");
|
|
952
|
+
}
|
|
953
|
+
xml += "</prompt>";
|
|
954
|
+
return xml;
|
|
955
|
+
};
|
|
956
|
+
const parseSectionFromJSON = (jsonSection) => {
|
|
957
|
+
if (!jsonSection || !jsonSection.items) {
|
|
958
|
+
throw new Error("Invalid section structure");
|
|
959
|
+
}
|
|
960
|
+
const section = create$4({
|
|
961
|
+
title: jsonSection.title,
|
|
962
|
+
weight: jsonSection.weight
|
|
963
|
+
});
|
|
964
|
+
for (const item of jsonSection.items) {
|
|
965
|
+
if (typeof item === "object" && "items" in item) {
|
|
966
|
+
section.add(parseSectionFromJSON(item));
|
|
967
|
+
} else if (typeof item === "object" && "text" in item) {
|
|
968
|
+
section.add({
|
|
969
|
+
text: item.text,
|
|
970
|
+
weight: item.weight
|
|
971
|
+
});
|
|
972
|
+
} else if (typeof item === "string") {
|
|
973
|
+
section.add(item);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
return section;
|
|
977
|
+
};
|
|
978
|
+
const fromJSON = (jsonString) => {
|
|
979
|
+
const json = JSON.parse(jsonString);
|
|
980
|
+
const persona = json.persona ? parseSectionFromJSON(json.persona) : void 0;
|
|
981
|
+
const instructions = json.instructions ? parseSectionFromJSON(json.instructions) : create$4({ title: "Instructions" });
|
|
982
|
+
const contents = json.contents ? parseSectionFromJSON(json.contents) : void 0;
|
|
983
|
+
const contexts = json.contexts ? parseSectionFromJSON(json.contexts) : void 0;
|
|
984
|
+
return create$3({
|
|
985
|
+
persona,
|
|
986
|
+
instructions,
|
|
987
|
+
contents,
|
|
988
|
+
contexts
|
|
989
|
+
});
|
|
990
|
+
};
|
|
991
|
+
const parseNodeToSection = (node) => {
|
|
992
|
+
const children = node.section || node.persona || node.instructions || node.contents || node.contexts || [];
|
|
993
|
+
let attributes = node[":@"] || {};
|
|
994
|
+
if (!node[":@"] && Array.isArray(children)) {
|
|
995
|
+
for (const child of children) {
|
|
996
|
+
if (child[":@"]) {
|
|
997
|
+
attributes = child[":@"];
|
|
998
|
+
break;
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
const title = attributes["@_title"];
|
|
1003
|
+
const weight = attributes["@_weight"] ? Number(attributes["@_weight"]) : void 0;
|
|
1004
|
+
const section = create$4({ title, weight });
|
|
1005
|
+
if (Array.isArray(children)) {
|
|
1006
|
+
for (const child of children) {
|
|
1007
|
+
const key = Object.keys(child)[0];
|
|
1008
|
+
if (key === ":@") continue;
|
|
1009
|
+
if (key === "item") {
|
|
1010
|
+
const itemContent = child.item;
|
|
1011
|
+
let text = "";
|
|
1012
|
+
let itemWeight = void 0;
|
|
1013
|
+
for (const part of itemContent) {
|
|
1014
|
+
const keys = Object.keys(part);
|
|
1015
|
+
if (keys.includes("#text")) {
|
|
1016
|
+
text = part["#text"];
|
|
1017
|
+
} else if (keys.includes(":@")) {
|
|
1018
|
+
const attrs = part[":@"];
|
|
1019
|
+
const w = attrs["@_weight"] || attrs["weight"];
|
|
1020
|
+
if (w !== void 0) itemWeight = Number(w);
|
|
1021
|
+
} else {
|
|
1022
|
+
const w = part["@_weight"] || part["weight"];
|
|
1023
|
+
if (w !== void 0) itemWeight = Number(w);
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
section.add({ text, weight: itemWeight });
|
|
1027
|
+
} else if (key === "section") {
|
|
1028
|
+
section.add(parseNodeToSection(child));
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
return section;
|
|
1033
|
+
};
|
|
1034
|
+
const fromXML = (xmlString) => {
|
|
1035
|
+
const parser = new fastXmlParser.XMLParser({
|
|
1036
|
+
ignoreAttributes: false,
|
|
1037
|
+
attributeNamePrefix: "@_",
|
|
1038
|
+
preserveOrder: true,
|
|
1039
|
+
trimValues: true
|
|
1040
|
+
});
|
|
1041
|
+
const parsed = parser.parse(xmlString);
|
|
1042
|
+
let promptNode = null;
|
|
1043
|
+
for (const node of parsed) {
|
|
1044
|
+
if (node.prompt) {
|
|
1045
|
+
promptNode = node.prompt;
|
|
1046
|
+
break;
|
|
1047
|
+
}
|
|
1048
|
+
}
|
|
1049
|
+
if (!promptNode) throw new Error("Invalid XML: missing <prompt> root");
|
|
1050
|
+
let persona;
|
|
1051
|
+
let instructions = create$4({ title: "Instructions" });
|
|
1052
|
+
let contents;
|
|
1053
|
+
let contexts;
|
|
1054
|
+
for (const child of promptNode) {
|
|
1055
|
+
if (child.persona) {
|
|
1056
|
+
persona = parseNodeToSection(child);
|
|
1057
|
+
persona.title = "Persona";
|
|
1058
|
+
} else if (child.instructions) {
|
|
1059
|
+
instructions = parseNodeToSection(child);
|
|
1060
|
+
instructions.title = "Instructions";
|
|
1061
|
+
} else if (child.contents) {
|
|
1062
|
+
contents = parseNodeToSection(child);
|
|
1063
|
+
contents.title = "Contents";
|
|
1064
|
+
} else if (child.contexts) {
|
|
1065
|
+
contexts = parseNodeToSection(child);
|
|
1066
|
+
contexts.title = "Contexts";
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
return create$3({
|
|
1070
|
+
persona,
|
|
1071
|
+
instructions,
|
|
1072
|
+
contents,
|
|
1073
|
+
contexts
|
|
1074
|
+
});
|
|
1075
|
+
};
|
|
1076
|
+
const saveToDirectory = async (prompt, basePath) => {
|
|
1077
|
+
await fs__namespace$1.mkdir(basePath, { recursive: true });
|
|
1078
|
+
if (prompt.persona) {
|
|
1079
|
+
await saveSection(prompt.persona, path__namespace.join(basePath, "persona"));
|
|
1080
|
+
}
|
|
1081
|
+
if (prompt.instructions) {
|
|
1082
|
+
await saveSection(prompt.instructions, path__namespace.join(basePath, "instructions"));
|
|
1083
|
+
}
|
|
1084
|
+
if (prompt.contexts) {
|
|
1085
|
+
await saveSection(prompt.contexts, path__namespace.join(basePath, "context"));
|
|
1086
|
+
}
|
|
1087
|
+
if (prompt.contents) {
|
|
1088
|
+
await saveSection(prompt.contents, path__namespace.join(basePath, "content"));
|
|
1089
|
+
}
|
|
1090
|
+
};
|
|
1091
|
+
const saveSection = async (section, targetPath) => {
|
|
1092
|
+
const hasSubsections = section.items.some(
|
|
1093
|
+
(item) => typeof item === "object" && "items" in item
|
|
1094
|
+
);
|
|
1095
|
+
if (!hasSubsections) {
|
|
1096
|
+
const content = section.items.map((item) => {
|
|
1097
|
+
if (typeof item === "string") return item;
|
|
1098
|
+
return item.text;
|
|
1099
|
+
}).join("\n\n");
|
|
1100
|
+
await fs__namespace$1.writeFile(`${targetPath}.md`, content);
|
|
1101
|
+
return;
|
|
1102
|
+
}
|
|
1103
|
+
await fs__namespace$1.mkdir(targetPath, { recursive: true });
|
|
1104
|
+
for (let i = 0; i < section.items.length; i++) {
|
|
1105
|
+
const item = section.items[i];
|
|
1106
|
+
if (typeof item === "object" && "items" in item) {
|
|
1107
|
+
const subTitle = item.title || `part-${i + 1}`;
|
|
1108
|
+
const subPath = path__namespace.join(targetPath, subTitle);
|
|
1109
|
+
await saveSection(item, subPath);
|
|
1110
|
+
} else {
|
|
1111
|
+
const fileName = `item-${i + 1}.md`;
|
|
1112
|
+
const content = typeof item === "string" ? item : item.text;
|
|
1113
|
+
await fs__namespace$1.writeFile(path__namespace.join(targetPath, fileName), content);
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
};
|
|
1117
|
+
class OpenAIProvider {
|
|
1118
|
+
async execute(request, options = {}) {
|
|
1119
|
+
const apiKey = options.apiKey || process.env.OPENAI_API_KEY;
|
|
1120
|
+
if (!apiKey) throw new Error("OpenAI API key is required");
|
|
1121
|
+
const client = new OpenAI({ apiKey });
|
|
1122
|
+
const model = options.model || request.model || "gpt-4";
|
|
1123
|
+
const messages = request.messages.map((msg) => {
|
|
1124
|
+
const role = msg.role === "developer" ? "system" : msg.role;
|
|
1125
|
+
return {
|
|
1126
|
+
role,
|
|
1127
|
+
content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content),
|
|
1128
|
+
name: msg.name
|
|
1129
|
+
};
|
|
1130
|
+
});
|
|
1131
|
+
const response = await client.chat.completions.create({
|
|
1132
|
+
model,
|
|
1133
|
+
messages,
|
|
1134
|
+
temperature: options.temperature,
|
|
1135
|
+
max_tokens: options.maxTokens
|
|
1136
|
+
});
|
|
1137
|
+
const choice = response.choices[0];
|
|
1138
|
+
return {
|
|
1139
|
+
content: choice.message.content || "",
|
|
1140
|
+
model: response.model,
|
|
1141
|
+
usage: response.usage ? {
|
|
1142
|
+
inputTokens: response.usage.prompt_tokens,
|
|
1143
|
+
outputTokens: response.usage.completion_tokens
|
|
1144
|
+
} : void 0
|
|
1145
|
+
};
|
|
1146
|
+
}
|
|
1147
|
+
}
|
|
1148
|
+
class AnthropicProvider {
|
|
1149
|
+
async execute(request, options = {}) {
|
|
1150
|
+
const apiKey = options.apiKey || process.env.ANTHROPIC_API_KEY;
|
|
1151
|
+
if (!apiKey) throw new Error("Anthropic API key is required");
|
|
1152
|
+
const client = new Anthropic({ apiKey });
|
|
1153
|
+
const model = options.model || request.model || "claude-3-opus-20240229";
|
|
1154
|
+
let systemPrompt = "";
|
|
1155
|
+
const messages = [];
|
|
1156
|
+
for (const msg of request.messages) {
|
|
1157
|
+
if (msg.role === "system" || msg.role === "developer") {
|
|
1158
|
+
systemPrompt += (typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)) + "\n\n";
|
|
1159
|
+
} else {
|
|
1160
|
+
messages.push({
|
|
1161
|
+
role: msg.role,
|
|
1162
|
+
content: typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)
|
|
1163
|
+
});
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
const response = await client.messages.create({
|
|
1167
|
+
model,
|
|
1168
|
+
system: systemPrompt.trim() || void 0,
|
|
1169
|
+
messages,
|
|
1170
|
+
max_tokens: options.maxTokens || 4096,
|
|
1171
|
+
// Anthropic requires max_tokens
|
|
1172
|
+
temperature: options.temperature
|
|
1173
|
+
});
|
|
1174
|
+
const contentBlock = response.content[0];
|
|
1175
|
+
const text = contentBlock.type === "text" ? contentBlock.text : "";
|
|
1176
|
+
return {
|
|
1177
|
+
content: text,
|
|
1178
|
+
model: response.model,
|
|
1179
|
+
usage: {
|
|
1180
|
+
inputTokens: response.usage.input_tokens,
|
|
1181
|
+
outputTokens: response.usage.output_tokens
|
|
1182
|
+
}
|
|
1183
|
+
};
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
class GeminiProvider {
|
|
1187
|
+
async execute(request, options = {}) {
|
|
1188
|
+
const apiKey = options.apiKey || process.env.GEMINI_API_KEY;
|
|
1189
|
+
if (!apiKey) throw new Error("Gemini API key is required");
|
|
1190
|
+
const genAI = new generativeAi.GoogleGenerativeAI(apiKey);
|
|
1191
|
+
const modelName = options.model || request.model || "gemini-1.5-pro";
|
|
1192
|
+
let systemInstruction = "";
|
|
1193
|
+
for (const msg of request.messages) {
|
|
1194
|
+
if (msg.role === "system" || msg.role === "developer") {
|
|
1195
|
+
systemInstruction += (typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content)) + "\n\n";
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
const configuredModel = genAI.getGenerativeModel({
|
|
1199
|
+
model: modelName,
|
|
1200
|
+
systemInstruction: systemInstruction ? systemInstruction.trim() : void 0
|
|
1201
|
+
});
|
|
1202
|
+
const chatHistory = [];
|
|
1203
|
+
let lastUserMessage = "";
|
|
1204
|
+
for (const msg of request.messages) {
|
|
1205
|
+
if (msg.role === "system" || msg.role === "developer") continue;
|
|
1206
|
+
const content = typeof msg.content === "string" ? msg.content : JSON.stringify(msg.content);
|
|
1207
|
+
if (msg.role === "user") {
|
|
1208
|
+
lastUserMessage = content;
|
|
1209
|
+
}
|
|
1210
|
+
chatHistory.push({
|
|
1211
|
+
role: msg.role === "assistant" ? "model" : "user",
|
|
1212
|
+
parts: [{ text: content }]
|
|
1213
|
+
});
|
|
1214
|
+
}
|
|
1215
|
+
let result;
|
|
1216
|
+
if (chatHistory.length > 1) {
|
|
1217
|
+
const lastMsg = chatHistory.pop();
|
|
1218
|
+
const chat = configuredModel.startChat({
|
|
1219
|
+
history: chatHistory
|
|
1220
|
+
});
|
|
1221
|
+
result = await chat.sendMessage(lastMsg?.parts[0].text || "");
|
|
1222
|
+
} else {
|
|
1223
|
+
result = await configuredModel.generateContent(lastUserMessage || " ");
|
|
1224
|
+
}
|
|
1225
|
+
const response = await result.response;
|
|
1226
|
+
const text = response.text();
|
|
1227
|
+
return {
|
|
1228
|
+
content: text,
|
|
1229
|
+
model: modelName,
|
|
1230
|
+
// Gemini usage metadata usageMetadata
|
|
1231
|
+
usage: response.usageMetadata ? {
|
|
1232
|
+
inputTokens: response.usageMetadata.promptTokenCount,
|
|
1233
|
+
outputTokens: response.usageMetadata.candidatesTokenCount
|
|
1234
|
+
} : void 0
|
|
1235
|
+
};
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
class ExecutionManager {
|
|
1239
|
+
providers;
|
|
1240
|
+
constructor() {
|
|
1241
|
+
this.providers = /* @__PURE__ */ new Map();
|
|
1242
|
+
this.providers.set("openai", new OpenAIProvider());
|
|
1243
|
+
this.providers.set("anthropic", new AnthropicProvider());
|
|
1244
|
+
this.providers.set("gemini", new GeminiProvider());
|
|
1245
|
+
}
|
|
1246
|
+
getProvider(model) {
|
|
1247
|
+
if (!model) {
|
|
1248
|
+
return this.providers.get("openai");
|
|
1249
|
+
}
|
|
1250
|
+
if (model.startsWith("gpt") || model.startsWith("o1")) {
|
|
1251
|
+
return this.providers.get("openai");
|
|
1252
|
+
} else if (model.startsWith("claude")) {
|
|
1253
|
+
return this.providers.get("anthropic");
|
|
1254
|
+
} else if (model.startsWith("gemini")) {
|
|
1255
|
+
return this.providers.get("gemini");
|
|
1256
|
+
}
|
|
1257
|
+
return this.providers.get("openai");
|
|
1258
|
+
}
|
|
1259
|
+
async execute(request, options = {}) {
|
|
1260
|
+
const model = options.model || request.model;
|
|
1261
|
+
const provider = this.getProvider(model);
|
|
1262
|
+
return provider.execute(request, options);
|
|
1263
|
+
}
|
|
1264
|
+
}
|
|
1265
|
+
const execute = async (request, options = {}) => {
|
|
1266
|
+
const manager = new ExecutionManager();
|
|
1267
|
+
return manager.execute(request, options);
|
|
1268
|
+
};
|
|
1269
|
+
const program = new commander.Command();
|
|
1270
|
+
const configManager = cardigantime.create({
|
|
1271
|
+
configShape: ConfigSchema.shape,
|
|
1272
|
+
defaults: {
|
|
1273
|
+
configDirectory: process.cwd(),
|
|
1274
|
+
configFile: "riotprompt.yaml"
|
|
1275
|
+
}
|
|
1276
|
+
});
|
|
1277
|
+
program.name("riotprompt").description("CLI tool for analyzing and processing prompts").version("0.0.1");
|
|
1278
|
+
async function isDirectory(path2) {
|
|
1279
|
+
try {
|
|
1280
|
+
const stat = await fs__namespace$1.stat(path2);
|
|
1281
|
+
return stat.isDirectory();
|
|
1282
|
+
} catch {
|
|
1283
|
+
return false;
|
|
1284
|
+
}
|
|
1285
|
+
}
|
|
1286
|
+
async function fileExists(path2) {
|
|
1287
|
+
try {
|
|
1288
|
+
await fs__namespace$1.access(path2);
|
|
1289
|
+
return true;
|
|
1290
|
+
} catch {
|
|
1291
|
+
return false;
|
|
1292
|
+
}
|
|
1293
|
+
}
|
|
1294
|
+
async function loadPromptFromDirectory(absolutePromptPath) {
|
|
1295
|
+
let personaSection;
|
|
1296
|
+
let instructionsSection;
|
|
1297
|
+
let contextSection;
|
|
1298
|
+
const loader = create();
|
|
1299
|
+
const personaDir = path__namespace.join(absolutePromptPath, "persona");
|
|
1300
|
+
const personaFile = path__namespace.join(absolutePromptPath, "persona.md");
|
|
1301
|
+
if (await isDirectory(personaDir)) {
|
|
1302
|
+
console.log("Loading persona from directory...");
|
|
1303
|
+
const personaSections = await loader.load([personaDir]);
|
|
1304
|
+
if (personaSections.length > 0) {
|
|
1305
|
+
personaSection = create$4({ title: "Persona" });
|
|
1306
|
+
personaSections.forEach((section) => {
|
|
1307
|
+
personaSection.add(section);
|
|
1308
|
+
});
|
|
1309
|
+
}
|
|
1310
|
+
} else if (await fileExists(personaFile)) {
|
|
1311
|
+
console.log("Loading persona from file...");
|
|
1312
|
+
const personaContent = await fs__namespace$1.readFile(personaFile, "utf-8");
|
|
1313
|
+
personaSection = create$4({ title: "Persona" });
|
|
1314
|
+
personaSection.add(create$5(personaContent));
|
|
1315
|
+
} else {
|
|
1316
|
+
console.log("No persona found, skipping.");
|
|
1317
|
+
}
|
|
1318
|
+
const instructionsDir = path__namespace.join(absolutePromptPath, "instructions");
|
|
1319
|
+
const instructionsFile = path__namespace.join(absolutePromptPath, "instructions.md");
|
|
1320
|
+
if (await isDirectory(instructionsDir)) {
|
|
1321
|
+
console.log("Loading instructions from directory...");
|
|
1322
|
+
const instructionSections = await loader.load([instructionsDir]);
|
|
1323
|
+
if (instructionSections.length > 0) {
|
|
1324
|
+
instructionsSection = create$4({ title: "Instructions" });
|
|
1325
|
+
instructionSections.forEach((section) => {
|
|
1326
|
+
instructionsSection.add(section);
|
|
1327
|
+
});
|
|
1328
|
+
}
|
|
1329
|
+
} else if (await fileExists(instructionsFile)) {
|
|
1330
|
+
console.log("Loading instructions from file...");
|
|
1331
|
+
const instructionsContent = await fs__namespace$1.readFile(instructionsFile, "utf-8");
|
|
1332
|
+
instructionsSection = create$4({ title: "Instructions" });
|
|
1333
|
+
instructionsSection.add(create$5(instructionsContent));
|
|
1334
|
+
}
|
|
1335
|
+
if (!instructionsSection) {
|
|
1336
|
+
throw new Error("instructions (directory or .md file) is required.");
|
|
1337
|
+
}
|
|
1338
|
+
const contextDir = path__namespace.join(absolutePromptPath, "context");
|
|
1339
|
+
if (await isDirectory(contextDir)) {
|
|
1340
|
+
console.log("Loading context from directory...");
|
|
1341
|
+
const contextSections = await loader.load([contextDir]);
|
|
1342
|
+
if (contextSections.length > 0) {
|
|
1343
|
+
contextSection = create$4({ title: "Context" });
|
|
1344
|
+
contextSections.forEach((section) => {
|
|
1345
|
+
contextSection.add(section);
|
|
1346
|
+
});
|
|
1347
|
+
}
|
|
1348
|
+
} else {
|
|
1349
|
+
console.log("No context directory found, skipping context.");
|
|
1350
|
+
}
|
|
1351
|
+
return create$3({
|
|
1352
|
+
persona: personaSection,
|
|
1353
|
+
instructions: instructionsSection,
|
|
1354
|
+
contexts: contextSection
|
|
1355
|
+
});
|
|
1356
|
+
}
|
|
1357
|
+
async function createAction(promptName, options) {
|
|
1358
|
+
try {
|
|
1359
|
+
const basePath = path__namespace.resolve(options.path, promptName);
|
|
1360
|
+
if (await fileExists(basePath)) {
|
|
1361
|
+
console.error(`Error: Directory ${basePath} already exists.`);
|
|
1362
|
+
process.exit(1);
|
|
1363
|
+
}
|
|
1364
|
+
if (options.import) {
|
|
1365
|
+
console.log(`Importing prompt from ${options.import} to ${basePath}...`);
|
|
1366
|
+
const content = await fs__namespace$1.readFile(options.import, "utf-8");
|
|
1367
|
+
let prompt;
|
|
1368
|
+
if (options.import.endsWith(".json")) {
|
|
1369
|
+
prompt = fromJSON(content);
|
|
1370
|
+
} else if (options.import.endsWith(".xml")) {
|
|
1371
|
+
prompt = fromXML(content);
|
|
1372
|
+
} else {
|
|
1373
|
+
throw new Error("Unsupported file extension. Use .json or .xml");
|
|
1374
|
+
}
|
|
1375
|
+
await saveToDirectory(prompt, basePath);
|
|
1376
|
+
console.log(`Successfully imported to ${basePath}`);
|
|
1377
|
+
} else {
|
|
1378
|
+
console.log(`Creating prompt structure at ${basePath}...`);
|
|
1379
|
+
await fs__namespace$1.mkdir(basePath, { recursive: true });
|
|
1380
|
+
const personaText = options.persona || "You are a helpful AI assistant.";
|
|
1381
|
+
await fs__namespace$1.writeFile(path__namespace.join(basePath, "persona.md"), personaText);
|
|
1382
|
+
console.log("Created persona.md");
|
|
1383
|
+
const instructionsText = options.instructions || "Please analyze the following request.";
|
|
1384
|
+
await fs__namespace$1.writeFile(path__namespace.join(basePath, "instructions.md"), instructionsText);
|
|
1385
|
+
console.log("Created instructions.md");
|
|
1386
|
+
if (options.context) {
|
|
1387
|
+
const contextDir = path__namespace.join(basePath, "context");
|
|
1388
|
+
await fs__namespace$1.mkdir(contextDir);
|
|
1389
|
+
await fs__namespace$1.writeFile(path__namespace.join(contextDir, "README.md"), "Place context files (json, md, txt) in this directory.");
|
|
1390
|
+
console.log("Created context directory");
|
|
1391
|
+
}
|
|
1392
|
+
console.log(`
|
|
1393
|
+
Prompt '${promptName}' created successfully!`);
|
|
1394
|
+
}
|
|
1395
|
+
console.log(`Run 'riotprompt process ${path__namespace.join(options.path, promptName)}' to test it.`);
|
|
1396
|
+
} catch (error) {
|
|
1397
|
+
console.error("Error creating prompt:", error);
|
|
1398
|
+
process.exit(1);
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
async function processAction(promptPath, options) {
|
|
1402
|
+
try {
|
|
1403
|
+
const config = await configManager.read(options);
|
|
1404
|
+
const modelName = options.model || config.defaultModel;
|
|
1405
|
+
console.log(`Processing prompt from: ${promptPath}`);
|
|
1406
|
+
console.log(`Using model: ${modelName}`);
|
|
1407
|
+
const absolutePromptPath = path__namespace.resolve(promptPath);
|
|
1408
|
+
if (!await fileExists(absolutePromptPath)) {
|
|
1409
|
+
console.error(`Error: Prompt path not found at ${absolutePromptPath}`);
|
|
1410
|
+
process.exit(1);
|
|
1411
|
+
}
|
|
1412
|
+
let prompt;
|
|
1413
|
+
if (await isDirectory(absolutePromptPath)) {
|
|
1414
|
+
prompt = await loadPromptFromDirectory(absolutePromptPath);
|
|
1415
|
+
} else {
|
|
1416
|
+
const content = await fs__namespace$1.readFile(absolutePromptPath, "utf-8");
|
|
1417
|
+
if (absolutePromptPath.endsWith(".json")) {
|
|
1418
|
+
prompt = fromJSON(content);
|
|
1419
|
+
} else if (absolutePromptPath.endsWith(".xml")) {
|
|
1420
|
+
prompt = fromXML(content);
|
|
1421
|
+
} else {
|
|
1422
|
+
console.error("Error: Supported file formats are .json and .xml");
|
|
1423
|
+
process.exit(1);
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
let output = "";
|
|
1427
|
+
if (options.format === "json") {
|
|
1428
|
+
output = toJSON(prompt);
|
|
1429
|
+
} else if (options.format === "xml") {
|
|
1430
|
+
output = toXML(prompt);
|
|
1431
|
+
} else {
|
|
1432
|
+
const model = modelName;
|
|
1433
|
+
const formatter = create$2();
|
|
1434
|
+
const chatRequest = formatter.formatPrompt(model, prompt);
|
|
1435
|
+
if (chatRequest.messages) {
|
|
1436
|
+
output = chatRequest.messages.map((m) => `--- ROLE: ${m.role} ---
|
|
1437
|
+
${m.content}`).join("\n\n");
|
|
1438
|
+
} else {
|
|
1439
|
+
output = JSON.stringify(chatRequest, null, 2);
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
if (options.output) {
|
|
1443
|
+
await fs__namespace$1.writeFile(options.output, output);
|
|
1444
|
+
console.log(`Output written to ${options.output}`);
|
|
1445
|
+
} else {
|
|
1446
|
+
console.log("\n--- Result ---\n");
|
|
1447
|
+
console.log(output);
|
|
1448
|
+
}
|
|
1449
|
+
} catch (error) {
|
|
1450
|
+
console.error("Error processing prompt:", error);
|
|
1451
|
+
process.exit(1);
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
async function executeAction(promptPath, options) {
|
|
1455
|
+
try {
|
|
1456
|
+
const config = await configManager.read(options);
|
|
1457
|
+
const modelName = options.model || config.defaultModel;
|
|
1458
|
+
console.log(`Executing prompt from: ${promptPath}`);
|
|
1459
|
+
console.log(`Using model: ${modelName}`);
|
|
1460
|
+
const absolutePromptPath = path__namespace.resolve(promptPath);
|
|
1461
|
+
if (!await fileExists(absolutePromptPath)) {
|
|
1462
|
+
console.error(`Error: Prompt path not found at ${absolutePromptPath}`);
|
|
1463
|
+
process.exit(1);
|
|
1464
|
+
}
|
|
1465
|
+
let prompt;
|
|
1466
|
+
if (await isDirectory(absolutePromptPath)) {
|
|
1467
|
+
prompt = await loadPromptFromDirectory(absolutePromptPath);
|
|
1468
|
+
} else {
|
|
1469
|
+
const content = await fs__namespace$1.readFile(absolutePromptPath, "utf-8");
|
|
1470
|
+
if (absolutePromptPath.endsWith(".json")) {
|
|
1471
|
+
prompt = fromJSON(content);
|
|
1472
|
+
} else if (absolutePromptPath.endsWith(".xml")) {
|
|
1473
|
+
prompt = fromXML(content);
|
|
1474
|
+
} else {
|
|
1475
|
+
console.error("Error: Supported file formats are .json and .xml");
|
|
1476
|
+
process.exit(1);
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
const formatter = create$2();
|
|
1480
|
+
const chatRequest = formatter.formatPrompt(modelName, prompt);
|
|
1481
|
+
const executionOptions = {
|
|
1482
|
+
apiKey: options.key,
|
|
1483
|
+
model: modelName,
|
|
1484
|
+
temperature: options.temperature,
|
|
1485
|
+
maxTokens: options.maxTokens
|
|
1486
|
+
};
|
|
1487
|
+
const result = await execute(chatRequest, executionOptions);
|
|
1488
|
+
console.log("\n--- Response ---\n");
|
|
1489
|
+
console.log(result.content);
|
|
1490
|
+
if (result.usage) {
|
|
1491
|
+
console.log("\n--- Usage ---");
|
|
1492
|
+
console.log(`Input Tokens: ${result.usage.inputTokens}`);
|
|
1493
|
+
console.log(`Output Tokens: ${result.usage.outputTokens}`);
|
|
1494
|
+
}
|
|
1495
|
+
} catch (error) {
|
|
1496
|
+
console.error("Error executing prompt:", error.message || error);
|
|
1497
|
+
process.exit(1);
|
|
1498
|
+
}
|
|
1499
|
+
}
|
|
1500
|
+
async function main() {
|
|
1501
|
+
await configManager.configure(program);
|
|
1502
|
+
program.command("create <promptName>").description("Create a new prompt directory structure or import from file").option("-p, --path <path>", "Base path to create the prompt in", ".").option("--persona <text>", "Initial text for persona.md").option("--instructions <text>", "Initial text for instructions.md").option("--context", "Create context directory with placeholder", true).option("--no-context", "Do not create context directory").option("--import <file>", "Import prompt structure from a JSON or XML file").action(createAction);
|
|
1503
|
+
program.command("process <promptPath>").description("Process a prompt (directory, JSON, or XML) and output the formatted prompt").option("-m, --model <model>", "Model to format for (e.g., gpt-4, claude)").option("-o, --output <file>", "Output file path").option("--format <format>", "Output format (text, json, xml)", "text").action(processAction);
|
|
1504
|
+
program.command("execute <promptPath>").description("Execute a prompt using an LLM provider").option("-m, --model <model>", "Model to use (e.g., gpt-4, claude-3-opus, gemini-1.5-pro)").option("-k, --key <key>", "API Key (overrides env vars)").option("-t, --temperature <number>", "Temperature (0-1)", parseFloat).option("--max-tokens <number>", "Max tokens", parseInt).action(executeAction);
|
|
1505
|
+
await program.parseAsync();
|
|
1506
|
+
}
|
|
1507
|
+
if (typeof require !== "undefined" && require.main === module) {
|
|
1508
|
+
main().catch((err) => {
|
|
1509
|
+
console.error(err);
|
|
1510
|
+
process.exit(1);
|
|
1511
|
+
});
|
|
1512
|
+
}
|
|
1513
|
+
exports.createAction = createAction;
|
|
1514
|
+
exports.executeAction = executeAction;
|
|
1515
|
+
exports.fileExists = fileExists;
|
|
1516
|
+
exports.isDirectory = isDirectory;
|
|
1517
|
+
exports.loadPromptFromDirectory = loadPromptFromDirectory;
|
|
1518
|
+
exports.main = main;
|
|
1519
|
+
exports.processAction = processAction;
|