promptopskit 0.3.4 → 0.3.5

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 (45) hide show
  1. package/README.md +6 -8
  2. package/dist/{chunk-VU3WKLFI.js → chunk-2X5HFPSD.js} +2 -2
  3. package/dist/{chunk-R5PKK6Y7.js → chunk-4QW4BSGE.js} +2 -2
  4. package/dist/{chunk-UJA7XBQZ.js → chunk-7RWTFGMS.js} +2 -2
  5. package/dist/{chunk-S5YHGHK5.js → chunk-M5VKRDIY.js} +27 -14
  6. package/dist/chunk-M5VKRDIY.js.map +1 -0
  7. package/dist/{chunk-U7IDX2P7.js → chunk-ROBYCHAW.js} +3 -3
  8. package/dist/cli/index.js +113 -54
  9. package/dist/cli/index.js.map +1 -1
  10. package/dist/index.cjs +30 -18
  11. package/dist/index.cjs.map +1 -1
  12. package/dist/index.d.cts +5 -5
  13. package/dist/index.d.ts +5 -5
  14. package/dist/index.js +14 -11
  15. package/dist/index.js.map +1 -1
  16. package/dist/providers/anthropic.cjs +24 -13
  17. package/dist/providers/anthropic.cjs.map +1 -1
  18. package/dist/providers/anthropic.d.cts +1 -1
  19. package/dist/providers/anthropic.d.ts +1 -1
  20. package/dist/providers/anthropic.js +2 -2
  21. package/dist/providers/gemini.cjs +24 -13
  22. package/dist/providers/gemini.cjs.map +1 -1
  23. package/dist/providers/gemini.d.cts +1 -1
  24. package/dist/providers/gemini.d.ts +1 -1
  25. package/dist/providers/gemini.js +2 -2
  26. package/dist/providers/openai.cjs +24 -13
  27. package/dist/providers/openai.cjs.map +1 -1
  28. package/dist/providers/openai.d.cts +1 -1
  29. package/dist/providers/openai.d.ts +1 -1
  30. package/dist/providers/openai.js +2 -2
  31. package/dist/providers/openrouter.cjs +24 -13
  32. package/dist/providers/openrouter.cjs.map +1 -1
  33. package/dist/providers/openrouter.d.cts +1 -1
  34. package/dist/providers/openrouter.d.ts +1 -1
  35. package/dist/providers/openrouter.js +3 -3
  36. package/dist/{types-CgA7_wNI.d.cts → types-ClXTFaX-.d.cts} +1 -1
  37. package/dist/{types-D_-336jx.d.ts → types-lLD7m02V.d.ts} +1 -1
  38. package/dist/usagetap/index.d.cts +1 -1
  39. package/dist/usagetap/index.d.ts +1 -1
  40. package/package.json +4 -1
  41. package/dist/chunk-S5YHGHK5.js.map +0 -1
  42. /package/dist/{chunk-VU3WKLFI.js.map → chunk-2X5HFPSD.js.map} +0 -0
  43. /package/dist/{chunk-R5PKK6Y7.js.map → chunk-4QW4BSGE.js.map} +0 -0
  44. /package/dist/{chunk-UJA7XBQZ.js.map → chunk-7RWTFGMS.js.map} +0 -0
  45. /package/dist/{chunk-U7IDX2P7.js.map → chunk-ROBYCHAW.js.map} +0 -0
package/README.md CHANGED
@@ -34,7 +34,7 @@ npm install promptopskit
34
34
  ### 1. Scaffold starter prompts
35
35
 
36
36
  ```bash
37
- npx promptopskit init ./prompts
37
+ npx promptopskit init
38
38
  npx promptopskit skill
39
39
  ```
40
40
 
@@ -84,7 +84,7 @@ You are a helpful support assistant working in {{ app_context }}.
84
84
  ```typescript
85
85
  import { createPromptOpsKit } from 'promptopskit';
86
86
 
87
- const kit = createPromptOpsKit({ sourceDir: './prompts' });
87
+ const kit = createPromptOpsKit();
88
88
 
89
89
  const result = await kit.renderPrompt({
90
90
  path: 'support/reply',
@@ -110,7 +110,6 @@ You can control context size warning behavior at the kit level:
110
110
 
111
111
  ```typescript
112
112
  const kit = createPromptOpsKit({
113
- sourceDir: './prompts',
114
113
  warnings: {
115
114
  contextSize: process.env.NODE_ENV === 'production' ? 'off' : 'console-and-result',
116
115
  },
@@ -141,7 +140,7 @@ Each adapter produces a `{ body, provider, model }` object shaped for the target
141
140
  ```typescript
142
141
  // OpenAI
143
142
  import { createPromptOpsKit } from 'promptopskit';
144
- const kit = createPromptOpsKit({ sourceDir: './prompts' });
143
+ const kit = createPromptOpsKit();
145
144
  const { request } = await kit.renderPrompt({
146
145
  path: 'hello',
147
146
  provider: 'openai',
@@ -207,17 +206,14 @@ const request = openaiAdapter.render(prompt, {
207
206
 
208
207
  In browser or client-side code, keep provider credentials on the server. Use the rendered request body with your own server endpoint, server action, or edge function rather than calling a provider directly from the client.
209
208
 
210
- On the server, adapters also provide async prompt-aware helpers so you can pass a prompt key plus `sourceDir` and `compiledDir` without creating a `PromptOpsKit` instance:
209
+ On the server, adapters also provide async prompt-aware helpers so you can use the default `./prompts` and `./.generated-prompts/json` directories without creating a `PromptOpsKit` instance:
211
210
 
212
211
  ```typescript
213
- import path from 'node:path';
214
212
  import { openaiAdapter } from 'promptopskit/openai';
215
213
 
216
214
  const request = await openaiAdapter.renderPrompt(
217
215
  {
218
216
  path: 'summarizePullRequest',
219
- sourceDir: path.join(process.cwd(), 'prompts'),
220
- compiledDir: path.join(process.cwd(), '.generated-prompts', 'json'),
221
217
  },
222
218
  {
223
219
  environment: 'dev',
@@ -229,6 +225,8 @@ const request = await openaiAdapter.renderPrompt(
229
225
  );
230
226
  ```
231
227
 
228
+ If you need a different layout, keep passing `sourceDir` and `compiledDir` explicitly.
229
+
232
230
  `renderPrompt()` and `validatePrompt()` use the same source-versus-compiled resolution rules as `kit.renderPrompt()`. The existing synchronous `render()` and `validate()` methods still work for already-resolved compiled or inline assets.
233
231
 
234
232
  ## Optional UsageTap Tracking
@@ -2,7 +2,7 @@ import {
2
2
  renderSections,
3
3
  resolveAssetForProvider,
4
4
  withPromptInputSupport
5
- } from "./chunk-S5YHGHK5.js";
5
+ } from "./chunk-M5VKRDIY.js";
6
6
 
7
7
  // src/providers/openai.ts
8
8
  var openaiAdapter = withPromptInputSupport({
@@ -84,4 +84,4 @@ var openaiAdapter = withPromptInputSupport({
84
84
  export {
85
85
  openaiAdapter
86
86
  };
87
- //# sourceMappingURL=chunk-VU3WKLFI.js.map
87
+ //# sourceMappingURL=chunk-2X5HFPSD.js.map
@@ -2,7 +2,7 @@ import {
2
2
  renderSections,
3
3
  resolveAssetForProvider,
4
4
  withPromptInputSupport
5
- } from "./chunk-S5YHGHK5.js";
5
+ } from "./chunk-M5VKRDIY.js";
6
6
 
7
7
  // src/providers/anthropic.ts
8
8
  var anthropicAdapter = withPromptInputSupport({
@@ -89,4 +89,4 @@ var anthropicAdapter = withPromptInputSupport({
89
89
  export {
90
90
  anthropicAdapter
91
91
  };
92
- //# sourceMappingURL=chunk-R5PKK6Y7.js.map
92
+ //# sourceMappingURL=chunk-4QW4BSGE.js.map
@@ -2,7 +2,7 @@ import {
2
2
  renderSections,
3
3
  resolveAssetForProvider,
4
4
  withPromptInputSupport
5
- } from "./chunk-S5YHGHK5.js";
5
+ } from "./chunk-M5VKRDIY.js";
6
6
 
7
7
  // src/providers/gemini.ts
8
8
  var geminiAdapter = withPromptInputSupport({
@@ -93,4 +93,4 @@ var geminiAdapter = withPromptInputSupport({
93
93
  export {
94
94
  geminiAdapter
95
95
  };
96
- //# sourceMappingURL=chunk-UJA7XBQZ.js.map
96
+ //# sourceMappingURL=chunk-7RWTFGMS.js.map
@@ -185,14 +185,24 @@ async function resolveIncludes(asset, basePath, visited = /* @__PURE__ */ new Se
185
185
  import { readFile as readFile2 } from "fs/promises";
186
186
  import { existsSync, statSync as statSync2 } from "fs";
187
187
  import { resolve as resolve2 } from "path";
188
+ var DEFAULT_PROMPTS_DIR = "./prompts";
189
+ var DEFAULT_COMPILED_JSON_DIR = "./.generated-prompts/json";
190
+ function withPromptResolutionDefaults(config) {
191
+ return {
192
+ ...config,
193
+ sourceDir: config.sourceDir ?? DEFAULT_PROMPTS_DIR,
194
+ compiledDir: config.compiledDir ?? DEFAULT_COMPILED_JSON_DIR
195
+ };
196
+ }
188
197
  var sharedPromptCache = new PromptCache();
189
198
  async function loadPromptAsset(promptPath, config, promptCache = sharedPromptCache) {
190
- const mode = config.mode ?? "auto";
191
- if (mode !== "source-only" && config.compiledDir) {
192
- const compiledFile = resolve2(config.compiledDir, promptPath + ".json");
199
+ const resolvedConfig = withPromptResolutionDefaults(config);
200
+ const mode = resolvedConfig.mode ?? "auto";
201
+ if (mode !== "source-only" && resolvedConfig.compiledDir) {
202
+ const compiledFile = resolve2(resolvedConfig.compiledDir, promptPath + ".json");
193
203
  if (existsSync(compiledFile)) {
194
204
  if (mode === "auto") {
195
- const sourceFile = resolve2(config.sourceDir, promptPath + ".md");
205
+ const sourceFile = resolve2(resolvedConfig.sourceDir, promptPath + ".md");
196
206
  if (existsSync(sourceFile)) {
197
207
  const compiledMtime = statSync2(compiledFile).mtimeMs;
198
208
  const sourceMtime = statSync2(sourceFile).mtimeMs;
@@ -215,8 +225,8 @@ Run "promptopskit compile" to generate it.`
215
225
  }
216
226
  }
217
227
  if (mode !== "compiled-only") {
218
- const sourceFile = resolve2(config.sourceDir, promptPath + ".md");
219
- if (config.cache !== false) {
228
+ const sourceFile = resolve2(resolvedConfig.sourceDir, promptPath + ".md");
229
+ if (resolvedConfig.cache !== false) {
220
230
  const cached = promptCache.get(sourceFile);
221
231
  if (cached) {
222
232
  return cached;
@@ -224,8 +234,8 @@ Run "promptopskit compile" to generate it.`
224
234
  }
225
235
  if (!existsSync(sourceFile)) {
226
236
  const paths = [sourceFile];
227
- if (config.compiledDir) {
228
- paths.unshift(resolve2(config.compiledDir, promptPath + ".json"));
237
+ if (resolvedConfig.compiledDir) {
238
+ paths.unshift(resolve2(resolvedConfig.compiledDir, promptPath + ".json"));
229
239
  }
230
240
  throw new Error(
231
241
  `Prompt not found: "${promptPath}"
@@ -233,8 +243,8 @@ Searched:
233
243
  ${paths.map((candidate) => ` - ${candidate}`).join("\n")}`
234
244
  );
235
245
  }
236
- const { asset } = await loadPromptFile(sourceFile, { defaultsRoot: config.sourceDir });
237
- if (config.cache !== false) {
246
+ const { asset } = await loadPromptFile(sourceFile, { defaultsRoot: resolvedConfig.sourceDir });
247
+ if (resolvedConfig.cache !== false) {
238
248
  promptCache.set(sourceFile, asset);
239
249
  }
240
250
  return asset;
@@ -242,8 +252,9 @@ ${paths.map((candidate) => ` - ${candidate}`).join("\n")}`
242
252
  throw new Error(`Prompt not found: "${promptPath}"`);
243
253
  }
244
254
  async function resolvePromptAsset(promptPath, config, options = {}, promptCache = sharedPromptCache) {
245
- let asset = await loadPromptAsset(promptPath, config, promptCache);
246
- const sourceFile = resolve2(config.sourceDir, promptPath + ".md");
255
+ const resolvedConfig = withPromptResolutionDefaults(config);
256
+ let asset = await loadPromptAsset(promptPath, resolvedConfig, promptCache);
257
+ const sourceFile = resolve2(resolvedConfig.sourceDir, promptPath + ".md");
247
258
  if (asset.includes && asset.includes.length > 0 && existsSync(sourceFile)) {
248
259
  asset = await resolveIncludes(asset, sourceFile);
249
260
  }
@@ -265,7 +276,7 @@ function resolveInlinePromptSource(source, options = {}) {
265
276
 
266
277
  // src/providers/prompt-input.ts
267
278
  function isPromptLookup(input) {
268
- return "path" in input && typeof input.path === "string" && "sourceDir" in input;
279
+ return "path" in input && typeof input.path === "string";
269
280
  }
270
281
  function isInlinePromptSource(input) {
271
282
  return "source" in input && typeof input.source === "string";
@@ -303,9 +314,11 @@ export {
303
314
  resolveAssetForProvider,
304
315
  PromptCache,
305
316
  resolveIncludes,
317
+ DEFAULT_PROMPTS_DIR,
318
+ withPromptResolutionDefaults,
306
319
  loadPromptAsset,
307
320
  resolvePromptAsset,
308
321
  resolveInlinePromptSource,
309
322
  withPromptInputSupport
310
323
  };
311
- //# sourceMappingURL=chunk-S5YHGHK5.js.map
324
+ //# sourceMappingURL=chunk-M5VKRDIY.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/renderer/interpolate.ts","../src/renderer/renderer.ts","../src/overrides/apply-overrides.ts","../src/providers/resolve-asset.ts","../src/cache.ts","../src/composition/resolve-includes.ts","../src/prompt-resolution.ts","../src/providers/prompt-input.ts"],"sourcesContent":["export interface InterpolateOptions {\n strict?: boolean;\n}\n\nconst VARIABLE_RE = /\\{\\{\\s*([a-zA-Z_][a-zA-Z0-9_]*)\\s*\\}\\}/g;\nconst ESCAPED_OPEN = /\\\\\\{\\\\\\{/g;\nconst ESCAPE_PLACEHOLDER = '\\x00ESCAPED_OPEN\\x00';\n\n/**\n * Interpolate variables into a template string.\n *\n * Syntax: {{ variable_name }}\n * Escape: \\{\\{ produces literal {{\n *\n * In strict mode, throws on missing variables.\n * In permissive mode, leaves {{ placeholder }} intact.\n */\nexport function interpolate(\n template: string,\n variables: Record<string, string>,\n options: InterpolateOptions = {},\n): string {\n const { strict = false } = options;\n\n // Replace escaped sequences with placeholder\n let result = template.replace(ESCAPED_OPEN, ESCAPE_PLACEHOLDER);\n\n result = result.replace(VARIABLE_RE, (match, name: string) => {\n if (name in variables) {\n return variables[name];\n }\n if (strict) {\n throw new Error(`Missing required variable: \"${name}\"`);\n }\n return match; // leave placeholder intact in permissive mode\n });\n\n // Restore escaped sequences\n result = result.replaceAll(ESCAPE_PLACEHOLDER, '{{');\n\n return result;\n}\n\n/**\n * Extract all variable names referenced in a template.\n */\nexport function extractVariables(template: string): string[] {\n const vars = new Set<string>();\n let match: RegExpExecArray | null;\n const re = new RegExp(VARIABLE_RE.source, 'g');\n while ((match = re.exec(template)) !== null) {\n vars.add(match[1]);\n }\n return [...vars];\n}\n","import type { ResolvedPromptAsset } from '../schema/index.js';\nimport { interpolate, extractVariables } from './interpolate.js';\n\nexport interface RenderOptions {\n variables?: Record<string, string>;\n strict?: boolean;\n}\n\nexport interface RenderedSections {\n system_instructions?: string;\n prompt_template?: string;\n}\n\n/**\n * Render the sections of a resolved prompt asset with variable interpolation.\n */\nexport function renderSections(\n asset: ResolvedPromptAsset,\n options: RenderOptions = {},\n): RenderedSections {\n const { variables = {}, strict = false } = options;\n\n const result: RenderedSections = {};\n\n if (asset.sections.system_instructions) {\n result.system_instructions = interpolate(\n asset.sections.system_instructions,\n variables,\n { strict },\n );\n }\n\n if (asset.sections.prompt_template) {\n result.prompt_template = interpolate(\n asset.sections.prompt_template,\n variables,\n { strict },\n );\n }\n\n return result;\n}\n\n/**\n * Get all variable names used across all sections.\n */\nexport function getRequiredVariables(asset: ResolvedPromptAsset): string[] {\n const vars = new Set<string>();\n\n if (asset.sections.system_instructions) {\n for (const v of extractVariables(asset.sections.system_instructions)) {\n vars.add(v);\n }\n }\n\n if (asset.sections.prompt_template) {\n for (const v of extractVariables(asset.sections.prompt_template)) {\n vars.add(v);\n }\n }\n\n return [...vars];\n}\n","import type { PromptAsset, PromptAssetOverrides } from '../schema/index.js';\n\nexport interface OverrideOptions {\n environment?: string;\n tier?: string;\n runtime?: Partial<PromptAssetOverrides>;\n}\n\n/**\n * Apply environment, tier, and runtime overrides to a prompt asset.\n *\n * Precedence: base → environment → tier → runtime\n * Scalars are replaced. Arrays are replaced (not concatenated).\n */\nexport function applyOverrides(\n asset: PromptAsset,\n options: OverrideOptions = {},\n): PromptAsset {\n let result = { ...asset };\n\n // Apply environment override\n if (options.environment && result.environments?.[options.environment]) {\n result = mergeOverride(result, result.environments[options.environment]);\n }\n\n // Apply tier override\n if (options.tier && result.tiers?.[options.tier]) {\n result = mergeOverride(result, result.tiers[options.tier]);\n }\n\n // Apply runtime overrides\n if (options.runtime) {\n result = mergeOverride(result, options.runtime);\n }\n\n return result;\n}\n\nfunction mergeOverride(\n base: PromptAsset,\n override: Partial<PromptAssetOverrides>,\n): PromptAsset {\n const result = { ...base };\n\n if (override.model !== undefined) result.model = override.model;\n if (override.fallback_models !== undefined) result.fallback_models = override.fallback_models;\n if (override.tools !== undefined) result.tools = override.tools;\n\n if (override.reasoning !== undefined) {\n result.reasoning = { ...result.reasoning, ...override.reasoning };\n }\n if (override.sampling !== undefined) {\n result.sampling = { ...result.sampling, ...override.sampling };\n }\n if (override.response !== undefined) {\n result.response = { ...result.response, ...override.response };\n }\n\n return result;\n}\n","import { applyOverrides } from '../overrides/index.js';\nimport type { ResolvedPromptAsset } from '../schema/index.js';\nimport type { RuntimeRenderOptions } from './types.js';\n\nexport function resolveAssetForProvider(\n asset: ResolvedPromptAsset,\n runtime: Pick<RuntimeRenderOptions, 'environment' | 'tier' | 'runtime'> = {},\n): ResolvedPromptAsset {\n if (!runtime.environment && !runtime.tier && !runtime.runtime) {\n return asset;\n }\n\n return applyOverrides(asset, {\n environment: runtime.environment,\n tier: runtime.tier,\n runtime: runtime.runtime,\n }) as ResolvedPromptAsset;\n}","import { statSync } from 'node:fs';\n\ninterface CacheEntry<T> {\n value: T;\n mtime: number;\n}\n\n/**\n * Simple in-memory LRU cache keyed by file path + mtime.\n * Used to avoid re-parsing prompt files on every renderPrompt() call.\n */\nexport class PromptCache<T> {\n private cache = new Map<string, CacheEntry<T>>();\n private maxSize: number;\n\n constructor(maxSize = 100) {\n this.maxSize = maxSize;\n }\n\n get(filePath: string): T | undefined {\n const entry = this.cache.get(filePath);\n if (!entry) return undefined;\n\n try {\n const stat = statSync(filePath);\n if (stat.mtimeMs !== entry.mtime) {\n this.cache.delete(filePath);\n return undefined;\n }\n } catch {\n this.cache.delete(filePath);\n return undefined;\n }\n\n // Refresh recency for true LRU semantics.\n this.cache.delete(filePath);\n this.cache.set(filePath, entry);\n\n return entry.value;\n }\n\n set(filePath: string, value: T): void {\n try {\n const stat = statSync(filePath);\n\n if (this.cache.has(filePath)) {\n this.cache.delete(filePath);\n } else if (this.cache.size >= this.maxSize) {\n const oldest = this.cache.keys().next().value;\n if (oldest) this.cache.delete(oldest);\n }\n\n this.cache.set(filePath, { value, mtime: stat.mtimeMs });\n } catch {\n // Can't stat the file — don't cache\n }\n }\n\n clear(): void {\n this.cache.clear();\n }\n\n get size(): number {\n return this.cache.size;\n }\n}\n","import { readFile } from 'node:fs/promises';\nimport { resolve, dirname } from 'node:path';\nimport { parsePrompt } from '../parser/index.js';\nimport type { PromptAsset } from '../schema/index.js';\n\n/**\n * Resolve includes for a prompt asset, inlining content from referenced files.\n * Detects and rejects circular includes.\n */\nexport async function resolveIncludes(\n asset: PromptAsset,\n basePath: string,\n visited: Set<string> = new Set(),\n): Promise<PromptAsset> {\n if (!asset.includes || asset.includes.length === 0) {\n return asset;\n }\n\n const baseDir = dirname(basePath);\n const resolvedPath = resolve(basePath);\n\n if (visited.has(resolvedPath)) {\n throw new Error(`Circular include detected: ${resolvedPath}`);\n }\n visited.add(resolvedPath);\n\n let mergedSystemInstructions = '';\n\n for (const includePath of asset.includes) {\n const fullPath = resolve(baseDir, includePath);\n\n if (visited.has(fullPath)) {\n throw new Error(`Circular include detected: ${fullPath} (included from ${basePath})`);\n }\n\n const content = await readFile(fullPath, 'utf-8');\n const { asset: includedAsset } = parsePrompt(content, fullPath);\n\n // Recursively resolve nested includes\n const resolved = await resolveIncludes(includedAsset, fullPath, new Set(visited));\n\n // Append included system instructions before local ones\n if (resolved.sections?.system_instructions) {\n mergedSystemInstructions += resolved.sections.system_instructions + '\\n\\n';\n }\n }\n\n // Prepend included system instructions before the local ones\n const localSystem = asset.sections?.system_instructions ?? '';\n const combinedSystem = (mergedSystemInstructions + localSystem).trim() || undefined;\n\n return {\n ...asset,\n sections: {\n ...asset.sections,\n system_instructions: combinedSystem,\n },\n // Drop includes from the resolved asset — they've been inlined\n includes: undefined,\n };\n}\n","import { readFile } from 'node:fs/promises';\nimport { existsSync, statSync } from 'node:fs';\nimport { resolve } from 'node:path';\n\nimport { PromptCache } from './cache.js';\nimport { resolveIncludes } from './composition/index.js';\nimport { applyOverrides } from './overrides/index.js';\nimport { parsePrompt, loadPromptFile } from './parser/index.js';\nimport type { PromptAsset, PromptAssetOverrides, ResolvedPromptAsset } from './schema/index.js';\n\nexport type PromptResolutionMode = 'auto' | 'compiled-only' | 'source-only';\n\nexport const DEFAULT_PROMPTS_DIR = './prompts';\nexport const DEFAULT_COMPILED_JSON_DIR = './.generated-prompts/json';\nexport const DEFAULT_COMPILED_ESM_DIR = './.generated-prompts/esm';\n\nexport interface PromptResolutionConfig {\n sourceDir?: string;\n compiledDir?: string;\n mode?: PromptResolutionMode;\n cache?: boolean;\n}\n\nexport interface ResolvedPromptResolutionConfig {\n sourceDir: string;\n compiledDir: string;\n mode?: PromptResolutionMode;\n cache?: boolean;\n}\n\nexport function defaultCompiledDirForFormat(format: 'json' | 'esm'): string {\n return format === 'esm' ? DEFAULT_COMPILED_ESM_DIR : DEFAULT_COMPILED_JSON_DIR;\n}\n\nexport function withPromptResolutionDefaults(config: PromptResolutionConfig): ResolvedPromptResolutionConfig {\n return {\n ...config,\n sourceDir: config.sourceDir ?? DEFAULT_PROMPTS_DIR,\n compiledDir: config.compiledDir ?? DEFAULT_COMPILED_JSON_DIR,\n };\n}\n\nconst sharedPromptCache = new PromptCache<PromptAsset>();\n\nexport async function loadPromptAsset(\n promptPath: string,\n config: PromptResolutionConfig,\n promptCache: PromptCache<PromptAsset> = sharedPromptCache,\n): Promise<PromptAsset> {\n const resolvedConfig = withPromptResolutionDefaults(config);\n const mode = resolvedConfig.mode ?? 'auto';\n\n if (mode !== 'source-only' && resolvedConfig.compiledDir) {\n const compiledFile = resolve(resolvedConfig.compiledDir, promptPath + '.json');\n if (existsSync(compiledFile)) {\n if (mode === 'auto') {\n const sourceFile = resolve(resolvedConfig.sourceDir, promptPath + '.md');\n if (existsSync(sourceFile)) {\n const compiledMtime = statSync(compiledFile).mtimeMs;\n const sourceMtime = statSync(sourceFile).mtimeMs;\n if (sourceMtime > compiledMtime) {\n console.warn(\n `[promptopskit] Warning: compiled artifact for \"${promptPath}\" is older than source .md file.\\n`\n + ' Run \"promptopskit compile\" or switch to source-only mode.',\n );\n }\n }\n }\n\n const content = await readFile(compiledFile, 'utf-8');\n return JSON.parse(content) as PromptAsset;\n }\n\n if (mode === 'compiled-only') {\n throw new Error(\n `Compiled artifact not found: ${compiledFile}\\n`\n + 'Run \"promptopskit compile\" to generate it.',\n );\n }\n }\n\n if (mode !== 'compiled-only') {\n const sourceFile = resolve(resolvedConfig.sourceDir, promptPath + '.md');\n\n if (resolvedConfig.cache !== false) {\n const cached = promptCache.get(sourceFile);\n if (cached) {\n return cached;\n }\n }\n\n if (!existsSync(sourceFile)) {\n const paths = [sourceFile];\n if (resolvedConfig.compiledDir) {\n paths.unshift(resolve(resolvedConfig.compiledDir, promptPath + '.json'));\n }\n\n throw new Error(\n `Prompt not found: \"${promptPath}\"\\nSearched:\\n${paths.map((candidate) => ` - ${candidate}`).join('\\n')}`,\n );\n }\n\n const { asset } = await loadPromptFile(sourceFile, { defaultsRoot: resolvedConfig.sourceDir });\n\n if (resolvedConfig.cache !== false) {\n promptCache.set(sourceFile, asset);\n }\n\n return asset;\n }\n\n throw new Error(`Prompt not found: \"${promptPath}\"`);\n}\n\nexport async function resolvePromptAsset(\n promptPath: string,\n config: PromptResolutionConfig,\n options: { environment?: string; tier?: string; runtime?: Partial<PromptAssetOverrides> } = {},\n promptCache: PromptCache<PromptAsset> = sharedPromptCache,\n): Promise<ResolvedPromptAsset> {\n const resolvedConfig = withPromptResolutionDefaults(config);\n let asset = await loadPromptAsset(promptPath, resolvedConfig, promptCache);\n\n const sourceFile = resolve(resolvedConfig.sourceDir, promptPath + '.md');\n if (asset.includes && asset.includes.length > 0 && existsSync(sourceFile)) {\n asset = await resolveIncludes(asset, sourceFile);\n }\n\n asset = applyOverrides(asset, {\n environment: options.environment,\n tier: options.tier,\n runtime: options.runtime,\n });\n\n return asset as ResolvedPromptAsset;\n}\n\nexport function resolveInlinePromptSource(\n source: string,\n options: { environment?: string; tier?: string; runtime?: Partial<PromptAssetOverrides> } = {},\n): ResolvedPromptAsset {\n const { asset } = parsePrompt(source);\n return applyOverrides(asset, {\n environment: options.environment,\n tier: options.tier,\n runtime: options.runtime,\n }) as ResolvedPromptAsset;\n}","import { resolveInlinePromptSource, resolvePromptAsset } from '../prompt-resolution.js';\nimport type { ResolvedPromptAsset } from '../schema/index.js';\nimport type {\n ProviderAdapter,\n ProviderPromptInput,\n RenderPromptMethod,\n RuntimeRenderOptions,\n ValidatePromptMethod,\n} from './types.js';\n\ntype SyncProviderAdapter = Omit<ProviderAdapter, 'validatePrompt' | 'renderPrompt'>;\n\nfunction isPromptLookup(input: ProviderPromptInput): input is Extract<ProviderPromptInput, { path: string }> {\n return 'path' in input && typeof input.path === 'string';\n}\n\nfunction isInlinePromptSource(input: ProviderPromptInput): input is Extract<ProviderPromptInput, { source: string }> {\n return 'source' in input && typeof input.source === 'string';\n}\n\nasync function resolveProviderPromptInput(\n input: ProviderPromptInput,\n runtime?: Pick<RuntimeRenderOptions, 'environment' | 'tier' | 'runtime'>,\n): Promise<ResolvedPromptAsset> {\n if (isPromptLookup(input)) {\n return resolvePromptAsset(input.path, input, runtime);\n }\n\n if (isInlinePromptSource(input)) {\n return resolveInlinePromptSource(input.source, runtime);\n }\n\n return input;\n}\n\nexport function withPromptInputSupport(adapter: SyncProviderAdapter): ProviderAdapter {\n const validatePrompt: ValidatePromptMethod = async (input, runtime) => {\n const resolved = await resolveProviderPromptInput(input, runtime);\n return adapter.validate(resolved, runtime);\n };\n\n const renderPrompt: RenderPromptMethod = async (input, runtime) => {\n const resolved = await resolveProviderPromptInput(input, runtime);\n return adapter.render(resolved, runtime);\n };\n\n return {\n ...adapter,\n validatePrompt,\n renderPrompt,\n };\n}"],"mappings":"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","names":["readFile","statSync","resolve","resolve","statSync","readFile"]}
@@ -1,9 +1,9 @@
1
1
  import {
2
2
  openaiAdapter
3
- } from "./chunk-VU3WKLFI.js";
3
+ } from "./chunk-2X5HFPSD.js";
4
4
  import {
5
5
  withPromptInputSupport
6
- } from "./chunk-S5YHGHK5.js";
6
+ } from "./chunk-M5VKRDIY.js";
7
7
 
8
8
  // src/providers/openrouter.ts
9
9
  var openrouterAdapter = withPromptInputSupport({
@@ -23,4 +23,4 @@ var openrouterAdapter = withPromptInputSupport({
23
23
  export {
24
24
  openrouterAdapter
25
25
  };
26
- //# sourceMappingURL=chunk-U7IDX2P7.js.map
26
+ //# sourceMappingURL=chunk-ROBYCHAW.js.map
package/dist/cli/index.js CHANGED
@@ -568,6 +568,99 @@ async function collectPromptFiles(dir) {
568
568
  // src/cli/commands/compile.ts
569
569
  import { readdir as readdir2, writeFile, mkdir, rm } from "fs/promises";
570
570
  import { join as join3, extname as extname2, relative, dirname as dirname3 } from "path";
571
+
572
+ // src/prompt-resolution.ts
573
+ import { readFile as readFile3 } from "fs/promises";
574
+ import { existsSync, statSync as statSync2 } from "fs";
575
+ import { resolve as resolve3 } from "path";
576
+
577
+ // src/cache.ts
578
+ import { statSync } from "fs";
579
+ var PromptCache = class {
580
+ cache = /* @__PURE__ */ new Map();
581
+ maxSize;
582
+ constructor(maxSize = 100) {
583
+ this.maxSize = maxSize;
584
+ }
585
+ get(filePath) {
586
+ const entry = this.cache.get(filePath);
587
+ if (!entry) return void 0;
588
+ try {
589
+ const stat = statSync(filePath);
590
+ if (stat.mtimeMs !== entry.mtime) {
591
+ this.cache.delete(filePath);
592
+ return void 0;
593
+ }
594
+ } catch {
595
+ this.cache.delete(filePath);
596
+ return void 0;
597
+ }
598
+ this.cache.delete(filePath);
599
+ this.cache.set(filePath, entry);
600
+ return entry.value;
601
+ }
602
+ set(filePath, value) {
603
+ try {
604
+ const stat = statSync(filePath);
605
+ if (this.cache.has(filePath)) {
606
+ this.cache.delete(filePath);
607
+ } else if (this.cache.size >= this.maxSize) {
608
+ const oldest = this.cache.keys().next().value;
609
+ if (oldest) this.cache.delete(oldest);
610
+ }
611
+ this.cache.set(filePath, { value, mtime: stat.mtimeMs });
612
+ } catch {
613
+ }
614
+ }
615
+ clear() {
616
+ this.cache.clear();
617
+ }
618
+ get size() {
619
+ return this.cache.size;
620
+ }
621
+ };
622
+
623
+ // src/overrides/apply-overrides.ts
624
+ function applyOverrides(asset, options = {}) {
625
+ let result = { ...asset };
626
+ if (options.environment && result.environments?.[options.environment]) {
627
+ result = mergeOverride(result, result.environments[options.environment]);
628
+ }
629
+ if (options.tier && result.tiers?.[options.tier]) {
630
+ result = mergeOverride(result, result.tiers[options.tier]);
631
+ }
632
+ if (options.runtime) {
633
+ result = mergeOverride(result, options.runtime);
634
+ }
635
+ return result;
636
+ }
637
+ function mergeOverride(base, override) {
638
+ const result = { ...base };
639
+ if (override.model !== void 0) result.model = override.model;
640
+ if (override.fallback_models !== void 0) result.fallback_models = override.fallback_models;
641
+ if (override.tools !== void 0) result.tools = override.tools;
642
+ if (override.reasoning !== void 0) {
643
+ result.reasoning = { ...result.reasoning, ...override.reasoning };
644
+ }
645
+ if (override.sampling !== void 0) {
646
+ result.sampling = { ...result.sampling, ...override.sampling };
647
+ }
648
+ if (override.response !== void 0) {
649
+ result.response = { ...result.response, ...override.response };
650
+ }
651
+ return result;
652
+ }
653
+
654
+ // src/prompt-resolution.ts
655
+ var DEFAULT_PROMPTS_DIR = "./prompts";
656
+ var DEFAULT_COMPILED_JSON_DIR = "./.generated-prompts/json";
657
+ var DEFAULT_COMPILED_ESM_DIR = "./.generated-prompts/esm";
658
+ function defaultCompiledDirForFormat(format) {
659
+ return format === "esm" ? DEFAULT_COMPILED_ESM_DIR : DEFAULT_COMPILED_JSON_DIR;
660
+ }
661
+ var sharedPromptCache = new PromptCache();
662
+
663
+ // src/cli/commands/compile.ts
571
664
  var HELP2 = `
572
665
  promptopskit compile [sourceDir] [outputDir] [options]
573
666
 
@@ -594,8 +687,8 @@ async function compile(args) {
594
687
  console.error(`Error: Unknown format "${format}". Use "json" or "esm".`);
595
688
  process.exit(1);
596
689
  }
597
- const sourceDir = getFlag(args, "--source", "-s") ?? positional[0] ?? "./prompts";
598
- const outputDir = getFlag(args, "--output", "-o") ?? positional[1] ?? defaultOutputDirForFormat(format);
690
+ const sourceDir = getFlag(args, "--source", "-s") ?? positional[0] ?? DEFAULT_PROMPTS_DIR;
691
+ const outputDir = getFlag(args, "--output", "-o") ?? positional[1] ?? defaultCompiledDirForFormat(format);
599
692
  const files = await collectPromptFiles2(sourceDir);
600
693
  if (files.length === 0) {
601
694
  console.log(`No .md prompt files found in ${sourceDir}`);
@@ -644,9 +737,6 @@ async function compile(args) {
644
737
  process.exit(1);
645
738
  }
646
739
  }
647
- function defaultOutputDirForFormat(format) {
648
- return format === "esm" ? "./.generated-prompts/esm" : "./.generated-prompts/json";
649
- }
650
740
  function getPositionalArgs(args, flagsWithValues) {
651
741
  const positional = [];
652
742
  for (let index = 0; index < args.length; index++) {
@@ -683,49 +773,18 @@ async function collectPromptFiles2(dir) {
683
773
  }
684
774
 
685
775
  // src/cli/commands/render.ts
686
- import { readFile as readFile3 } from "fs/promises";
687
- import { existsSync as existsSync2 } from "fs";
688
-
689
- // src/overrides/apply-overrides.ts
690
- function applyOverrides(asset, options = {}) {
691
- let result = { ...asset };
692
- if (options.environment && result.environments?.[options.environment]) {
693
- result = mergeOverride(result, result.environments[options.environment]);
694
- }
695
- if (options.tier && result.tiers?.[options.tier]) {
696
- result = mergeOverride(result, result.tiers[options.tier]);
697
- }
698
- if (options.runtime) {
699
- result = mergeOverride(result, options.runtime);
700
- }
701
- return result;
702
- }
703
- function mergeOverride(base, override) {
704
- const result = { ...base };
705
- if (override.model !== void 0) result.model = override.model;
706
- if (override.fallback_models !== void 0) result.fallback_models = override.fallback_models;
707
- if (override.tools !== void 0) result.tools = override.tools;
708
- if (override.reasoning !== void 0) {
709
- result.reasoning = { ...result.reasoning, ...override.reasoning };
710
- }
711
- if (override.sampling !== void 0) {
712
- result.sampling = { ...result.sampling, ...override.sampling };
713
- }
714
- if (override.response !== void 0) {
715
- result.response = { ...result.response, ...override.response };
716
- }
717
- return result;
718
- }
776
+ import { readFile as readFile4 } from "fs/promises";
777
+ import { existsSync as existsSync3 } from "fs";
719
778
 
720
779
  // src/cli/commands/defaults-root.ts
721
- import { existsSync } from "fs";
722
- import { dirname as dirname4, join as join4, resolve as resolve3 } from "path";
780
+ import { existsSync as existsSync2 } from "fs";
781
+ import { dirname as dirname4, join as join4, resolve as resolve4 } from "path";
723
782
  function findDefaultsRoot(filePath) {
724
- let current = resolve3(dirname4(filePath));
783
+ let current = resolve4(dirname4(filePath));
725
784
  let root = current;
726
785
  let foundDefaults = false;
727
786
  while (true) {
728
- if (existsSync(join4(current, "defaults.md"))) {
787
+ if (existsSync2(join4(current, "defaults.md"))) {
729
788
  root = current;
730
789
  foundDefaults = true;
731
790
  } else if (foundDefaults) {
@@ -768,13 +827,13 @@ async function render(args) {
768
827
  const jsonOutput = args.includes("--json");
769
828
  let variables = {};
770
829
  if (varsFile) {
771
- const varsContent = await readFile3(varsFile, "utf-8");
830
+ const varsContent = await readFile4(varsFile, "utf-8");
772
831
  variables = JSON.parse(varsContent);
773
832
  } else {
774
833
  const sidecarPath = file.replace(/\.md$/, ".test.yaml");
775
- if (existsSync2(sidecarPath)) {
834
+ if (existsSync3(sidecarPath)) {
776
835
  const { default: yaml } = await import("gray-matter");
777
- const sidecarContent = await readFile3(sidecarPath, "utf-8");
836
+ const sidecarContent = await readFile4(sidecarPath, "utf-8");
778
837
  const parsed2 = yaml(`---
779
838
  ${sidecarContent}---
780
839
  `);
@@ -859,7 +918,7 @@ async function inspect(args) {
859
918
  // src/cli/commands/init.ts
860
919
  import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
861
920
  import { join as join5, dirname as dirname5 } from "path";
862
- import { existsSync as existsSync3, readFileSync } from "fs";
921
+ import { existsSync as existsSync4, readFileSync } from "fs";
863
922
  var HELP5 = `
864
923
  promptopskit init [dir]
865
924
 
@@ -1010,7 +1069,7 @@ async function init(args) {
1010
1069
  let created = 0;
1011
1070
  let skipped = 0;
1012
1071
  for (const file of files) {
1013
- if (existsSync3(file.path)) {
1072
+ if (existsSync4(file.path)) {
1014
1073
  console.log(` skip ${file.path} (already exists)`);
1015
1074
  skipped++;
1016
1075
  continue;
@@ -1022,7 +1081,7 @@ async function init(args) {
1022
1081
  }
1023
1082
  console.log();
1024
1083
  console.log(`Created ${created} file(s), skipped ${skipped} existing.`);
1025
- if (existsSync3("package.json")) {
1084
+ if (existsSync4("package.json")) {
1026
1085
  try {
1027
1086
  const pkg = JSON.parse(readFileSync("package.json", "utf-8"));
1028
1087
  if (!pkg.scripts?.["build:prompts"]) {
@@ -1036,9 +1095,9 @@ async function init(args) {
1036
1095
  }
1037
1096
 
1038
1097
  // src/cli/commands/skill.ts
1039
- import { writeFile as writeFile3, readFile as readFile4, mkdir as mkdir3 } from "fs/promises";
1098
+ import { writeFile as writeFile3, readFile as readFile5, mkdir as mkdir3 } from "fs/promises";
1040
1099
  import { dirname as dirname6 } from "path";
1041
- import { existsSync as existsSync4 } from "fs";
1100
+ import { existsSync as existsSync5 } from "fs";
1042
1101
  var MARKER_START = "<!-- promptopskit:start -->";
1043
1102
  var MARKER_END = "<!-- promptopskit:end -->";
1044
1103
  var HELP6 = `
@@ -1124,8 +1183,8 @@ async function skill(args) {
1124
1183
  continue;
1125
1184
  }
1126
1185
  const markedContent = config.wrap(wrapMarkers(STUB_CONTENT));
1127
- if (existsSync4(filePath) && !force) {
1128
- const existing = await readFile4(filePath, "utf-8");
1186
+ if (existsSync5(filePath) && !force) {
1187
+ const existing = await readFile5(filePath, "utf-8");
1129
1188
  const merged = mergeContent(existing, markedContent);
1130
1189
  if (merged === existing) {
1131
1190
  console.log(` skip ${filePath} (already up to date)`);
@@ -1148,13 +1207,13 @@ async function skill(args) {
1148
1207
  }
1149
1208
  async function deployClaude(filePath, force) {
1150
1209
  const content = CLAUDE_LINE + "\n";
1151
- if (!existsSync4(filePath) || force) {
1210
+ if (!existsSync5(filePath) || force) {
1152
1211
  await mkdir3(dirname6(filePath), { recursive: true });
1153
1212
  await writeFile3(filePath, content, "utf-8");
1154
1213
  console.log(` \u2713 ${filePath}`);
1155
1214
  return 1;
1156
1215
  }
1157
- const existing = await readFile4(filePath, "utf-8");
1216
+ const existing = await readFile5(filePath, "utf-8");
1158
1217
  if (existing.split("\n").some((line) => line.trim() === CLAUDE_LINE)) {
1159
1218
  console.log(` skip ${filePath} (already up to date)`);
1160
1219
  return 0;