promptopskit 0.5.0 → 0.7.0

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-QK3WKE3K.js → chunk-35NN5F6O.js} +2 -2
  4. package/dist/{chunk-D75HKF64.js → chunk-3G3EAV7H.js} +2 -2
  5. package/dist/{chunk-LNGHGGJN.js → chunk-6NOUETAC.js} +2 -2
  6. package/dist/{chunk-KN7H4JRK.js → chunk-F552OBUV.js} +619 -502
  7. package/dist/chunk-F552OBUV.js.map +1 -0
  8. package/dist/{chunk-6BOUQVSX.js → chunk-FICB2ZSK.js} +3 -3
  9. package/dist/{chunk-6VLKZCNS.js → chunk-OWMP5NMV.js} +2 -2
  10. package/dist/{chunk-IN4KT5EU.js → chunk-R65RAHAG.js} +3 -3
  11. package/dist/{chunk-K6ZU3CGV.js → chunk-ULVY473P.js} +306 -17
  12. package/dist/chunk-ULVY473P.js.map +1 -0
  13. package/dist/cli/index.js +446 -103
  14. package/dist/cli/index.js.map +1 -1
  15. package/dist/index.cjs +1223 -772
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +9 -5
  18. package/dist/index.d.ts +9 -5
  19. package/dist/index.js +67 -18
  20. package/dist/index.js.map +1 -1
  21. package/dist/providers/anthropic.cjs +902 -502
  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 +902 -502
  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 +901 -501
  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 +902 -502
  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 +902 -502
  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 +902 -502
  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-DBcSns_b.d.cts} +732 -206
  52. package/dist/{schema-D-RI4w-k.d.ts → schema-DBcSns_b.d.ts} +732 -206
  53. package/dist/testing.cjs +54 -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-8T2D5KB5.d.cts} +20 -2
  59. package/dist/{types-DbTAjlTN.d.cts → types-BlLXT5Qb.d.ts} +20 -2
  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-QK3WKE3K.js.map → chunk-35NN5F6O.js.map} +0 -0
  66. /package/dist/{chunk-D75HKF64.js.map → chunk-3G3EAV7H.js.map} +0 -0
  67. /package/dist/{chunk-LNGHGGJN.js.map → chunk-6NOUETAC.js.map} +0 -0
  68. /package/dist/{chunk-6BOUQVSX.js.map → chunk-FICB2ZSK.js.map} +0 -0
  69. /package/dist/{chunk-6VLKZCNS.js.map → chunk-OWMP5NMV.js.map} +0 -0
  70. /package/dist/{chunk-IN4KT5EU.js.map → chunk-R65RAHAG.js.map} +0 -0
package/dist/cli/index.js CHANGED
@@ -5,7 +5,7 @@ import { readFileSync as readFileSync2 } from "fs";
5
5
 
6
6
  // src/cli/commands/validate.ts
7
7
  import { readdir, stat } from "fs/promises";
8
- import { join as join3, extname } from "path";
8
+ import { join as join3, extname as extname2 } from "path";
9
9
 
10
10
  // src/parser/parser.ts
11
11
  import matter from "gray-matter";
@@ -41,10 +41,26 @@ var ResponseSchema = z.object({
41
41
  format: z.enum(["text", "json", "markdown"]).optional(),
42
42
  stream: z.boolean().optional(),
43
43
  schema: z.record(z.unknown()).optional(),
44
+ schema_ref: z.string().min(1).optional(),
45
+ schema_source: z.object({
46
+ mode: z.enum(["inline", "schema_ref_json", "schema_ref_zod_module"]).optional(),
47
+ ref: z.string().optional(),
48
+ resolved_path: z.string().optional(),
49
+ hash: z.string().optional()
50
+ }).optional(),
44
51
  schema_name: z.string().optional(),
45
52
  schema_description: z.string().optional(),
46
53
  schema_strict: z.boolean().optional()
47
54
  });
55
+ var ResponseSchemaWithValidation = ResponseSchema.superRefine((value, ctx) => {
56
+ if (value.schema !== void 0 && value.schema_ref !== void 0) {
57
+ ctx.addIssue({
58
+ code: z.ZodIssueCode.custom,
59
+ message: "response.schema and response.schema_ref are mutually exclusive",
60
+ path: ["schema_ref"]
61
+ });
62
+ }
63
+ });
48
64
  var AnthropicProviderOptionsSchema = z.object({
49
65
  top_k: z.number().int().min(0).optional(),
50
66
  tool_choice: z.record(z.unknown()).optional(),
@@ -96,6 +112,14 @@ var RawProviderBodySchema = z.object({
96
112
  openrouter: z.record(z.unknown()).optional(),
97
113
  llmasaservice: z.record(z.unknown()).optional()
98
114
  });
115
+ var TheTokenCompanyCompressionSchema = z.object({
116
+ enabled: z.boolean().optional(),
117
+ model: z.string().min(1).optional(),
118
+ aggressiveness: z.number().min(0).max(1).optional()
119
+ });
120
+ var CompressionSchema = z.object({
121
+ thetokencompany: TheTokenCompanyCompressionSchema.optional()
122
+ });
99
123
  var OpenAICacheSchema = z.object({
100
124
  prompt_cache_key: z.string().min(1).optional(),
101
125
  retention: z.enum(["in_memory", "24h"]).optional()
@@ -136,6 +160,8 @@ var ContextBuiltInValidatorSchema = z.union([
136
160
  ]);
137
161
  var ContextInputDefinitionObjectSchema = z.object({
138
162
  name: z.string(),
163
+ optional: z.boolean().optional(),
164
+ warnings: z.boolean().optional(),
139
165
  max_size: z.number().int().positive().optional(),
140
166
  trim: z.union([z.boolean(), z.enum(["start", "end", "both"])]).optional(),
141
167
  allow_regex: ContextRegexSchema.optional(),
@@ -165,7 +191,8 @@ var PromptAssetOverridesSchema = z.object({
165
191
  fallback_models: z.array(z.string()).optional(),
166
192
  reasoning: ReasoningSchema.optional(),
167
193
  sampling: SamplingSchema.optional(),
168
- response: ResponseSchema.optional(),
194
+ response: ResponseSchemaWithValidation.optional(),
195
+ compression: CompressionSchema.optional(),
169
196
  cache: CacheSchema.optional(),
170
197
  raw: RawProviderBodySchema.optional(),
171
198
  tools: z.array(ToolRefSchema).optional(),
@@ -183,7 +210,20 @@ var SectionsSchema = z.object({
183
210
  var PromptDefaultsSchema = z.object({
184
211
  provider: z.enum(["openai", "openai-responses", "anthropic", "google", "gemini", "openrouter", "llmasaservice", "any"]).optional(),
185
212
  model: z.string().optional(),
213
+ fallback_models: z.array(z.string()).optional(),
214
+ reasoning: ReasoningSchema.optional(),
215
+ sampling: SamplingSchema.optional(),
216
+ response: ResponseSchema.optional(),
217
+ compression: CompressionSchema.optional(),
186
218
  cache: CacheSchema.optional(),
219
+ raw: RawProviderBodySchema.optional(),
220
+ tools: z.array(ToolRefSchema).optional(),
221
+ provider_options: ProviderOptionsSchema.optional(),
222
+ mcp: MCPSchema.optional(),
223
+ context: ContextSchema.optional(),
224
+ includes: z.array(z.string()).optional(),
225
+ environments: z.record(PromptAssetOverridesSchema).optional(),
226
+ tiers: z.record(PromptAssetOverridesSchema).optional(),
187
227
  metadata: MetadataSchema.optional(),
188
228
  sections: z.object({
189
229
  system_instructions: z.string().optional()
@@ -198,7 +238,8 @@ var PromptAssetSchema = z.object({
198
238
  fallback_models: z.array(z.string()).optional(),
199
239
  reasoning: ReasoningSchema.optional(),
200
240
  sampling: SamplingSchema.optional(),
201
- response: ResponseSchema.optional(),
241
+ response: ResponseSchemaWithValidation.optional(),
242
+ compression: CompressionSchema.optional(),
202
243
  cache: CacheSchema.optional(),
203
244
  raw: RawProviderBodySchema.optional(),
204
245
  tools: z.array(ToolRefSchema).optional(),
@@ -390,16 +431,122 @@ function parsePrompt(content, filePath) {
390
431
  }
391
432
 
392
433
  // src/parser/loader.ts
393
- import { readFile } from "fs/promises";
394
- import { dirname, join, resolve } from "path";
434
+ import { readFile as readFile2 } from "fs/promises";
435
+ import { dirname as dirname2, join, relative, resolve as resolve2 } from "path";
395
436
  import matter2 from "gray-matter";
437
+
438
+ // src/parser/response-schema-ref.ts
439
+ import { readFile } from "fs/promises";
440
+ import { createHash } from "crypto";
441
+ import { dirname, extname, resolve } from "path";
442
+ import { pathToFileURL } from "url";
443
+ import { z as z2 } from "zod";
444
+ import { zodToJsonSchema } from "zod-to-json-schema";
445
+ var SUPPORTED_SCHEMA_REF_EXTENSIONS = /* @__PURE__ */ new Set([".json", ".js", ".mjs", ".cjs"]);
446
+ async function resolveJsonSchemaRef(schemaRef, promptFilePath) {
447
+ const resolvedPath = resolve(dirname(promptFilePath), schemaRef);
448
+ let raw;
449
+ try {
450
+ raw = await readFile(resolvedPath, "utf-8");
451
+ } catch (error) {
452
+ const code = error.code;
453
+ if (code === "ENOENT") {
454
+ throw new Error(`POK050: response.schema_ref "${schemaRef}" not found (resolved from ${promptFilePath})`);
455
+ }
456
+ throw error;
457
+ }
458
+ let parsed;
459
+ try {
460
+ parsed = JSON.parse(raw);
461
+ } catch {
462
+ throw new Error(`POK050: response.schema_ref "${schemaRef}" is not valid JSON`);
463
+ }
464
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
465
+ throw new Error(`POK050: response.schema_ref "${schemaRef}" must resolve to a JSON object schema`);
466
+ }
467
+ return {
468
+ schema: parsed,
469
+ resolvedPath,
470
+ hash: createHash("sha256").update(raw).digest("hex")
471
+ };
472
+ }
473
+ async function resolveZodSchemaRef(schemaRef, promptFilePath) {
474
+ const resolvedPath = resolve(dirname(promptFilePath), schemaRef);
475
+ let moduleSource;
476
+ try {
477
+ moduleSource = await readFile(resolvedPath, "utf-8");
478
+ } catch (error) {
479
+ const code = error.code;
480
+ if (code === "ENOENT") {
481
+ throw new Error(`POK051: response.schema_ref "${schemaRef}" not found (resolved from ${promptFilePath})`);
482
+ }
483
+ throw error;
484
+ }
485
+ let imported;
486
+ try {
487
+ imported = await import(pathToFileURL(resolvedPath).href);
488
+ } catch (error) {
489
+ const message = error instanceof Error ? error.message : String(error);
490
+ throw new Error(`POK051: response.schema_ref "${schemaRef}" could not be imported as a module (${message})`);
491
+ }
492
+ const mod = imported;
493
+ const candidate = mod.default ?? mod.schema;
494
+ if (!(candidate instanceof z2.ZodType)) {
495
+ throw new Error('POK051: zod schema modules must export a Zod schema as default export or named export "schema"');
496
+ }
497
+ const jsonSchema = zodToJsonSchema(candidate, {
498
+ target: "jsonSchema7",
499
+ $refStrategy: "none"
500
+ });
501
+ if (!jsonSchema || typeof jsonSchema !== "object" || Array.isArray(jsonSchema)) {
502
+ throw new Error(`POK051: response.schema_ref "${schemaRef}" did not produce a valid JSON schema object`);
503
+ }
504
+ return {
505
+ schema: jsonSchema,
506
+ resolvedPath,
507
+ hash: createHash("sha256").update(moduleSource).digest("hex")
508
+ };
509
+ }
510
+ async function resolveResponseSchemaRef(asset, promptFilePath) {
511
+ const schemaRef = asset.response?.schema_ref;
512
+ if (!schemaRef) return asset;
513
+ const ext = extname(schemaRef).toLowerCase();
514
+ if (!SUPPORTED_SCHEMA_REF_EXTENSIONS.has(ext)) {
515
+ throw new Error(`POK051: response.schema_ref "${schemaRef}" has unsupported extension "${ext}". Use .json, .js, .mjs, or .cjs`);
516
+ }
517
+ const resolved = ext === ".json" ? await resolveJsonSchemaRef(schemaRef, promptFilePath) : await resolveZodSchemaRef(schemaRef, promptFilePath);
518
+ return {
519
+ ...asset,
520
+ response: {
521
+ ...asset.response,
522
+ schema: resolved.schema,
523
+ schema_ref: void 0,
524
+ schema_source: {
525
+ mode: ext === ".json" ? "schema_ref_json" : "schema_ref_zod_module",
526
+ ref: schemaRef,
527
+ resolved_path: resolved.resolvedPath,
528
+ hash: resolved.hash
529
+ }
530
+ }
531
+ };
532
+ }
533
+
534
+ // src/parser/loader.ts
396
535
  var DEFAULTS_FILE_NAME = "defaults.md";
397
536
  async function loadPromptFile(filePath, options = {}) {
398
- const content = await readFile(filePath, "utf-8");
537
+ const content = await readFile2(filePath, "utf-8");
399
538
  const parsed = parsePrompt(content, filePath);
400
- const root = options.defaultsRoot ?? dirname(filePath);
539
+ const root = options.defaultsRoot ?? dirname2(filePath);
401
540
  const defaults = await loadDefaultsForPath(filePath, root);
402
- const asset = applyDefaults(parsed.asset, defaults);
541
+ const withDefaults = applyDefaults(parsed.asset, defaults);
542
+ const resolved = await resolveResponseSchemaRef(withDefaults, filePath);
543
+ const asset = resolved.response?.schema !== void 0 && !resolved.response?.schema_source ? {
544
+ ...resolved,
545
+ response: {
546
+ ...resolved.response,
547
+ schema_source: { mode: "inline" }
548
+ }
549
+ } : resolved;
403
550
  return {
404
551
  ...parsed,
405
552
  asset
@@ -411,8 +558,8 @@ async function loadDefaultsForPath(filePath, defaultsRoot) {
411
558
  for (const dir of directories) {
412
559
  const defaultsPath = join(dir, DEFAULTS_FILE_NAME);
413
560
  try {
414
- const defaultsContent = await readFile(defaultsPath, "utf-8");
415
- const defaults = parseDefaults(defaultsContent);
561
+ const defaultsContent = await readFile2(defaultsPath, "utf-8");
562
+ const defaults = normalizeDefaultIncludes(parseDefaults(defaultsContent), defaultsPath, filePath);
416
563
  merged = mergeDefaults(merged, defaults);
417
564
  } catch (error) {
418
565
  if (error.code !== "ENOENT") {
@@ -424,14 +571,14 @@ async function loadDefaultsForPath(filePath, defaultsRoot) {
424
571
  }
425
572
  function getDirectoriesToCheck(filePath, defaultsRoot) {
426
573
  const dirs = [];
427
- let current = resolve(dirname(filePath));
428
- const boundary = defaultsRoot ? resolve(defaultsRoot) : void 0;
574
+ let current = resolve2(dirname2(filePath));
575
+ const boundary = defaultsRoot ? resolve2(defaultsRoot) : void 0;
429
576
  while (true) {
430
577
  dirs.unshift(current);
431
- if (boundary && current === boundary || current === dirname(current)) {
578
+ if (boundary && current === boundary || current === dirname2(current)) {
432
579
  break;
433
580
  }
434
- current = dirname(current);
581
+ current = dirname2(current);
435
582
  }
436
583
  return dirs;
437
584
  }
@@ -449,7 +596,20 @@ function mergeDefaults(base, local) {
449
596
  return {
450
597
  provider: local.provider ?? base.provider,
451
598
  model: local.model ?? base.model,
599
+ fallback_models: local.fallback_models ?? base.fallback_models,
600
+ reasoning: mergeRecordBlock(base.reasoning, local.reasoning),
601
+ sampling: mergeRecordBlock(base.sampling, local.sampling),
602
+ response: mergeRecordBlock(base.response, local.response),
603
+ compression: mergeCompression(base.compression, local.compression),
452
604
  cache: mergeCache(base.cache, local.cache),
605
+ raw: mergeRaw(base.raw, local.raw),
606
+ tools: local.tools ?? base.tools,
607
+ provider_options: mergeProviderOptions(base.provider_options, local.provider_options),
608
+ mcp: mergeMcp(base.mcp, local.mcp),
609
+ context: mergeContext(base.context, local.context),
610
+ includes: local.includes ?? base.includes,
611
+ environments: mergeOverrideMap(base.environments, local.environments),
612
+ tiers: mergeOverrideMap(base.tiers, local.tiers),
453
613
  metadata: {
454
614
  ...base.metadata ?? {},
455
615
  ...local.metadata ?? {}
@@ -461,12 +621,22 @@ function mergeDefaults(base, local) {
461
621
  };
462
622
  }
463
623
  function applyDefaults(asset, defaults) {
624
+ const compression = mergeCompression(defaults.compression, asset.compression);
464
625
  const cache = mergeCache(defaults.cache, asset.cache);
626
+ const raw = mergeRaw(defaults.raw, asset.raw);
627
+ const providerOptions = mergeProviderOptions(defaults.provider_options, asset.provider_options);
628
+ const reasoning = mergeRecordBlock(defaults.reasoning, asset.reasoning);
629
+ const sampling = mergeRecordBlock(defaults.sampling, asset.sampling);
630
+ const response = mergeRecordBlock(defaults.response, asset.response);
631
+ const mcp = mergeMcp(defaults.mcp, asset.mcp);
632
+ const context = mergeContext(defaults.context, asset.context);
633
+ const environments = mergeOverrideMap(defaults.environments, asset.environments);
634
+ const tiers = mergeOverrideMap(defaults.tiers, asset.tiers);
465
635
  const hasDefaultMetadata = defaults.metadata && Object.keys(defaults.metadata).length > 0;
466
636
  const hasDefaultSystem = !!defaults.sections?.system_instructions;
467
- const hasDefaultScalars = defaults.provider !== void 0 || defaults.model !== void 0;
468
- const hasDefaultCache = cache !== void 0;
469
- if (!hasDefaultMetadata && !hasDefaultSystem && !hasDefaultScalars && !hasDefaultCache) {
637
+ const hasDefaultScalars = defaults.provider !== void 0 || defaults.model !== void 0 || defaults.fallback_models !== void 0 || defaults.tools !== void 0 || defaults.includes !== void 0;
638
+ const hasDefaultObjects = compression !== void 0 || cache !== void 0 || raw !== void 0 || providerOptions !== void 0 || reasoning !== void 0 || sampling !== void 0 || response !== void 0 || mcp !== void 0 || context !== void 0 || environments !== void 0 || tiers !== void 0;
639
+ if (!hasDefaultMetadata && !hasDefaultSystem && !hasDefaultScalars && !hasDefaultObjects) {
470
640
  return asset;
471
641
  }
472
642
  const mergedMetadata = {
@@ -480,11 +650,53 @@ function applyDefaults(asset, defaults) {
480
650
  ...asset,
481
651
  provider: asset.provider ?? defaults.provider,
482
652
  model: asset.model ?? defaults.model,
653
+ fallback_models: asset.fallback_models ?? defaults.fallback_models,
654
+ reasoning,
655
+ sampling,
656
+ response,
657
+ compression,
483
658
  cache,
659
+ raw,
660
+ tools: asset.tools ?? defaults.tools,
661
+ provider_options: providerOptions,
662
+ mcp,
663
+ context,
664
+ includes: asset.includes ?? defaults.includes,
665
+ environments,
666
+ tiers,
484
667
  metadata,
485
668
  sections
486
669
  };
487
670
  }
671
+ function normalizeDefaultIncludes(defaults, defaultsPath, filePath) {
672
+ if (!defaults.includes) return defaults;
673
+ const promptDir = dirname2(filePath);
674
+ const defaultsDir = dirname2(defaultsPath);
675
+ const includes = defaults.includes.map((includePath) => {
676
+ const relativePath = relative(promptDir, resolve2(defaultsDir, includePath)).replace(/\\/g, "/");
677
+ return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
678
+ });
679
+ return {
680
+ ...defaults,
681
+ includes
682
+ };
683
+ }
684
+ function mergeRecordBlock(base, local) {
685
+ if (base === void 0) return local;
686
+ if (local === void 0) return base;
687
+ return { ...base, ...local };
688
+ }
689
+ function mergeMcp(base, local) {
690
+ return mergeRecordBlock(base, local);
691
+ }
692
+ function mergeContext(base, local) {
693
+ const merged = {
694
+ ...base ?? {},
695
+ ...local ?? {},
696
+ history: mergeRecordBlock(base?.history, local?.history)
697
+ };
698
+ return Object.keys(merged).length > 0 ? merged : void 0;
699
+ }
488
700
  function mergeCache(base, local) {
489
701
  const merged = {
490
702
  ...base ?? {},
@@ -512,6 +724,83 @@ function mergeCache(base, local) {
512
724
  if (merged.google && Object.keys(merged.google).length === 0) delete merged.google;
513
725
  return Object.keys(merged).length > 0 ? merged : void 0;
514
726
  }
727
+ function mergeCompression(base, local) {
728
+ const merged = {
729
+ ...base ?? {},
730
+ ...local ?? {},
731
+ thetokencompany: mergeRecordBlock(base?.thetokencompany, local?.thetokencompany)
732
+ };
733
+ removeEmptyProviderBlocks(merged);
734
+ return Object.keys(merged).length > 0 ? merged : void 0;
735
+ }
736
+ function mergeRaw(base, local) {
737
+ const merged = {
738
+ ...base ?? {},
739
+ ...local ?? {},
740
+ openai: mergeRecordBlock(base?.openai, local?.openai),
741
+ "openai-responses": mergeRecordBlock(base?.["openai-responses"], local?.["openai-responses"]),
742
+ openai_responses: mergeRecordBlock(base?.openai_responses, local?.openai_responses),
743
+ anthropic: mergeRecordBlock(base?.anthropic, local?.anthropic),
744
+ gemini: mergeRecordBlock(base?.gemini, local?.gemini),
745
+ google: mergeRecordBlock(base?.google, local?.google),
746
+ openrouter: mergeRecordBlock(base?.openrouter, local?.openrouter),
747
+ llmasaservice: mergeRecordBlock(base?.llmasaservice, local?.llmasaservice)
748
+ };
749
+ removeEmptyProviderBlocks(merged);
750
+ return Object.keys(merged).length > 0 ? merged : void 0;
751
+ }
752
+ function mergeProviderOptions(base, local) {
753
+ const merged = {
754
+ ...base ?? {},
755
+ ...local ?? {},
756
+ anthropic: mergeRecordBlock(base?.anthropic, local?.anthropic),
757
+ gemini: mergeRecordBlock(base?.gemini, local?.gemini),
758
+ openrouter: mergeRecordBlock(base?.openrouter, local?.openrouter),
759
+ llmasaservice: mergeRecordBlock(base?.llmasaservice, local?.llmasaservice)
760
+ };
761
+ removeEmptyProviderBlocks(merged);
762
+ return Object.keys(merged).length > 0 ? merged : void 0;
763
+ }
764
+ function mergeOverrideMap(base, local) {
765
+ const merged = { ...base ?? {} };
766
+ for (const [name, localOverride] of Object.entries(local ?? {})) {
767
+ merged[name] = mergeOverrideConfig(merged[name], localOverride);
768
+ }
769
+ return Object.keys(merged).length > 0 ? merged : void 0;
770
+ }
771
+ function mergeOverrideConfig(base, local) {
772
+ const merged = {
773
+ ...base ?? {},
774
+ ...local
775
+ };
776
+ const reasoning = mergeRecordBlock(base?.reasoning, local.reasoning);
777
+ const sampling = mergeRecordBlock(base?.sampling, local.sampling);
778
+ const response = mergeRecordBlock(base?.response, local.response);
779
+ const compression = mergeCompression(base?.compression, local.compression);
780
+ const cache = mergeCache(base?.cache, local.cache);
781
+ const raw = mergeRaw(base?.raw, local.raw);
782
+ const providerOptions = mergeProviderOptions(base?.provider_options, local.provider_options);
783
+ if (reasoning !== void 0) merged.reasoning = reasoning;
784
+ if (sampling !== void 0) merged.sampling = sampling;
785
+ if (response !== void 0) merged.response = response;
786
+ if (compression !== void 0) merged.compression = compression;
787
+ if (cache !== void 0) merged.cache = cache;
788
+ if (raw !== void 0) merged.raw = raw;
789
+ if (providerOptions !== void 0) merged.provider_options = providerOptions;
790
+ return merged;
791
+ }
792
+ function removeEmptyProviderBlocks(value) {
793
+ for (const key of Object.keys(value)) {
794
+ const block = value[key];
795
+ if (block === void 0) {
796
+ delete value[key];
797
+ continue;
798
+ }
799
+ if (block !== null && typeof block === "object" && !Array.isArray(block) && Object.keys(block).length === 0) {
800
+ delete value[key];
801
+ }
802
+ }
803
+ }
515
804
 
516
805
  // src/renderer/interpolate.ts
517
806
  var VARIABLE_RE = /\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}/g;
@@ -519,12 +808,13 @@ var ESCAPED_OPEN = /\\\{\\\{/g;
519
808
  var ESCAPE_PLACEHOLDER = "\0ESCAPED_OPEN\0";
520
809
  function interpolate(template, variables, options = {}) {
521
810
  const { strict = false } = options;
811
+ const optionalVariables = new Set(options.optionalVariables ?? []);
522
812
  let result = template.replace(ESCAPED_OPEN, ESCAPE_PLACEHOLDER);
523
813
  result = result.replace(VARIABLE_RE, (match, name) => {
524
814
  if (name in variables) {
525
815
  return variables[name];
526
816
  }
527
- if (strict) {
817
+ if (strict && !optionalVariables.has(name)) {
528
818
  throw new Error(`Missing required variable: "${name}"`);
529
819
  }
530
820
  return match;
@@ -542,59 +832,19 @@ function extractVariables(template) {
542
832
  return [...vars];
543
833
  }
544
834
 
545
- // src/composition/resolve-includes.ts
546
- import { readFile as readFile2 } from "fs/promises";
547
- import { resolve as resolve2, dirname as dirname2 } from "path";
548
- async function resolveIncludes(asset, basePath, visited = /* @__PURE__ */ new Set()) {
549
- if (!asset.includes || asset.includes.length === 0) {
550
- return asset;
551
- }
552
- const baseDir = dirname2(basePath);
553
- const resolvedPath = resolve2(basePath);
554
- if (visited.has(resolvedPath)) {
555
- throw new Error(`Circular include detected: ${resolvedPath}`);
556
- }
557
- visited.add(resolvedPath);
558
- let mergedSystemInstructions = "";
559
- for (const includePath of asset.includes) {
560
- const fullPath = resolve2(baseDir, includePath);
561
- if (visited.has(fullPath)) {
562
- throw new Error(`Circular include detected: ${fullPath} (included from ${basePath})`);
563
- }
564
- const content = await readFile2(fullPath, "utf-8");
565
- const { asset: includedAsset } = parsePrompt(content, fullPath);
566
- const resolved = await resolveIncludes(includedAsset, fullPath, new Set(visited));
567
- if (resolved.sections?.system_instructions) {
568
- mergedSystemInstructions += resolved.sections.system_instructions + "\n\n";
569
- }
570
- }
571
- const localSystem = asset.sections?.system_instructions ?? "";
572
- const combinedSystem = (mergedSystemInstructions + localSystem).trim() || void 0;
573
- return {
574
- ...asset,
575
- sections: {
576
- ...asset.sections,
577
- system_instructions: combinedSystem
578
- },
579
- // Drop includes from the resolved asset — they've been inlined
580
- includes: void 0
581
- };
582
- }
583
-
584
835
  // src/context.ts
585
836
  var textEncoder = new TextEncoder();
586
837
  function getContextInputs(asset) {
587
838
  return (asset.context?.inputs ?? []).map(normalizeContextInput);
588
839
  }
589
- function getContextInputNames(asset) {
590
- return getContextInputs(asset).map((input) => input.name);
591
- }
592
840
  function normalizeContextInput(input) {
593
841
  if (typeof input === "string") {
594
842
  return { name: input };
595
843
  }
596
844
  return {
597
845
  name: input.name,
846
+ optional: input.optional,
847
+ warnings: input.warnings,
598
848
  max_size: input.max_size,
599
849
  trim: input.trim,
600
850
  allow_regex: normalizeContextRegex(input.allow_regex),
@@ -603,6 +853,9 @@ function normalizeContextInput(input) {
603
853
  reject_secrets: normalizeBuiltInValidator(input.reject_secrets)
604
854
  };
605
855
  }
856
+ function areContextInputWarningsEnabled(input) {
857
+ return input.warnings !== false;
858
+ }
606
859
  function normalizeContextRegex(value) {
607
860
  if (value === void 0) {
608
861
  return void 0;
@@ -689,6 +942,45 @@ function compileContextRegex(regex, details) {
689
942
  }
690
943
  }
691
944
 
945
+ // src/composition/resolve-includes.ts
946
+ import { readFile as readFile3 } from "fs/promises";
947
+ import { resolve as resolve3, dirname as dirname3 } from "path";
948
+ async function resolveIncludes(asset, basePath, visited = /* @__PURE__ */ new Set()) {
949
+ if (!asset.includes || asset.includes.length === 0) {
950
+ return asset;
951
+ }
952
+ const baseDir = dirname3(basePath);
953
+ const resolvedPath = resolve3(basePath);
954
+ if (visited.has(resolvedPath)) {
955
+ throw new Error(`Circular include detected: ${resolvedPath}`);
956
+ }
957
+ visited.add(resolvedPath);
958
+ let mergedSystemInstructions = "";
959
+ for (const includePath of asset.includes) {
960
+ const fullPath = resolve3(baseDir, includePath);
961
+ if (visited.has(fullPath)) {
962
+ throw new Error(`Circular include detected: ${fullPath} (included from ${basePath})`);
963
+ }
964
+ const content = await readFile3(fullPath, "utf-8");
965
+ const { asset: includedAsset } = parsePrompt(content, fullPath);
966
+ const resolved = await resolveIncludes(includedAsset, fullPath, new Set(visited));
967
+ if (resolved.sections?.system_instructions) {
968
+ mergedSystemInstructions += resolved.sections.system_instructions + "\n\n";
969
+ }
970
+ }
971
+ const localSystem = asset.sections?.system_instructions ?? "";
972
+ const combinedSystem = (mergedSystemInstructions + localSystem).trim() || void 0;
973
+ return {
974
+ ...asset,
975
+ sections: {
976
+ ...asset.sections,
977
+ system_instructions: combinedSystem
978
+ },
979
+ // Drop includes from the resolved asset — they've been inlined
980
+ includes: void 0
981
+ };
982
+ }
983
+
692
984
  // src/validation/levenshtein.ts
693
985
  function levenshtein(a, b) {
694
986
  const m = a.length;
@@ -730,9 +1022,22 @@ var KNOWN_FRONT_MATTER_KEYS = /* @__PURE__ */ new Set([
730
1022
  "tiers",
731
1023
  "metadata",
732
1024
  "cache",
1025
+ "compression",
733
1026
  "provider_options",
734
1027
  "raw"
735
1028
  ]);
1029
+ function collectSchemaKeywords(value, acc) {
1030
+ if (!value || typeof value !== "object") return;
1031
+ if (Array.isArray(value)) {
1032
+ for (const item of value) collectSchemaKeywords(item, acc);
1033
+ return;
1034
+ }
1035
+ const record = value;
1036
+ for (const [key, child] of Object.entries(record)) {
1037
+ acc.add(key);
1038
+ collectSchemaKeywords(child, acc);
1039
+ }
1040
+ }
736
1041
  var RISKY_UNBOUNDED_INPUT_NAMES = [
737
1042
  "message",
738
1043
  "prompt",
@@ -784,7 +1089,9 @@ function validateAsset(asset, frontMatterKeys, filePath) {
784
1089
  }
785
1090
  }
786
1091
  }
787
- const declaredInputs = new Set(getContextInputNames(asset));
1092
+ const contextInputs = getContextInputs(asset);
1093
+ const declaredInputs = new Set(contextInputs.map((input) => input.name));
1094
+ const declaredInputsByName = new Map(contextInputs.map((input) => [input.name, input]));
788
1095
  const usedVars = /* @__PURE__ */ new Set();
789
1096
  if (asset.sections?.system_instructions) {
790
1097
  for (const v of extractVariables(asset.sections.system_instructions)) {
@@ -806,7 +1113,8 @@ function validateAsset(asset, frontMatterKeys, filePath) {
806
1113
  }
807
1114
  }
808
1115
  for (const v of declaredInputs) {
809
- if (!usedVars.has(v)) {
1116
+ const input = declaredInputsByName.get(v);
1117
+ if (!usedVars.has(v) && input?.optional !== true && areContextInputWarningsEnabled(input ?? {})) {
810
1118
  warnings.push({
811
1119
  code: "POK012",
812
1120
  message: `Variable "${v}" is declared in context.inputs but never used`,
@@ -822,9 +1130,10 @@ function validateAsset(asset, frontMatterKeys, filePath) {
822
1130
  suggestion: "Declare context.inputs to enable input policy validation."
823
1131
  });
824
1132
  }
825
- for (const input of getContextInputs(asset)) {
1133
+ for (const input of contextInputs) {
826
1134
  const lowerName = input.name.toLowerCase();
827
- if (input.max_size === void 0 && RISKY_UNBOUNDED_INPUT_NAMES.some((needle) => lowerName.includes(needle))) {
1135
+ const inputWarningsEnabled = areContextInputWarningsEnabled(input);
1136
+ if (inputWarningsEnabled && input.max_size === void 0 && RISKY_UNBOUNDED_INPUT_NAMES.some((needle) => lowerName.includes(needle))) {
828
1137
  warnings.push({
829
1138
  code: "POK040",
830
1139
  message: `Context input "${input.name}" has no max_size and appears unbounded.`,
@@ -832,7 +1141,7 @@ function validateAsset(asset, frontMatterKeys, filePath) {
832
1141
  suggestion: "Add max_size to constrain prompt payload growth."
833
1142
  });
834
1143
  }
835
- if (input.allow_regex === void 0 && input.deny_regex === void 0 && input.non_empty === void 0 && input.reject_secrets === void 0) {
1144
+ if (inputWarningsEnabled && input.optional !== true && input.allow_regex === void 0 && input.deny_regex === void 0 && input.non_empty === void 0 && input.reject_secrets === void 0) {
836
1145
  warnings.push({
837
1146
  code: "POK041",
838
1147
  message: `Context input "${input.name}" has no input hardening validators.`,
@@ -840,7 +1149,7 @@ function validateAsset(asset, frontMatterKeys, filePath) {
840
1149
  suggestion: "Consider non_empty/reject_secrets and allow/deny regex validators."
841
1150
  });
842
1151
  }
843
- if (input.trim !== void 0 && input.trim !== false && input.max_size === void 0) {
1152
+ if (inputWarningsEnabled && input.trim !== void 0 && input.trim !== false && input.max_size === void 0) {
844
1153
  warnings.push({
845
1154
  code: "POK014",
846
1155
  message: `Context input "${input.name}" sets trim but has no max_size; trim-to-budget will be skipped.`,
@@ -867,6 +1176,30 @@ function validateAsset(asset, frontMatterKeys, filePath) {
867
1176
  }
868
1177
  }
869
1178
  }
1179
+ if (asset.response?.schema && asset.provider) {
1180
+ const keywords = /* @__PURE__ */ new Set();
1181
+ collectSchemaKeywords(asset.response.schema, keywords);
1182
+ const openAIUnsupported = ["patternProperties", "unevaluatedProperties", "$dynamicRef"];
1183
+ const geminiUnsupported = ["patternProperties", "$dynamicRef", "dependentSchemas"];
1184
+ const anthropicCaution = ["if", "then", "else"];
1185
+ const checks = [
1186
+ { provider: ["openai", "openai-responses", "openrouter", "llmasaservice"], unsupported: openAIUnsupported, code: "POK052" },
1187
+ { provider: ["gemini", "google"], unsupported: geminiUnsupported, code: "POK053" },
1188
+ { provider: ["anthropic"], unsupported: anthropicCaution, code: "POK054" }
1189
+ ];
1190
+ for (const check of checks) {
1191
+ if (!check.provider.includes(asset.provider)) continue;
1192
+ const hits = check.unsupported.filter((k) => keywords.has(k));
1193
+ if (hits.length > 0) {
1194
+ warnings.push({
1195
+ code: check.code,
1196
+ message: `response.schema includes provider-sensitive JSON Schema keywords for "${asset.provider}": ${hits.join(", ")}`,
1197
+ filePath,
1198
+ suggestion: "Validate schema compatibility with your provider docs or simplify the schema dialect."
1199
+ });
1200
+ }
1201
+ }
1202
+ }
870
1203
  if (asset.provider) {
871
1204
  let providerCache;
872
1205
  let cacheSuggestionField;
@@ -993,9 +1326,9 @@ function isRecord(value) {
993
1326
  }
994
1327
 
995
1328
  // src/prompt-resolution.ts
996
- import { readFile as readFile3 } from "fs/promises";
1329
+ import { readFile as readFile4 } from "fs/promises";
997
1330
  import { existsSync, statSync as statSync2 } from "fs";
998
- import { resolve as resolve3 } from "path";
1331
+ import { resolve as resolve4 } from "path";
999
1332
 
1000
1333
  // src/cache.ts
1001
1334
  import { statSync } from "fs";
@@ -1071,28 +1404,38 @@ function mergeOverride(base, override) {
1071
1404
  if (override.response !== void 0) {
1072
1405
  result.response = { ...result.response, ...override.response };
1073
1406
  }
1407
+ if (override.compression !== void 0) {
1408
+ result.compression = {
1409
+ ...result.compression,
1410
+ ...override.compression,
1411
+ thetokencompany: mergeRecordBlock2(
1412
+ result.compression?.thetokencompany,
1413
+ override.compression.thetokencompany
1414
+ )
1415
+ };
1416
+ }
1074
1417
  if (override.cache !== void 0) {
1075
1418
  result.cache = {
1076
1419
  ...result.cache,
1077
1420
  ...override.cache,
1078
- openai: mergeRecordBlock(result.cache?.openai, override.cache.openai),
1079
- anthropic: mergeRecordBlock(result.cache?.anthropic, override.cache.anthropic),
1080
- gemini: mergeRecordBlock(result.cache?.gemini, override.cache.gemini),
1081
- google: mergeRecordBlock(result.cache?.google, override.cache.google)
1421
+ openai: mergeRecordBlock2(result.cache?.openai, override.cache.openai),
1422
+ anthropic: mergeRecordBlock2(result.cache?.anthropic, override.cache.anthropic),
1423
+ gemini: mergeRecordBlock2(result.cache?.gemini, override.cache.gemini),
1424
+ google: mergeRecordBlock2(result.cache?.google, override.cache.google)
1082
1425
  };
1083
1426
  }
1084
1427
  if (override.raw !== void 0) {
1085
1428
  result.raw = {
1086
1429
  ...result.raw,
1087
1430
  ...override.raw,
1088
- openai: mergeRecordBlock(result.raw?.openai, override.raw.openai),
1089
- "openai-responses": mergeRecordBlock(result.raw?.["openai-responses"], override.raw["openai-responses"]),
1090
- openai_responses: mergeRecordBlock(result.raw?.openai_responses, override.raw.openai_responses),
1091
- anthropic: mergeRecordBlock(result.raw?.anthropic, override.raw.anthropic),
1092
- gemini: mergeRecordBlock(result.raw?.gemini, override.raw.gemini),
1093
- google: mergeRecordBlock(result.raw?.google, override.raw.google),
1094
- openrouter: mergeRecordBlock(result.raw?.openrouter, override.raw.openrouter),
1095
- llmasaservice: mergeRecordBlock(result.raw?.llmasaservice, override.raw.llmasaservice)
1431
+ openai: mergeRecordBlock2(result.raw?.openai, override.raw.openai),
1432
+ "openai-responses": mergeRecordBlock2(result.raw?.["openai-responses"], override.raw["openai-responses"]),
1433
+ openai_responses: mergeRecordBlock2(result.raw?.openai_responses, override.raw.openai_responses),
1434
+ anthropic: mergeRecordBlock2(result.raw?.anthropic, override.raw.anthropic),
1435
+ gemini: mergeRecordBlock2(result.raw?.gemini, override.raw.gemini),
1436
+ google: mergeRecordBlock2(result.raw?.google, override.raw.google),
1437
+ openrouter: mergeRecordBlock2(result.raw?.openrouter, override.raw.openrouter),
1438
+ llmasaservice: mergeRecordBlock2(result.raw?.llmasaservice, override.raw.llmasaservice)
1096
1439
  };
1097
1440
  }
1098
1441
  if (override.provider_options !== void 0) {
@@ -1119,7 +1462,7 @@ function mergeOverride(base, override) {
1119
1462
  }
1120
1463
  return result;
1121
1464
  }
1122
- function mergeRecordBlock(base, override) {
1465
+ function mergeRecordBlock2(base, override) {
1123
1466
  if (override === void 0) return base;
1124
1467
  return { ...base, ...override };
1125
1468
  }
@@ -1135,9 +1478,9 @@ var sharedPromptCache = new PromptCache();
1135
1478
 
1136
1479
  // src/cli/commands/defaults-root.ts
1137
1480
  import { existsSync as existsSync2 } from "fs";
1138
- import { dirname as dirname3, join as join2, resolve as resolve4 } from "path";
1481
+ import { dirname as dirname4, join as join2, resolve as resolve5 } from "path";
1139
1482
  function findDefaultsRoot(filePath) {
1140
- let current = resolve4(dirname3(filePath));
1483
+ let current = resolve5(dirname4(filePath));
1141
1484
  let root = current;
1142
1485
  let foundDefaults = false;
1143
1486
  while (true) {
@@ -1147,7 +1490,7 @@ function findDefaultsRoot(filePath) {
1147
1490
  } else if (foundDefaults) {
1148
1491
  return root;
1149
1492
  }
1150
- const parent = dirname3(current);
1493
+ const parent = dirname4(current);
1151
1494
  if (parent === current) {
1152
1495
  return root;
1153
1496
  }
@@ -1242,7 +1585,7 @@ function getFlag(args, ...flags) {
1242
1585
  async function collectPromptFiles(target) {
1243
1586
  const targetStats = await stat(target);
1244
1587
  if (targetStats.isFile()) {
1245
- if (extname(target) !== ".md" || target.endsWith("defaults.md") || target.endsWith(".test.md")) {
1588
+ if (extname2(target) !== ".md" || target.endsWith("defaults.md") || target.endsWith(".test.md")) {
1246
1589
  return [];
1247
1590
  }
1248
1591
  return [target];
@@ -1250,7 +1593,7 @@ async function collectPromptFiles(target) {
1250
1593
  const results = [];
1251
1594
  const entries = await readdir(target, { withFileTypes: true, recursive: true });
1252
1595
  for (const entry of entries) {
1253
- if (entry.isFile() && extname(entry.name) === ".md" && !entry.name.endsWith(".test.md") && entry.name !== "defaults.md") {
1596
+ if (entry.isFile() && extname2(entry.name) === ".md" && !entry.name.endsWith(".test.md") && entry.name !== "defaults.md") {
1254
1597
  results.push(join3(entry.parentPath ?? target, entry.name));
1255
1598
  }
1256
1599
  }
@@ -1259,7 +1602,7 @@ async function collectPromptFiles(target) {
1259
1602
 
1260
1603
  // src/cli/commands/compile.ts
1261
1604
  import { readdir as readdir2, writeFile, mkdir, rm } from "fs/promises";
1262
- import { join as join4, extname as extname2, relative, dirname as dirname5 } from "path";
1605
+ import { join as join4, extname as extname3, relative as relative2, dirname as dirname6 } from "path";
1263
1606
  var HELP2 = `
1264
1607
  promptopskit compile [sourceDir] [outputDir] [options]
1265
1608
 
@@ -1299,7 +1642,7 @@ async function compile(args) {
1299
1642
  let compiled = 0;
1300
1643
  let errors = 0;
1301
1644
  for (const file of files) {
1302
- const rel = relative(sourceDir, file).replace(/\.md$/, "");
1645
+ const rel = relative2(sourceDir, file).replace(/\.md$/, "");
1303
1646
  const outExt = format === "esm" ? ".mjs" : ".json";
1304
1647
  const outPath = join4(outputDir, rel + outExt);
1305
1648
  try {
@@ -1312,7 +1655,7 @@ async function compile(args) {
1312
1655
  if (dryRun) {
1313
1656
  console.log(` Would create: ${outPath}`);
1314
1657
  } else {
1315
- await mkdir(dirname5(outPath), { recursive: true });
1658
+ await mkdir(dirname6(outPath), { recursive: true });
1316
1659
  if (format === "esm") {
1317
1660
  const esmContent = `export default ${JSON.stringify(asset, null, 2)};
1318
1661
  `;
@@ -1368,7 +1711,7 @@ async function collectPromptFiles2(dir) {
1368
1711
  const results = [];
1369
1712
  const entries = await readdir2(dir, { withFileTypes: true, recursive: true });
1370
1713
  for (const entry of entries) {
1371
- if (entry.isFile() && extname2(entry.name) === ".md" && !entry.name.endsWith(".test.md") && entry.name !== "defaults.md") {
1714
+ if (entry.isFile() && extname3(entry.name) === ".md" && !entry.name.endsWith(".test.md") && entry.name !== "defaults.md") {
1372
1715
  results.push(join4(entry.parentPath ?? dir, entry.name));
1373
1716
  }
1374
1717
  }
@@ -1376,7 +1719,7 @@ async function collectPromptFiles2(dir) {
1376
1719
  }
1377
1720
 
1378
1721
  // src/cli/commands/render.ts
1379
- import { readFile as readFile4 } from "fs/promises";
1722
+ import { readFile as readFile5 } from "fs/promises";
1380
1723
  import { existsSync as existsSync3 } from "fs";
1381
1724
  var HELP3 = `
1382
1725
  promptopskit render <file> [options]
@@ -1407,13 +1750,13 @@ async function render(args) {
1407
1750
  const jsonOutput = args.includes("--json");
1408
1751
  let variables = {};
1409
1752
  if (varsFile) {
1410
- const varsContent = await readFile4(varsFile, "utf-8");
1753
+ const varsContent = await readFile5(varsFile, "utf-8");
1411
1754
  variables = JSON.parse(varsContent);
1412
1755
  } else {
1413
1756
  const sidecarPath = file.replace(/\.md$/, ".test.yaml");
1414
1757
  if (existsSync3(sidecarPath)) {
1415
1758
  const { default: yaml } = await import("gray-matter");
1416
- const sidecarContent = await readFile4(sidecarPath, "utf-8");
1759
+ const sidecarContent = await readFile5(sidecarPath, "utf-8");
1417
1760
  const parsed2 = yaml(`---
1418
1761
  ${sidecarContent}---
1419
1762
  `);
@@ -1512,7 +1855,7 @@ async function inspect(args) {
1512
1855
 
1513
1856
  // src/cli/commands/init.ts
1514
1857
  import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
1515
- import { join as join5, dirname as dirname6, relative as relative2 } from "path";
1858
+ import { join as join5, dirname as dirname7, relative as relative3 } from "path";
1516
1859
  import { existsSync as existsSync4, readFileSync } from "fs";
1517
1860
  var HELP5 = `
1518
1861
  promptopskit init [dir]
@@ -1663,8 +2006,8 @@ async function init(args) {
1663
2006
  return;
1664
2007
  }
1665
2008
  const dir = args.find((a) => !a.startsWith("--")) ?? "./prompts";
1666
- const testFilePath = join5(dirname6(dir), "tests", "hello.prompt.test.mjs");
1667
- const promptsDirFromTest = relative2(dirname6(testFilePath), dir) || ".";
2009
+ const testFilePath = join5(dirname7(dir), "tests", "hello.prompt.test.mjs");
2010
+ const promptsDirFromTest = relative3(dirname7(testFilePath), dir) || ".";
1668
2011
  const helloPromptTest = createHelloPromptTest(promptsDirFromTest);
1669
2012
  const files = [
1670
2013
  { path: join5(dir, "defaults.md"), content: DEFAULTS },
@@ -1682,7 +2025,7 @@ async function init(args) {
1682
2025
  skipped++;
1683
2026
  continue;
1684
2027
  }
1685
- await mkdir2(dirname6(file.path), { recursive: true });
2028
+ await mkdir2(dirname7(file.path), { recursive: true });
1686
2029
  await writeFile2(file.path, file.content, "utf-8");
1687
2030
  console.log(` \u2713 ${file.path}`);
1688
2031
  created++;
@@ -1750,8 +2093,8 @@ test('hello prompt can return a deterministic response without calling a model',
1750
2093
  }
1751
2094
 
1752
2095
  // src/cli/commands/skill.ts
1753
- import { writeFile as writeFile3, readFile as readFile5, mkdir as mkdir3 } from "fs/promises";
1754
- import { dirname as dirname7 } from "path";
2096
+ import { writeFile as writeFile3, readFile as readFile6, mkdir as mkdir3 } from "fs/promises";
2097
+ import { dirname as dirname8 } from "path";
1755
2098
  import { existsSync as existsSync5 } from "fs";
1756
2099
  var MARKER_START = "<!-- promptopskit:start -->";
1757
2100
  var MARKER_END = "<!-- promptopskit:end -->";
@@ -1839,7 +2182,7 @@ async function skill(args) {
1839
2182
  }
1840
2183
  const markedContent = config.wrap(wrapMarkers(STUB_CONTENT));
1841
2184
  if (existsSync5(filePath) && !force) {
1842
- const existing = await readFile5(filePath, "utf-8");
2185
+ const existing = await readFile6(filePath, "utf-8");
1843
2186
  const merged = mergeContent(existing, markedContent);
1844
2187
  if (merged === existing) {
1845
2188
  console.log(` skip ${filePath} (already up to date)`);
@@ -1850,7 +2193,7 @@ async function skill(args) {
1850
2193
  written++;
1851
2194
  continue;
1852
2195
  }
1853
- await mkdir3(dirname7(filePath), { recursive: true });
2196
+ await mkdir3(dirname8(filePath), { recursive: true });
1854
2197
  await writeFile3(filePath, markedContent, "utf-8");
1855
2198
  console.log(` \u2713 ${filePath}`);
1856
2199
  written++;
@@ -1863,12 +2206,12 @@ async function skill(args) {
1863
2206
  async function deployClaude(filePath, force) {
1864
2207
  const content = CLAUDE_LINE + "\n";
1865
2208
  if (!existsSync5(filePath) || force) {
1866
- await mkdir3(dirname7(filePath), { recursive: true });
2209
+ await mkdir3(dirname8(filePath), { recursive: true });
1867
2210
  await writeFile3(filePath, content, "utf-8");
1868
2211
  console.log(` \u2713 ${filePath}`);
1869
2212
  return 1;
1870
2213
  }
1871
- const existing = await readFile5(filePath, "utf-8");
2214
+ const existing = await readFile6(filePath, "utf-8");
1872
2215
  if (existing.split("\n").some((line) => line.trim() === CLAUDE_LINE)) {
1873
2216
  console.log(` skip ${filePath} (already up to date)`);
1874
2217
  return 0;