promptopskit 0.5.0 → 0.6.2

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.
Files changed (70) hide show
  1. package/README.md +9 -7
  2. package/SKILL.md +16 -10
  3. package/dist/{chunk-6VLKZCNS.js → chunk-55B7QQBK.js} +2 -2
  4. package/dist/{chunk-6BOUQVSX.js → chunk-FNCIBZJF.js} +3 -3
  5. package/dist/{chunk-D75HKF64.js → chunk-GLT3YYWK.js} +2 -2
  6. package/dist/{chunk-IN4KT5EU.js → chunk-IPFD5Y3O.js} +3 -3
  7. package/dist/{chunk-KN7H4JRK.js → chunk-JR7252DG.js} +393 -386
  8. package/dist/chunk-JR7252DG.js.map +1 -0
  9. package/dist/{chunk-K6ZU3CGV.js → chunk-UY5OKMVP.js} +281 -17
  10. package/dist/chunk-UY5OKMVP.js.map +1 -0
  11. package/dist/{chunk-QK3WKE3K.js → chunk-VMLGUI4F.js} +2 -2
  12. package/dist/{chunk-LNGHGGJN.js → chunk-XEZJZ3IG.js} +2 -2
  13. package/dist/cli/index.js +410 -103
  14. package/dist/cli/index.js.map +1 -1
  15. package/dist/index.cjs +971 -660
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +5 -4
  18. package/dist/index.d.ts +5 -4
  19. package/dist/index.js +55 -15
  20. package/dist/index.js.map +1 -1
  21. package/dist/providers/anthropic.cjs +675 -407
  22. package/dist/providers/anthropic.cjs.map +1 -1
  23. package/dist/providers/anthropic.d.cts +2 -2
  24. package/dist/providers/anthropic.d.ts +2 -2
  25. package/dist/providers/anthropic.js +3 -3
  26. package/dist/providers/gemini.cjs +675 -407
  27. package/dist/providers/gemini.cjs.map +1 -1
  28. package/dist/providers/gemini.d.cts +2 -2
  29. package/dist/providers/gemini.d.ts +2 -2
  30. package/dist/providers/gemini.js +3 -3
  31. package/dist/providers/llmasaservice.cjs +675 -407
  32. package/dist/providers/llmasaservice.cjs.map +1 -1
  33. package/dist/providers/llmasaservice.d.cts +2 -2
  34. package/dist/providers/llmasaservice.d.ts +2 -2
  35. package/dist/providers/llmasaservice.js +4 -4
  36. package/dist/providers/openai-responses.cjs +675 -407
  37. package/dist/providers/openai-responses.cjs.map +1 -1
  38. package/dist/providers/openai-responses.d.cts +2 -2
  39. package/dist/providers/openai-responses.d.ts +2 -2
  40. package/dist/providers/openai-responses.js +3 -3
  41. package/dist/providers/openai.cjs +675 -407
  42. package/dist/providers/openai.cjs.map +1 -1
  43. package/dist/providers/openai.d.cts +2 -2
  44. package/dist/providers/openai.d.ts +2 -2
  45. package/dist/providers/openai.js +3 -3
  46. package/dist/providers/openrouter.cjs +675 -407
  47. package/dist/providers/openrouter.cjs.map +1 -1
  48. package/dist/providers/openrouter.d.cts +2 -2
  49. package/dist/providers/openrouter.d.ts +2 -2
  50. package/dist/providers/openrouter.js +4 -4
  51. package/dist/{schema-D-RI4w-k.d.cts → schema-CuYsv8JA.d.cts} +540 -206
  52. package/dist/{schema-D-RI4w-k.d.ts → schema-CuYsv8JA.d.ts} +540 -206
  53. package/dist/testing.cjs +43 -5
  54. package/dist/testing.cjs.map +1 -1
  55. package/dist/testing.d.cts +1 -1
  56. package/dist/testing.d.ts +1 -1
  57. package/dist/testing.js +1 -1
  58. package/dist/{types-B3dxBGu6.d.ts → types-DpK0mTHL.d.ts} +1 -1
  59. package/dist/{types-DbTAjlTN.d.cts → types-UA_p9uJy.d.cts} +1 -1
  60. package/dist/usagetap/index.d.cts +2 -2
  61. package/dist/usagetap/index.d.ts +2 -2
  62. package/package.json +9 -2
  63. package/dist/chunk-K6ZU3CGV.js.map +0 -1
  64. package/dist/chunk-KN7H4JRK.js.map +0 -1
  65. /package/dist/{chunk-6VLKZCNS.js.map → chunk-55B7QQBK.js.map} +0 -0
  66. /package/dist/{chunk-6BOUQVSX.js.map → chunk-FNCIBZJF.js.map} +0 -0
  67. /package/dist/{chunk-D75HKF64.js.map → chunk-GLT3YYWK.js.map} +0 -0
  68. /package/dist/{chunk-IN4KT5EU.js.map → chunk-IPFD5Y3O.js.map} +0 -0
  69. /package/dist/{chunk-QK3WKE3K.js.map → chunk-VMLGUI4F.js.map} +0 -0
  70. /package/dist/{chunk-LNGHGGJN.js.map → chunk-XEZJZ3IG.js.map} +0 -0
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  loadPromptFile,
3
3
  parsePrompt
4
- } from "./chunk-K6ZU3CGV.js";
4
+ } from "./chunk-UY5OKMVP.js";
5
5
 
6
6
  // src/renderer/interpolate.ts
7
7
  var VARIABLE_RE = /\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}/g;
@@ -9,12 +9,13 @@ var ESCAPED_OPEN = /\\\{\\\{/g;
9
9
  var ESCAPE_PLACEHOLDER = "\0ESCAPED_OPEN\0";
10
10
  function interpolate(template, variables, options = {}) {
11
11
  const { strict = false } = options;
12
+ const optionalVariables = new Set(options.optionalVariables ?? []);
12
13
  let result = template.replace(ESCAPED_OPEN, ESCAPE_PLACEHOLDER);
13
14
  result = result.replace(VARIABLE_RE, (match, name) => {
14
15
  if (name in variables) {
15
16
  return variables[name];
16
17
  }
17
- if (strict) {
18
+ if (strict && !optionalVariables.has(name)) {
18
19
  throw new Error(`Missing required variable: "${name}"`);
19
20
  }
20
21
  return match;
@@ -32,22 +33,326 @@ function extractVariables(template) {
32
33
  return [...vars];
33
34
  }
34
35
 
36
+ // src/context.ts
37
+ var textEncoder = new TextEncoder();
38
+ var REJECT_SECRETS_PATTERN = "(secret|api[_-]?key|password)";
39
+ var REJECT_SECRETS_FLAGS = "i";
40
+ function getContextInputs(asset) {
41
+ return (asset.context?.inputs ?? []).map(normalizeContextInput);
42
+ }
43
+ function normalizeContextInput(input) {
44
+ if (typeof input === "string") {
45
+ return { name: input };
46
+ }
47
+ return {
48
+ name: input.name,
49
+ optional: input.optional,
50
+ warnings: input.warnings,
51
+ max_size: input.max_size,
52
+ trim: input.trim,
53
+ allow_regex: normalizeContextRegex(input.allow_regex),
54
+ deny_regex: normalizeContextRegex(input.deny_regex),
55
+ non_empty: normalizeBuiltInValidator(input.non_empty),
56
+ reject_secrets: normalizeBuiltInValidator(input.reject_secrets)
57
+ };
58
+ }
59
+ function areContextInputWarningsEnabled(input) {
60
+ return input.warnings !== false;
61
+ }
62
+ function normalizeContextRegex(value) {
63
+ if (value === void 0) {
64
+ return void 0;
65
+ }
66
+ if (typeof value === "string") {
67
+ const literal = parseRegexLiteral(value);
68
+ if (value.startsWith("/") && !literal) {
69
+ return {
70
+ pattern: value,
71
+ flags: "",
72
+ raw: value,
73
+ invalidLiteral: true
74
+ };
75
+ }
76
+ return {
77
+ pattern: literal?.pattern ?? value,
78
+ flags: literal?.flags ?? "",
79
+ raw: value
80
+ };
81
+ }
82
+ return {
83
+ pattern: value.pattern,
84
+ flags: value.flags ?? "",
85
+ raw: JSON.stringify(value),
86
+ returnMessage: value.return_message
87
+ };
88
+ }
89
+ function normalizeBuiltInValidator(value) {
90
+ if (value === void 0 || value === false) {
91
+ return void 0;
92
+ }
93
+ if (value === true) {
94
+ return {};
95
+ }
96
+ return {
97
+ returnMessage: value.return_message
98
+ };
99
+ }
100
+ function parseRegexLiteral(value) {
101
+ if (!value.startsWith("/")) {
102
+ return void 0;
103
+ }
104
+ for (let index = value.length - 1; index > 0; index -= 1) {
105
+ if (value[index] !== "/") {
106
+ continue;
107
+ }
108
+ let backslashCount = 0;
109
+ for (let cursor = index - 1; cursor >= 0 && value[cursor] === "\\"; cursor -= 1) {
110
+ backslashCount += 1;
111
+ }
112
+ if (backslashCount % 2 === 1) {
113
+ continue;
114
+ }
115
+ return {
116
+ pattern: value.slice(1, index),
117
+ flags: value.slice(index + 1)
118
+ };
119
+ }
120
+ return void 0;
121
+ }
122
+ function formatInvalidContextRegexMessage(details) {
123
+ return [
124
+ `Invalid context regex for prompt "${details.promptId}"`,
125
+ `variable "${details.variable}"`,
126
+ `field "${details.field}"`,
127
+ `value ${JSON.stringify(details.raw)}: ${details.reason}`
128
+ ].join(", ");
129
+ }
130
+ function compileContextRegex(regex, details) {
131
+ if (regex.invalidLiteral) {
132
+ throw new Error(
133
+ `POK013: ${formatInvalidContextRegexMessage({
134
+ ...details,
135
+ raw: regex.raw,
136
+ reason: "Malformed regex literal. Use /pattern/flags or { pattern, flags }."
137
+ })}`
138
+ );
139
+ }
140
+ try {
141
+ return new RegExp(regex.pattern, regex.flags);
142
+ } catch (error) {
143
+ const reason = error instanceof Error ? error.message : String(error);
144
+ throw new Error(`POK013: ${formatInvalidContextRegexMessage({ ...details, raw: regex.raw, reason })}`);
145
+ }
146
+ }
147
+ function getRejectSecretsRegex() {
148
+ return {
149
+ pattern: REJECT_SECRETS_PATTERN,
150
+ flags: REJECT_SECRETS_FLAGS,
151
+ raw: JSON.stringify({ pattern: REJECT_SECRETS_PATTERN, flags: REJECT_SECRETS_FLAGS })
152
+ };
153
+ }
154
+ function isTrimEnabled(mode) {
155
+ return mode === true || mode === "start" || mode === "end" || mode === "both";
156
+ }
157
+ function normalizeTrimMode(mode) {
158
+ if (mode === "start") {
159
+ return "start";
160
+ }
161
+ return "end";
162
+ }
163
+ function trimToMaxSize(value, maxSize, mode) {
164
+ const measured = measureContextValueSize(value);
165
+ if (measured <= maxSize) {
166
+ return value;
167
+ }
168
+ const characters = Array.from(value);
169
+ const normalizedMode = normalizeTrimMode(mode);
170
+ if (normalizedMode === "start") {
171
+ let collected2 = "";
172
+ let size2 = 0;
173
+ for (let i = characters.length - 1; i >= 0; i -= 1) {
174
+ const next = characters[i];
175
+ const charSize = measureContextValueSize(next);
176
+ if (size2 + charSize > maxSize) {
177
+ break;
178
+ }
179
+ collected2 = next + collected2;
180
+ size2 += charSize;
181
+ }
182
+ return collected2;
183
+ }
184
+ let collected = "";
185
+ let size = 0;
186
+ for (const char of characters) {
187
+ const charSize = measureContextValueSize(char);
188
+ if (size + charSize > maxSize) {
189
+ break;
190
+ }
191
+ collected += char;
192
+ size += charSize;
193
+ }
194
+ return collected;
195
+ }
196
+ function sanitizeContextVariables(asset, variables = {}, options = {}) {
197
+ const { onContextOverflow } = options;
198
+ const sanitized = { ...variables };
199
+ for (const input of getContextInputs(asset)) {
200
+ const value = sanitized[input.name];
201
+ if (value === void 0) {
202
+ continue;
203
+ }
204
+ let candidate = value;
205
+ if (input.max_size !== void 0) {
206
+ const actualSize = measureContextValueSize(candidate);
207
+ if (actualSize > input.max_size && onContextOverflow) {
208
+ candidate = onContextOverflow({
209
+ promptId: asset.id,
210
+ variable: input.name,
211
+ value: candidate,
212
+ maxSize: input.max_size,
213
+ actualSize
214
+ });
215
+ }
216
+ }
217
+ if (isTrimEnabled(input.trim) && input.max_size !== void 0) {
218
+ candidate = trimToMaxSize(candidate, input.max_size, input.trim);
219
+ }
220
+ sanitized[input.name] = candidate;
221
+ if (input.allow_regex) {
222
+ const candidate2 = sanitized[input.name];
223
+ const matcher = compileContextRegex(input.allow_regex, {
224
+ promptId: asset.id,
225
+ variable: input.name,
226
+ field: "allow_regex"
227
+ });
228
+ if (!matcher.test(candidate2)) {
229
+ if (input.allow_regex.returnMessage) {
230
+ return {
231
+ variables: sanitized,
232
+ shortCircuit: {
233
+ returnMessage: input.allow_regex.returnMessage,
234
+ code: "POK031",
235
+ variable: input.name,
236
+ field: "allow_regex"
237
+ }
238
+ };
239
+ }
240
+ throw new Error(
241
+ `POK031: Context variable "${input.name}" failed allow_regex validation for prompt "${asset.id}".`
242
+ );
243
+ }
244
+ }
245
+ if (input.deny_regex) {
246
+ const candidate2 = sanitized[input.name];
247
+ const matcher = compileContextRegex(input.deny_regex, {
248
+ promptId: asset.id,
249
+ variable: input.name,
250
+ field: "deny_regex"
251
+ });
252
+ if (matcher.test(candidate2)) {
253
+ if (input.deny_regex.returnMessage) {
254
+ return {
255
+ variables: sanitized,
256
+ shortCircuit: {
257
+ returnMessage: input.deny_regex.returnMessage,
258
+ code: "POK032",
259
+ variable: input.name,
260
+ field: "deny_regex"
261
+ }
262
+ };
263
+ }
264
+ throw new Error(
265
+ `POK032: Context variable "${input.name}" matched deny_regex for prompt "${asset.id}".`
266
+ );
267
+ }
268
+ }
269
+ if (input.non_empty && candidate.trim().length === 0) {
270
+ if (input.non_empty.returnMessage) {
271
+ return {
272
+ variables: sanitized,
273
+ shortCircuit: {
274
+ returnMessage: input.non_empty.returnMessage,
275
+ code: "POK033",
276
+ variable: input.name,
277
+ field: "non_empty"
278
+ }
279
+ };
280
+ }
281
+ throw new Error(
282
+ `POK033: Context variable "${input.name}" failed non_empty validation for prompt "${asset.id}".`
283
+ );
284
+ }
285
+ if (input.reject_secrets) {
286
+ const matcher = compileContextRegex(getRejectSecretsRegex(), {
287
+ promptId: asset.id,
288
+ variable: input.name,
289
+ field: "reject_secrets"
290
+ });
291
+ if (matcher.test(candidate)) {
292
+ if (input.reject_secrets.returnMessage) {
293
+ return {
294
+ variables: sanitized,
295
+ shortCircuit: {
296
+ returnMessage: input.reject_secrets.returnMessage,
297
+ code: "POK034",
298
+ variable: input.name,
299
+ field: "reject_secrets"
300
+ }
301
+ };
302
+ }
303
+ throw new Error(
304
+ `POK034: Context variable "${input.name}" matched reject_secrets validation for prompt "${asset.id}".`
305
+ );
306
+ }
307
+ }
308
+ }
309
+ return { variables: sanitized };
310
+ }
311
+ function measureContextValueSize(value) {
312
+ return textEncoder.encode(value).length;
313
+ }
314
+ function collectContextSizeWarnings(asset, variables = {}) {
315
+ const warnings = [];
316
+ for (const input of getContextInputs(asset)) {
317
+ if (!areContextInputWarningsEnabled(input)) {
318
+ continue;
319
+ }
320
+ if (input.max_size === void 0) {
321
+ continue;
322
+ }
323
+ const value = variables[input.name];
324
+ if (value === void 0) {
325
+ continue;
326
+ }
327
+ const actualSize = measureContextValueSize(value);
328
+ if (actualSize > input.max_size) {
329
+ warnings.push({
330
+ variable: input.name,
331
+ maxSize: input.max_size,
332
+ actualSize
333
+ });
334
+ }
335
+ }
336
+ return warnings;
337
+ }
338
+
35
339
  // src/renderer/renderer.ts
36
340
  function renderSections(asset, options = {}) {
37
341
  const { variables = {}, strict = false } = options;
342
+ const optionalVariables = getContextInputs(asset).filter((input) => input.optional === true).map((input) => input.name);
38
343
  const result = {};
39
344
  if (asset.sections.system_instructions) {
40
345
  result.system_instructions = interpolate(
41
346
  asset.sections.system_instructions,
42
347
  variables,
43
- { strict }
348
+ { strict, optionalVariables }
44
349
  );
45
350
  }
46
351
  if (asset.sections.prompt_template) {
47
352
  result.prompt_template = interpolate(
48
353
  asset.sections.prompt_template,
49
354
  variables,
50
- { strict }
355
+ { strict, optionalVariables }
51
356
  );
52
357
  }
53
358
  return result;
@@ -226,400 +531,102 @@ async function resolveIncludes(asset, basePath, visited = /* @__PURE__ */ new Se
226
531
  ...asset.sections,
227
532
  system_instructions: combinedSystem
228
533
  },
229
- // Drop includes from the resolved asset — they've been inlined
230
- includes: void 0
231
- };
232
- }
233
-
234
- // src/prompt-resolution.ts
235
- import { readFile as readFile2 } from "fs/promises";
236
- import { existsSync, statSync as statSync2 } from "fs";
237
- import { resolve as resolve2 } from "path";
238
- var DEFAULT_PROMPTS_DIR = "./prompts";
239
- var DEFAULT_COMPILED_JSON_DIR = "./.generated-prompts/json";
240
- function withPromptResolutionDefaults(config) {
241
- return {
242
- ...config,
243
- sourceDir: config.sourceDir ?? DEFAULT_PROMPTS_DIR,
244
- compiledDir: config.compiledDir ?? DEFAULT_COMPILED_JSON_DIR
245
- };
246
- }
247
- var sharedPromptCache = new PromptCache();
248
- async function loadPromptAsset(promptPath, config, promptCache = sharedPromptCache) {
249
- const resolvedConfig = withPromptResolutionDefaults(config);
250
- const mode = resolvedConfig.mode ?? "auto";
251
- if (mode !== "source-only" && resolvedConfig.compiledDir) {
252
- const compiledFile = resolve2(resolvedConfig.compiledDir, promptPath + ".json");
253
- if (existsSync(compiledFile)) {
254
- if (mode === "auto") {
255
- const sourceFile = resolve2(resolvedConfig.sourceDir, promptPath + ".md");
256
- if (existsSync(sourceFile)) {
257
- const compiledMtime = statSync2(compiledFile).mtimeMs;
258
- const sourceMtime = statSync2(sourceFile).mtimeMs;
259
- if (sourceMtime > compiledMtime) {
260
- console.warn(
261
- `[promptopskit] Warning: compiled artifact for "${promptPath}" is older than source .md file.
262
- Run "promptopskit compile" or switch to source-only mode.`
263
- );
264
- }
265
- }
266
- }
267
- const content = await readFile2(compiledFile, "utf-8");
268
- return JSON.parse(content);
269
- }
270
- if (mode === "compiled-only") {
271
- throw new Error(
272
- `Compiled artifact not found: ${compiledFile}
273
- Run "promptopskit compile" to generate it.`
274
- );
275
- }
276
- }
277
- if (mode !== "compiled-only") {
278
- const sourceFile = resolve2(resolvedConfig.sourceDir, promptPath + ".md");
279
- if (resolvedConfig.cache !== false) {
280
- const cached = promptCache.get(sourceFile);
281
- if (cached) {
282
- return cached;
283
- }
284
- }
285
- if (!existsSync(sourceFile)) {
286
- const paths = [sourceFile];
287
- if (resolvedConfig.compiledDir) {
288
- paths.unshift(resolve2(resolvedConfig.compiledDir, promptPath + ".json"));
289
- }
290
- throw new Error(
291
- `Prompt not found: "${promptPath}"
292
- Searched:
293
- ${paths.map((candidate) => ` - ${candidate}`).join("\n")}`
294
- );
295
- }
296
- const { asset } = await loadPromptFile(sourceFile, { defaultsRoot: resolvedConfig.sourceDir });
297
- if (resolvedConfig.cache !== false) {
298
- promptCache.set(sourceFile, asset);
299
- }
300
- return asset;
301
- }
302
- throw new Error(`Prompt not found: "${promptPath}"`);
303
- }
304
- async function resolvePromptAsset(promptPath, config, options = {}, promptCache = sharedPromptCache) {
305
- const resolvedConfig = withPromptResolutionDefaults(config);
306
- let asset = await loadPromptAsset(promptPath, resolvedConfig, promptCache);
307
- const sourceFile = resolve2(resolvedConfig.sourceDir, promptPath + ".md");
308
- if (asset.includes && asset.includes.length > 0 && existsSync(sourceFile)) {
309
- asset = await resolveIncludes(asset, sourceFile);
310
- }
311
- asset = applyOverrides(asset, {
312
- environment: options.environment,
313
- tier: options.tier,
314
- runtime: options.runtime
315
- });
316
- return asset;
317
- }
318
- function resolveInlinePromptSource(source, options = {}) {
319
- const { asset } = parsePrompt(source);
320
- return applyOverrides(asset, {
321
- environment: options.environment,
322
- tier: options.tier,
323
- runtime: options.runtime
324
- });
325
- }
326
-
327
- // src/context.ts
328
- var textEncoder = new TextEncoder();
329
- var REJECT_SECRETS_PATTERN = "(secret|api[_-]?key|password)";
330
- var REJECT_SECRETS_FLAGS = "i";
331
- function getContextInputs(asset) {
332
- return (asset.context?.inputs ?? []).map(normalizeContextInput);
333
- }
334
- function getContextInputNames(asset) {
335
- return getContextInputs(asset).map((input) => input.name);
336
- }
337
- function normalizeContextInput(input) {
338
- if (typeof input === "string") {
339
- return { name: input };
340
- }
341
- return {
342
- name: input.name,
343
- max_size: input.max_size,
344
- trim: input.trim,
345
- allow_regex: normalizeContextRegex(input.allow_regex),
346
- deny_regex: normalizeContextRegex(input.deny_regex),
347
- non_empty: normalizeBuiltInValidator(input.non_empty),
348
- reject_secrets: normalizeBuiltInValidator(input.reject_secrets)
349
- };
350
- }
351
- function normalizeContextRegex(value) {
352
- if (value === void 0) {
353
- return void 0;
354
- }
355
- if (typeof value === "string") {
356
- const literal = parseRegexLiteral(value);
357
- if (value.startsWith("/") && !literal) {
358
- return {
359
- pattern: value,
360
- flags: "",
361
- raw: value,
362
- invalidLiteral: true
363
- };
364
- }
365
- return {
366
- pattern: literal?.pattern ?? value,
367
- flags: literal?.flags ?? "",
368
- raw: value
369
- };
370
- }
371
- return {
372
- pattern: value.pattern,
373
- flags: value.flags ?? "",
374
- raw: JSON.stringify(value),
375
- returnMessage: value.return_message
376
- };
377
- }
378
- function normalizeBuiltInValidator(value) {
379
- if (value === void 0 || value === false) {
380
- return void 0;
381
- }
382
- if (value === true) {
383
- return {};
384
- }
385
- return {
386
- returnMessage: value.return_message
387
- };
388
- }
389
- function parseRegexLiteral(value) {
390
- if (!value.startsWith("/")) {
391
- return void 0;
392
- }
393
- for (let index = value.length - 1; index > 0; index -= 1) {
394
- if (value[index] !== "/") {
395
- continue;
396
- }
397
- let backslashCount = 0;
398
- for (let cursor = index - 1; cursor >= 0 && value[cursor] === "\\"; cursor -= 1) {
399
- backslashCount += 1;
400
- }
401
- if (backslashCount % 2 === 1) {
402
- continue;
403
- }
404
- return {
405
- pattern: value.slice(1, index),
406
- flags: value.slice(index + 1)
407
- };
408
- }
409
- return void 0;
410
- }
411
- function formatInvalidContextRegexMessage(details) {
412
- return [
413
- `Invalid context regex for prompt "${details.promptId}"`,
414
- `variable "${details.variable}"`,
415
- `field "${details.field}"`,
416
- `value ${JSON.stringify(details.raw)}: ${details.reason}`
417
- ].join(", ");
418
- }
419
- function compileContextRegex(regex, details) {
420
- if (regex.invalidLiteral) {
421
- throw new Error(
422
- `POK013: ${formatInvalidContextRegexMessage({
423
- ...details,
424
- raw: regex.raw,
425
- reason: "Malformed regex literal. Use /pattern/flags or { pattern, flags }."
426
- })}`
427
- );
428
- }
429
- try {
430
- return new RegExp(regex.pattern, regex.flags);
431
- } catch (error) {
432
- const reason = error instanceof Error ? error.message : String(error);
433
- throw new Error(`POK013: ${formatInvalidContextRegexMessage({ ...details, raw: regex.raw, reason })}`);
434
- }
534
+ // Drop includes from the resolved asset — they've been inlined
535
+ includes: void 0
536
+ };
435
537
  }
436
- function getRejectSecretsRegex() {
538
+
539
+ // src/prompt-resolution.ts
540
+ import { readFile as readFile2 } from "fs/promises";
541
+ import { existsSync, statSync as statSync2 } from "fs";
542
+ import { resolve as resolve2 } from "path";
543
+ var DEFAULT_PROMPTS_DIR = "./prompts";
544
+ var DEFAULT_COMPILED_JSON_DIR = "./.generated-prompts/json";
545
+ function withPromptResolutionDefaults(config) {
437
546
  return {
438
- pattern: REJECT_SECRETS_PATTERN,
439
- flags: REJECT_SECRETS_FLAGS,
440
- raw: JSON.stringify({ pattern: REJECT_SECRETS_PATTERN, flags: REJECT_SECRETS_FLAGS })
547
+ ...config,
548
+ sourceDir: config.sourceDir ?? DEFAULT_PROMPTS_DIR,
549
+ compiledDir: config.compiledDir ?? DEFAULT_COMPILED_JSON_DIR
441
550
  };
442
551
  }
443
- function isTrimEnabled(mode) {
444
- return mode === true || mode === "start" || mode === "end" || mode === "both";
445
- }
446
- function normalizeTrimMode(mode) {
447
- if (mode === "start") {
448
- return "start";
449
- }
450
- return "end";
451
- }
452
- function trimToMaxSize(value, maxSize, mode) {
453
- const measured = measureContextValueSize(value);
454
- if (measured <= maxSize) {
455
- return value;
456
- }
457
- const characters = Array.from(value);
458
- const normalizedMode = normalizeTrimMode(mode);
459
- if (normalizedMode === "start") {
460
- let collected2 = "";
461
- let size2 = 0;
462
- for (let i = characters.length - 1; i >= 0; i -= 1) {
463
- const next = characters[i];
464
- const charSize = measureContextValueSize(next);
465
- if (size2 + charSize > maxSize) {
466
- break;
552
+ var sharedPromptCache = new PromptCache();
553
+ async function loadPromptAsset(promptPath, config, promptCache = sharedPromptCache) {
554
+ const resolvedConfig = withPromptResolutionDefaults(config);
555
+ const mode = resolvedConfig.mode ?? "auto";
556
+ if (mode !== "source-only" && resolvedConfig.compiledDir) {
557
+ const compiledFile = resolve2(resolvedConfig.compiledDir, promptPath + ".json");
558
+ if (existsSync(compiledFile)) {
559
+ if (mode === "auto") {
560
+ const sourceFile = resolve2(resolvedConfig.sourceDir, promptPath + ".md");
561
+ if (existsSync(sourceFile)) {
562
+ const compiledMtime = statSync2(compiledFile).mtimeMs;
563
+ const sourceMtime = statSync2(sourceFile).mtimeMs;
564
+ if (sourceMtime > compiledMtime) {
565
+ console.warn(
566
+ `[promptopskit] Warning: compiled artifact for "${promptPath}" is older than source .md file.
567
+ Run "promptopskit compile" or switch to source-only mode.`
568
+ );
569
+ }
570
+ }
467
571
  }
468
- collected2 = next + collected2;
469
- size2 += charSize;
572
+ const content = await readFile2(compiledFile, "utf-8");
573
+ return JSON.parse(content);
470
574
  }
471
- return collected2;
472
- }
473
- let collected = "";
474
- let size = 0;
475
- for (const char of characters) {
476
- const charSize = measureContextValueSize(char);
477
- if (size + charSize > maxSize) {
478
- break;
575
+ if (mode === "compiled-only") {
576
+ throw new Error(
577
+ `Compiled artifact not found: ${compiledFile}
578
+ Run "promptopskit compile" to generate it.`
579
+ );
479
580
  }
480
- collected += char;
481
- size += charSize;
482
581
  }
483
- return collected;
484
- }
485
- function sanitizeContextVariables(asset, variables = {}, options = {}) {
486
- const { onContextOverflow } = options;
487
- const sanitized = { ...variables };
488
- for (const input of getContextInputs(asset)) {
489
- const value = sanitized[input.name];
490
- if (value === void 0) {
491
- continue;
492
- }
493
- let candidate = value;
494
- if (input.max_size !== void 0) {
495
- const actualSize = measureContextValueSize(candidate);
496
- if (actualSize > input.max_size && onContextOverflow) {
497
- candidate = onContextOverflow({
498
- promptId: asset.id,
499
- variable: input.name,
500
- value: candidate,
501
- maxSize: input.max_size,
502
- actualSize
503
- });
504
- }
505
- }
506
- if (isTrimEnabled(input.trim) && input.max_size !== void 0) {
507
- candidate = trimToMaxSize(candidate, input.max_size, input.trim);
508
- }
509
- sanitized[input.name] = candidate;
510
- if (input.allow_regex) {
511
- const candidate2 = sanitized[input.name];
512
- const matcher = compileContextRegex(input.allow_regex, {
513
- promptId: asset.id,
514
- variable: input.name,
515
- field: "allow_regex"
516
- });
517
- if (!matcher.test(candidate2)) {
518
- if (input.allow_regex.returnMessage) {
519
- return {
520
- variables: sanitized,
521
- shortCircuit: {
522
- returnMessage: input.allow_regex.returnMessage,
523
- code: "POK031",
524
- variable: input.name,
525
- field: "allow_regex"
526
- }
527
- };
528
- }
529
- throw new Error(
530
- `POK031: Context variable "${input.name}" failed allow_regex validation for prompt "${asset.id}".`
531
- );
532
- }
533
- }
534
- if (input.deny_regex) {
535
- const candidate2 = sanitized[input.name];
536
- const matcher = compileContextRegex(input.deny_regex, {
537
- promptId: asset.id,
538
- variable: input.name,
539
- field: "deny_regex"
540
- });
541
- if (matcher.test(candidate2)) {
542
- if (input.deny_regex.returnMessage) {
543
- return {
544
- variables: sanitized,
545
- shortCircuit: {
546
- returnMessage: input.deny_regex.returnMessage,
547
- code: "POK032",
548
- variable: input.name,
549
- field: "deny_regex"
550
- }
551
- };
552
- }
553
- throw new Error(
554
- `POK032: Context variable "${input.name}" matched deny_regex for prompt "${asset.id}".`
555
- );
582
+ if (mode !== "compiled-only") {
583
+ const sourceFile = resolve2(resolvedConfig.sourceDir, promptPath + ".md");
584
+ if (resolvedConfig.cache !== false) {
585
+ const cached = promptCache.get(sourceFile);
586
+ if (cached) {
587
+ return cached;
556
588
  }
557
589
  }
558
- if (input.non_empty && candidate.trim().length === 0) {
559
- if (input.non_empty.returnMessage) {
560
- return {
561
- variables: sanitized,
562
- shortCircuit: {
563
- returnMessage: input.non_empty.returnMessage,
564
- code: "POK033",
565
- variable: input.name,
566
- field: "non_empty"
567
- }
568
- };
590
+ if (!existsSync(sourceFile)) {
591
+ const paths = [sourceFile];
592
+ if (resolvedConfig.compiledDir) {
593
+ paths.unshift(resolve2(resolvedConfig.compiledDir, promptPath + ".json"));
569
594
  }
570
595
  throw new Error(
571
- `POK033: Context variable "${input.name}" failed non_empty validation for prompt "${asset.id}".`
596
+ `Prompt not found: "${promptPath}"
597
+ Searched:
598
+ ${paths.map((candidate) => ` - ${candidate}`).join("\n")}`
572
599
  );
573
600
  }
574
- if (input.reject_secrets) {
575
- const matcher = compileContextRegex(getRejectSecretsRegex(), {
576
- promptId: asset.id,
577
- variable: input.name,
578
- field: "reject_secrets"
579
- });
580
- if (matcher.test(candidate)) {
581
- if (input.reject_secrets.returnMessage) {
582
- return {
583
- variables: sanitized,
584
- shortCircuit: {
585
- returnMessage: input.reject_secrets.returnMessage,
586
- code: "POK034",
587
- variable: input.name,
588
- field: "reject_secrets"
589
- }
590
- };
591
- }
592
- throw new Error(
593
- `POK034: Context variable "${input.name}" matched reject_secrets validation for prompt "${asset.id}".`
594
- );
595
- }
601
+ const { asset } = await loadPromptFile(sourceFile, { defaultsRoot: resolvedConfig.sourceDir });
602
+ if (resolvedConfig.cache !== false) {
603
+ promptCache.set(sourceFile, asset);
596
604
  }
605
+ return asset;
597
606
  }
598
- return { variables: sanitized };
599
- }
600
- function measureContextValueSize(value) {
601
- return textEncoder.encode(value).length;
607
+ throw new Error(`Prompt not found: "${promptPath}"`);
602
608
  }
603
- function collectContextSizeWarnings(asset, variables = {}) {
604
- const warnings = [];
605
- for (const input of getContextInputs(asset)) {
606
- if (input.max_size === void 0) {
607
- continue;
608
- }
609
- const value = variables[input.name];
610
- if (value === void 0) {
611
- continue;
612
- }
613
- const actualSize = measureContextValueSize(value);
614
- if (actualSize > input.max_size) {
615
- warnings.push({
616
- variable: input.name,
617
- maxSize: input.max_size,
618
- actualSize
619
- });
620
- }
609
+ async function resolvePromptAsset(promptPath, config, options = {}, promptCache = sharedPromptCache) {
610
+ const resolvedConfig = withPromptResolutionDefaults(config);
611
+ let asset = await loadPromptAsset(promptPath, resolvedConfig, promptCache);
612
+ const sourceFile = resolve2(resolvedConfig.sourceDir, promptPath + ".md");
613
+ if (asset.includes && asset.includes.length > 0 && existsSync(sourceFile)) {
614
+ asset = await resolveIncludes(asset, sourceFile);
621
615
  }
622
- return warnings;
616
+ asset = applyOverrides(asset, {
617
+ environment: options.environment,
618
+ tier: options.tier,
619
+ runtime: options.runtime
620
+ });
621
+ return asset;
622
+ }
623
+ function resolveInlinePromptSource(source, options = {}) {
624
+ const { asset } = parsePrompt(source);
625
+ return applyOverrides(asset, {
626
+ environment: options.environment,
627
+ tier: options.tier,
628
+ runtime: options.runtime
629
+ });
623
630
  }
624
631
 
625
632
  // src/history.ts
@@ -736,6 +743,11 @@ function getRawProviderBody(asset, provider) {
736
743
  export {
737
744
  interpolate,
738
745
  extractVariables,
746
+ getContextInputs,
747
+ areContextInputWarningsEnabled,
748
+ compileContextRegex,
749
+ sanitizeContextVariables,
750
+ collectContextSizeWarnings,
739
751
  renderSections,
740
752
  applyOverrides,
741
753
  resolveAssetForProvider,
@@ -746,13 +758,8 @@ export {
746
758
  loadPromptAsset,
747
759
  resolvePromptAsset,
748
760
  resolveInlinePromptSource,
749
- getContextInputs,
750
- getContextInputNames,
751
- compileContextRegex,
752
- sanitizeContextVariables,
753
- collectContextSizeWarnings,
754
761
  compactHistoryForPrompt,
755
762
  withPromptInputSupport,
756
763
  applyRawProviderBody
757
764
  };
758
- //# sourceMappingURL=chunk-KN7H4JRK.js.map
765
+ //# sourceMappingURL=chunk-JR7252DG.js.map