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
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(),
@@ -136,6 +152,8 @@ var ContextBuiltInValidatorSchema = z.union([
136
152
  ]);
137
153
  var ContextInputDefinitionObjectSchema = z.object({
138
154
  name: z.string(),
155
+ optional: z.boolean().optional(),
156
+ warnings: z.boolean().optional(),
139
157
  max_size: z.number().int().positive().optional(),
140
158
  trim: z.union([z.boolean(), z.enum(["start", "end", "both"])]).optional(),
141
159
  allow_regex: ContextRegexSchema.optional(),
@@ -165,7 +183,7 @@ var PromptAssetOverridesSchema = z.object({
165
183
  fallback_models: z.array(z.string()).optional(),
166
184
  reasoning: ReasoningSchema.optional(),
167
185
  sampling: SamplingSchema.optional(),
168
- response: ResponseSchema.optional(),
186
+ response: ResponseSchemaWithValidation.optional(),
169
187
  cache: CacheSchema.optional(),
170
188
  raw: RawProviderBodySchema.optional(),
171
189
  tools: z.array(ToolRefSchema).optional(),
@@ -183,7 +201,19 @@ var SectionsSchema = z.object({
183
201
  var PromptDefaultsSchema = z.object({
184
202
  provider: z.enum(["openai", "openai-responses", "anthropic", "google", "gemini", "openrouter", "llmasaservice", "any"]).optional(),
185
203
  model: z.string().optional(),
204
+ fallback_models: z.array(z.string()).optional(),
205
+ reasoning: ReasoningSchema.optional(),
206
+ sampling: SamplingSchema.optional(),
207
+ response: ResponseSchema.optional(),
186
208
  cache: CacheSchema.optional(),
209
+ raw: RawProviderBodySchema.optional(),
210
+ tools: z.array(ToolRefSchema).optional(),
211
+ provider_options: ProviderOptionsSchema.optional(),
212
+ mcp: MCPSchema.optional(),
213
+ context: ContextSchema.optional(),
214
+ includes: z.array(z.string()).optional(),
215
+ environments: z.record(PromptAssetOverridesSchema).optional(),
216
+ tiers: z.record(PromptAssetOverridesSchema).optional(),
187
217
  metadata: MetadataSchema.optional(),
188
218
  sections: z.object({
189
219
  system_instructions: z.string().optional()
@@ -198,7 +228,7 @@ var PromptAssetSchema = z.object({
198
228
  fallback_models: z.array(z.string()).optional(),
199
229
  reasoning: ReasoningSchema.optional(),
200
230
  sampling: SamplingSchema.optional(),
201
- response: ResponseSchema.optional(),
231
+ response: ResponseSchemaWithValidation.optional(),
202
232
  cache: CacheSchema.optional(),
203
233
  raw: RawProviderBodySchema.optional(),
204
234
  tools: z.array(ToolRefSchema).optional(),
@@ -390,16 +420,122 @@ function parsePrompt(content, filePath) {
390
420
  }
391
421
 
392
422
  // src/parser/loader.ts
393
- import { readFile } from "fs/promises";
394
- import { dirname, join, resolve } from "path";
423
+ import { readFile as readFile2 } from "fs/promises";
424
+ import { dirname as dirname2, join, relative, resolve as resolve2 } from "path";
395
425
  import matter2 from "gray-matter";
426
+
427
+ // src/parser/response-schema-ref.ts
428
+ import { readFile } from "fs/promises";
429
+ import { createHash } from "crypto";
430
+ import { dirname, extname, resolve } from "path";
431
+ import { pathToFileURL } from "url";
432
+ import { z as z2 } from "zod";
433
+ import { zodToJsonSchema } from "zod-to-json-schema";
434
+ var SUPPORTED_SCHEMA_REF_EXTENSIONS = /* @__PURE__ */ new Set([".json", ".js", ".mjs", ".cjs"]);
435
+ async function resolveJsonSchemaRef(schemaRef, promptFilePath) {
436
+ const resolvedPath = resolve(dirname(promptFilePath), schemaRef);
437
+ let raw;
438
+ try {
439
+ raw = await readFile(resolvedPath, "utf-8");
440
+ } catch (error) {
441
+ const code = error.code;
442
+ if (code === "ENOENT") {
443
+ throw new Error(`POK050: response.schema_ref "${schemaRef}" not found (resolved from ${promptFilePath})`);
444
+ }
445
+ throw error;
446
+ }
447
+ let parsed;
448
+ try {
449
+ parsed = JSON.parse(raw);
450
+ } catch {
451
+ throw new Error(`POK050: response.schema_ref "${schemaRef}" is not valid JSON`);
452
+ }
453
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
454
+ throw new Error(`POK050: response.schema_ref "${schemaRef}" must resolve to a JSON object schema`);
455
+ }
456
+ return {
457
+ schema: parsed,
458
+ resolvedPath,
459
+ hash: createHash("sha256").update(raw).digest("hex")
460
+ };
461
+ }
462
+ async function resolveZodSchemaRef(schemaRef, promptFilePath) {
463
+ const resolvedPath = resolve(dirname(promptFilePath), schemaRef);
464
+ let moduleSource;
465
+ try {
466
+ moduleSource = await readFile(resolvedPath, "utf-8");
467
+ } catch (error) {
468
+ const code = error.code;
469
+ if (code === "ENOENT") {
470
+ throw new Error(`POK051: response.schema_ref "${schemaRef}" not found (resolved from ${promptFilePath})`);
471
+ }
472
+ throw error;
473
+ }
474
+ let imported;
475
+ try {
476
+ imported = await import(pathToFileURL(resolvedPath).href);
477
+ } catch (error) {
478
+ const message = error instanceof Error ? error.message : String(error);
479
+ throw new Error(`POK051: response.schema_ref "${schemaRef}" could not be imported as a module (${message})`);
480
+ }
481
+ const mod = imported;
482
+ const candidate = mod.default ?? mod.schema;
483
+ if (!(candidate instanceof z2.ZodType)) {
484
+ throw new Error('POK051: zod schema modules must export a Zod schema as default export or named export "schema"');
485
+ }
486
+ const jsonSchema = zodToJsonSchema(candidate, {
487
+ target: "jsonSchema7",
488
+ $refStrategy: "none"
489
+ });
490
+ if (!jsonSchema || typeof jsonSchema !== "object" || Array.isArray(jsonSchema)) {
491
+ throw new Error(`POK051: response.schema_ref "${schemaRef}" did not produce a valid JSON schema object`);
492
+ }
493
+ return {
494
+ schema: jsonSchema,
495
+ resolvedPath,
496
+ hash: createHash("sha256").update(moduleSource).digest("hex")
497
+ };
498
+ }
499
+ async function resolveResponseSchemaRef(asset, promptFilePath) {
500
+ const schemaRef = asset.response?.schema_ref;
501
+ if (!schemaRef) return asset;
502
+ const ext = extname(schemaRef).toLowerCase();
503
+ if (!SUPPORTED_SCHEMA_REF_EXTENSIONS.has(ext)) {
504
+ throw new Error(`POK051: response.schema_ref "${schemaRef}" has unsupported extension "${ext}". Use .json, .js, .mjs, or .cjs`);
505
+ }
506
+ const resolved = ext === ".json" ? await resolveJsonSchemaRef(schemaRef, promptFilePath) : await resolveZodSchemaRef(schemaRef, promptFilePath);
507
+ return {
508
+ ...asset,
509
+ response: {
510
+ ...asset.response,
511
+ schema: resolved.schema,
512
+ schema_ref: void 0,
513
+ schema_source: {
514
+ mode: ext === ".json" ? "schema_ref_json" : "schema_ref_zod_module",
515
+ ref: schemaRef,
516
+ resolved_path: resolved.resolvedPath,
517
+ hash: resolved.hash
518
+ }
519
+ }
520
+ };
521
+ }
522
+
523
+ // src/parser/loader.ts
396
524
  var DEFAULTS_FILE_NAME = "defaults.md";
397
525
  async function loadPromptFile(filePath, options = {}) {
398
- const content = await readFile(filePath, "utf-8");
526
+ const content = await readFile2(filePath, "utf-8");
399
527
  const parsed = parsePrompt(content, filePath);
400
- const root = options.defaultsRoot ?? dirname(filePath);
528
+ const root = options.defaultsRoot ?? dirname2(filePath);
401
529
  const defaults = await loadDefaultsForPath(filePath, root);
402
- const asset = applyDefaults(parsed.asset, defaults);
530
+ const withDefaults = applyDefaults(parsed.asset, defaults);
531
+ const resolved = await resolveResponseSchemaRef(withDefaults, filePath);
532
+ const asset = resolved.response?.schema !== void 0 && !resolved.response?.schema_source ? {
533
+ ...resolved,
534
+ response: {
535
+ ...resolved.response,
536
+ schema_source: { mode: "inline" }
537
+ }
538
+ } : resolved;
403
539
  return {
404
540
  ...parsed,
405
541
  asset
@@ -411,8 +547,8 @@ async function loadDefaultsForPath(filePath, defaultsRoot) {
411
547
  for (const dir of directories) {
412
548
  const defaultsPath = join(dir, DEFAULTS_FILE_NAME);
413
549
  try {
414
- const defaultsContent = await readFile(defaultsPath, "utf-8");
415
- const defaults = parseDefaults(defaultsContent);
550
+ const defaultsContent = await readFile2(defaultsPath, "utf-8");
551
+ const defaults = normalizeDefaultIncludes(parseDefaults(defaultsContent), defaultsPath, filePath);
416
552
  merged = mergeDefaults(merged, defaults);
417
553
  } catch (error) {
418
554
  if (error.code !== "ENOENT") {
@@ -424,14 +560,14 @@ async function loadDefaultsForPath(filePath, defaultsRoot) {
424
560
  }
425
561
  function getDirectoriesToCheck(filePath, defaultsRoot) {
426
562
  const dirs = [];
427
- let current = resolve(dirname(filePath));
428
- const boundary = defaultsRoot ? resolve(defaultsRoot) : void 0;
563
+ let current = resolve2(dirname2(filePath));
564
+ const boundary = defaultsRoot ? resolve2(defaultsRoot) : void 0;
429
565
  while (true) {
430
566
  dirs.unshift(current);
431
- if (boundary && current === boundary || current === dirname(current)) {
567
+ if (boundary && current === boundary || current === dirname2(current)) {
432
568
  break;
433
569
  }
434
- current = dirname(current);
570
+ current = dirname2(current);
435
571
  }
436
572
  return dirs;
437
573
  }
@@ -449,7 +585,19 @@ function mergeDefaults(base, local) {
449
585
  return {
450
586
  provider: local.provider ?? base.provider,
451
587
  model: local.model ?? base.model,
588
+ fallback_models: local.fallback_models ?? base.fallback_models,
589
+ reasoning: mergeRecordBlock(base.reasoning, local.reasoning),
590
+ sampling: mergeRecordBlock(base.sampling, local.sampling),
591
+ response: mergeRecordBlock(base.response, local.response),
452
592
  cache: mergeCache(base.cache, local.cache),
593
+ raw: mergeRaw(base.raw, local.raw),
594
+ tools: local.tools ?? base.tools,
595
+ provider_options: mergeProviderOptions(base.provider_options, local.provider_options),
596
+ mcp: mergeMcp(base.mcp, local.mcp),
597
+ context: mergeContext(base.context, local.context),
598
+ includes: local.includes ?? base.includes,
599
+ environments: mergeOverrideMap(base.environments, local.environments),
600
+ tiers: mergeOverrideMap(base.tiers, local.tiers),
453
601
  metadata: {
454
602
  ...base.metadata ?? {},
455
603
  ...local.metadata ?? {}
@@ -462,11 +610,20 @@ function mergeDefaults(base, local) {
462
610
  }
463
611
  function applyDefaults(asset, defaults) {
464
612
  const cache = mergeCache(defaults.cache, asset.cache);
613
+ const raw = mergeRaw(defaults.raw, asset.raw);
614
+ const providerOptions = mergeProviderOptions(defaults.provider_options, asset.provider_options);
615
+ const reasoning = mergeRecordBlock(defaults.reasoning, asset.reasoning);
616
+ const sampling = mergeRecordBlock(defaults.sampling, asset.sampling);
617
+ const response = mergeRecordBlock(defaults.response, asset.response);
618
+ const mcp = mergeMcp(defaults.mcp, asset.mcp);
619
+ const context = mergeContext(defaults.context, asset.context);
620
+ const environments = mergeOverrideMap(defaults.environments, asset.environments);
621
+ const tiers = mergeOverrideMap(defaults.tiers, asset.tiers);
465
622
  const hasDefaultMetadata = defaults.metadata && Object.keys(defaults.metadata).length > 0;
466
623
  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) {
624
+ const hasDefaultScalars = defaults.provider !== void 0 || defaults.model !== void 0 || defaults.fallback_models !== void 0 || defaults.tools !== void 0 || defaults.includes !== void 0;
625
+ const hasDefaultObjects = 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;
626
+ if (!hasDefaultMetadata && !hasDefaultSystem && !hasDefaultScalars && !hasDefaultObjects) {
470
627
  return asset;
471
628
  }
472
629
  const mergedMetadata = {
@@ -480,11 +637,52 @@ function applyDefaults(asset, defaults) {
480
637
  ...asset,
481
638
  provider: asset.provider ?? defaults.provider,
482
639
  model: asset.model ?? defaults.model,
640
+ fallback_models: asset.fallback_models ?? defaults.fallback_models,
641
+ reasoning,
642
+ sampling,
643
+ response,
483
644
  cache,
645
+ raw,
646
+ tools: asset.tools ?? defaults.tools,
647
+ provider_options: providerOptions,
648
+ mcp,
649
+ context,
650
+ includes: asset.includes ?? defaults.includes,
651
+ environments,
652
+ tiers,
484
653
  metadata,
485
654
  sections
486
655
  };
487
656
  }
657
+ function normalizeDefaultIncludes(defaults, defaultsPath, filePath) {
658
+ if (!defaults.includes) return defaults;
659
+ const promptDir = dirname2(filePath);
660
+ const defaultsDir = dirname2(defaultsPath);
661
+ const includes = defaults.includes.map((includePath) => {
662
+ const relativePath = relative(promptDir, resolve2(defaultsDir, includePath)).replace(/\\/g, "/");
663
+ return relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
664
+ });
665
+ return {
666
+ ...defaults,
667
+ includes
668
+ };
669
+ }
670
+ function mergeRecordBlock(base, local) {
671
+ if (base === void 0) return local;
672
+ if (local === void 0) return base;
673
+ return { ...base, ...local };
674
+ }
675
+ function mergeMcp(base, local) {
676
+ return mergeRecordBlock(base, local);
677
+ }
678
+ function mergeContext(base, local) {
679
+ const merged = {
680
+ ...base ?? {},
681
+ ...local ?? {},
682
+ history: mergeRecordBlock(base?.history, local?.history)
683
+ };
684
+ return Object.keys(merged).length > 0 ? merged : void 0;
685
+ }
488
686
  function mergeCache(base, local) {
489
687
  const merged = {
490
688
  ...base ?? {},
@@ -512,6 +710,72 @@ function mergeCache(base, local) {
512
710
  if (merged.google && Object.keys(merged.google).length === 0) delete merged.google;
513
711
  return Object.keys(merged).length > 0 ? merged : void 0;
514
712
  }
713
+ function mergeRaw(base, local) {
714
+ const merged = {
715
+ ...base ?? {},
716
+ ...local ?? {},
717
+ openai: mergeRecordBlock(base?.openai, local?.openai),
718
+ "openai-responses": mergeRecordBlock(base?.["openai-responses"], local?.["openai-responses"]),
719
+ openai_responses: mergeRecordBlock(base?.openai_responses, local?.openai_responses),
720
+ anthropic: mergeRecordBlock(base?.anthropic, local?.anthropic),
721
+ gemini: mergeRecordBlock(base?.gemini, local?.gemini),
722
+ google: mergeRecordBlock(base?.google, local?.google),
723
+ openrouter: mergeRecordBlock(base?.openrouter, local?.openrouter),
724
+ llmasaservice: mergeRecordBlock(base?.llmasaservice, local?.llmasaservice)
725
+ };
726
+ removeEmptyProviderBlocks(merged);
727
+ return Object.keys(merged).length > 0 ? merged : void 0;
728
+ }
729
+ function mergeProviderOptions(base, local) {
730
+ const merged = {
731
+ ...base ?? {},
732
+ ...local ?? {},
733
+ anthropic: mergeRecordBlock(base?.anthropic, local?.anthropic),
734
+ gemini: mergeRecordBlock(base?.gemini, local?.gemini),
735
+ openrouter: mergeRecordBlock(base?.openrouter, local?.openrouter),
736
+ llmasaservice: mergeRecordBlock(base?.llmasaservice, local?.llmasaservice)
737
+ };
738
+ removeEmptyProviderBlocks(merged);
739
+ return Object.keys(merged).length > 0 ? merged : void 0;
740
+ }
741
+ function mergeOverrideMap(base, local) {
742
+ const merged = { ...base ?? {} };
743
+ for (const [name, localOverride] of Object.entries(local ?? {})) {
744
+ merged[name] = mergeOverrideConfig(merged[name], localOverride);
745
+ }
746
+ return Object.keys(merged).length > 0 ? merged : void 0;
747
+ }
748
+ function mergeOverrideConfig(base, local) {
749
+ const merged = {
750
+ ...base ?? {},
751
+ ...local
752
+ };
753
+ const reasoning = mergeRecordBlock(base?.reasoning, local.reasoning);
754
+ const sampling = mergeRecordBlock(base?.sampling, local.sampling);
755
+ const response = mergeRecordBlock(base?.response, local.response);
756
+ const cache = mergeCache(base?.cache, local.cache);
757
+ const raw = mergeRaw(base?.raw, local.raw);
758
+ const providerOptions = mergeProviderOptions(base?.provider_options, local.provider_options);
759
+ if (reasoning !== void 0) merged.reasoning = reasoning;
760
+ if (sampling !== void 0) merged.sampling = sampling;
761
+ if (response !== void 0) merged.response = response;
762
+ if (cache !== void 0) merged.cache = cache;
763
+ if (raw !== void 0) merged.raw = raw;
764
+ if (providerOptions !== void 0) merged.provider_options = providerOptions;
765
+ return merged;
766
+ }
767
+ function removeEmptyProviderBlocks(value) {
768
+ for (const key of Object.keys(value)) {
769
+ const block = value[key];
770
+ if (block === void 0) {
771
+ delete value[key];
772
+ continue;
773
+ }
774
+ if (block !== null && typeof block === "object" && !Array.isArray(block) && Object.keys(block).length === 0) {
775
+ delete value[key];
776
+ }
777
+ }
778
+ }
515
779
 
516
780
  // src/renderer/interpolate.ts
517
781
  var VARIABLE_RE = /\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}/g;
@@ -519,12 +783,13 @@ var ESCAPED_OPEN = /\\\{\\\{/g;
519
783
  var ESCAPE_PLACEHOLDER = "\0ESCAPED_OPEN\0";
520
784
  function interpolate(template, variables, options = {}) {
521
785
  const { strict = false } = options;
786
+ const optionalVariables = new Set(options.optionalVariables ?? []);
522
787
  let result = template.replace(ESCAPED_OPEN, ESCAPE_PLACEHOLDER);
523
788
  result = result.replace(VARIABLE_RE, (match, name) => {
524
789
  if (name in variables) {
525
790
  return variables[name];
526
791
  }
527
- if (strict) {
792
+ if (strict && !optionalVariables.has(name)) {
528
793
  throw new Error(`Missing required variable: "${name}"`);
529
794
  }
530
795
  return match;
@@ -542,59 +807,19 @@ function extractVariables(template) {
542
807
  return [...vars];
543
808
  }
544
809
 
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
810
  // src/context.ts
585
811
  var textEncoder = new TextEncoder();
586
812
  function getContextInputs(asset) {
587
813
  return (asset.context?.inputs ?? []).map(normalizeContextInput);
588
814
  }
589
- function getContextInputNames(asset) {
590
- return getContextInputs(asset).map((input) => input.name);
591
- }
592
815
  function normalizeContextInput(input) {
593
816
  if (typeof input === "string") {
594
817
  return { name: input };
595
818
  }
596
819
  return {
597
820
  name: input.name,
821
+ optional: input.optional,
822
+ warnings: input.warnings,
598
823
  max_size: input.max_size,
599
824
  trim: input.trim,
600
825
  allow_regex: normalizeContextRegex(input.allow_regex),
@@ -603,6 +828,9 @@ function normalizeContextInput(input) {
603
828
  reject_secrets: normalizeBuiltInValidator(input.reject_secrets)
604
829
  };
605
830
  }
831
+ function areContextInputWarningsEnabled(input) {
832
+ return input.warnings !== false;
833
+ }
606
834
  function normalizeContextRegex(value) {
607
835
  if (value === void 0) {
608
836
  return void 0;
@@ -689,6 +917,45 @@ function compileContextRegex(regex, details) {
689
917
  }
690
918
  }
691
919
 
920
+ // src/composition/resolve-includes.ts
921
+ import { readFile as readFile3 } from "fs/promises";
922
+ import { resolve as resolve3, dirname as dirname3 } from "path";
923
+ async function resolveIncludes(asset, basePath, visited = /* @__PURE__ */ new Set()) {
924
+ if (!asset.includes || asset.includes.length === 0) {
925
+ return asset;
926
+ }
927
+ const baseDir = dirname3(basePath);
928
+ const resolvedPath = resolve3(basePath);
929
+ if (visited.has(resolvedPath)) {
930
+ throw new Error(`Circular include detected: ${resolvedPath}`);
931
+ }
932
+ visited.add(resolvedPath);
933
+ let mergedSystemInstructions = "";
934
+ for (const includePath of asset.includes) {
935
+ const fullPath = resolve3(baseDir, includePath);
936
+ if (visited.has(fullPath)) {
937
+ throw new Error(`Circular include detected: ${fullPath} (included from ${basePath})`);
938
+ }
939
+ const content = await readFile3(fullPath, "utf-8");
940
+ const { asset: includedAsset } = parsePrompt(content, fullPath);
941
+ const resolved = await resolveIncludes(includedAsset, fullPath, new Set(visited));
942
+ if (resolved.sections?.system_instructions) {
943
+ mergedSystemInstructions += resolved.sections.system_instructions + "\n\n";
944
+ }
945
+ }
946
+ const localSystem = asset.sections?.system_instructions ?? "";
947
+ const combinedSystem = (mergedSystemInstructions + localSystem).trim() || void 0;
948
+ return {
949
+ ...asset,
950
+ sections: {
951
+ ...asset.sections,
952
+ system_instructions: combinedSystem
953
+ },
954
+ // Drop includes from the resolved asset — they've been inlined
955
+ includes: void 0
956
+ };
957
+ }
958
+
692
959
  // src/validation/levenshtein.ts
693
960
  function levenshtein(a, b) {
694
961
  const m = a.length;
@@ -733,6 +1000,18 @@ var KNOWN_FRONT_MATTER_KEYS = /* @__PURE__ */ new Set([
733
1000
  "provider_options",
734
1001
  "raw"
735
1002
  ]);
1003
+ function collectSchemaKeywords(value, acc) {
1004
+ if (!value || typeof value !== "object") return;
1005
+ if (Array.isArray(value)) {
1006
+ for (const item of value) collectSchemaKeywords(item, acc);
1007
+ return;
1008
+ }
1009
+ const record = value;
1010
+ for (const [key, child] of Object.entries(record)) {
1011
+ acc.add(key);
1012
+ collectSchemaKeywords(child, acc);
1013
+ }
1014
+ }
736
1015
  var RISKY_UNBOUNDED_INPUT_NAMES = [
737
1016
  "message",
738
1017
  "prompt",
@@ -784,7 +1063,9 @@ function validateAsset(asset, frontMatterKeys, filePath) {
784
1063
  }
785
1064
  }
786
1065
  }
787
- const declaredInputs = new Set(getContextInputNames(asset));
1066
+ const contextInputs = getContextInputs(asset);
1067
+ const declaredInputs = new Set(contextInputs.map((input) => input.name));
1068
+ const declaredInputsByName = new Map(contextInputs.map((input) => [input.name, input]));
788
1069
  const usedVars = /* @__PURE__ */ new Set();
789
1070
  if (asset.sections?.system_instructions) {
790
1071
  for (const v of extractVariables(asset.sections.system_instructions)) {
@@ -806,7 +1087,8 @@ function validateAsset(asset, frontMatterKeys, filePath) {
806
1087
  }
807
1088
  }
808
1089
  for (const v of declaredInputs) {
809
- if (!usedVars.has(v)) {
1090
+ const input = declaredInputsByName.get(v);
1091
+ if (!usedVars.has(v) && input?.optional !== true && areContextInputWarningsEnabled(input ?? {})) {
810
1092
  warnings.push({
811
1093
  code: "POK012",
812
1094
  message: `Variable "${v}" is declared in context.inputs but never used`,
@@ -822,9 +1104,10 @@ function validateAsset(asset, frontMatterKeys, filePath) {
822
1104
  suggestion: "Declare context.inputs to enable input policy validation."
823
1105
  });
824
1106
  }
825
- for (const input of getContextInputs(asset)) {
1107
+ for (const input of contextInputs) {
826
1108
  const lowerName = input.name.toLowerCase();
827
- if (input.max_size === void 0 && RISKY_UNBOUNDED_INPUT_NAMES.some((needle) => lowerName.includes(needle))) {
1109
+ const inputWarningsEnabled = areContextInputWarningsEnabled(input);
1110
+ if (inputWarningsEnabled && input.max_size === void 0 && RISKY_UNBOUNDED_INPUT_NAMES.some((needle) => lowerName.includes(needle))) {
828
1111
  warnings.push({
829
1112
  code: "POK040",
830
1113
  message: `Context input "${input.name}" has no max_size and appears unbounded.`,
@@ -832,7 +1115,7 @@ function validateAsset(asset, frontMatterKeys, filePath) {
832
1115
  suggestion: "Add max_size to constrain prompt payload growth."
833
1116
  });
834
1117
  }
835
- if (input.allow_regex === void 0 && input.deny_regex === void 0 && input.non_empty === void 0 && input.reject_secrets === void 0) {
1118
+ 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
1119
  warnings.push({
837
1120
  code: "POK041",
838
1121
  message: `Context input "${input.name}" has no input hardening validators.`,
@@ -840,7 +1123,7 @@ function validateAsset(asset, frontMatterKeys, filePath) {
840
1123
  suggestion: "Consider non_empty/reject_secrets and allow/deny regex validators."
841
1124
  });
842
1125
  }
843
- if (input.trim !== void 0 && input.trim !== false && input.max_size === void 0) {
1126
+ if (inputWarningsEnabled && input.trim !== void 0 && input.trim !== false && input.max_size === void 0) {
844
1127
  warnings.push({
845
1128
  code: "POK014",
846
1129
  message: `Context input "${input.name}" sets trim but has no max_size; trim-to-budget will be skipped.`,
@@ -867,6 +1150,30 @@ function validateAsset(asset, frontMatterKeys, filePath) {
867
1150
  }
868
1151
  }
869
1152
  }
1153
+ if (asset.response?.schema && asset.provider) {
1154
+ const keywords = /* @__PURE__ */ new Set();
1155
+ collectSchemaKeywords(asset.response.schema, keywords);
1156
+ const openAIUnsupported = ["patternProperties", "unevaluatedProperties", "$dynamicRef"];
1157
+ const geminiUnsupported = ["patternProperties", "$dynamicRef", "dependentSchemas"];
1158
+ const anthropicCaution = ["if", "then", "else"];
1159
+ const checks = [
1160
+ { provider: ["openai", "openai-responses", "openrouter", "llmasaservice"], unsupported: openAIUnsupported, code: "POK052" },
1161
+ { provider: ["gemini", "google"], unsupported: geminiUnsupported, code: "POK053" },
1162
+ { provider: ["anthropic"], unsupported: anthropicCaution, code: "POK054" }
1163
+ ];
1164
+ for (const check of checks) {
1165
+ if (!check.provider.includes(asset.provider)) continue;
1166
+ const hits = check.unsupported.filter((k) => keywords.has(k));
1167
+ if (hits.length > 0) {
1168
+ warnings.push({
1169
+ code: check.code,
1170
+ message: `response.schema includes provider-sensitive JSON Schema keywords for "${asset.provider}": ${hits.join(", ")}`,
1171
+ filePath,
1172
+ suggestion: "Validate schema compatibility with your provider docs or simplify the schema dialect."
1173
+ });
1174
+ }
1175
+ }
1176
+ }
870
1177
  if (asset.provider) {
871
1178
  let providerCache;
872
1179
  let cacheSuggestionField;
@@ -993,9 +1300,9 @@ function isRecord(value) {
993
1300
  }
994
1301
 
995
1302
  // src/prompt-resolution.ts
996
- import { readFile as readFile3 } from "fs/promises";
1303
+ import { readFile as readFile4 } from "fs/promises";
997
1304
  import { existsSync, statSync as statSync2 } from "fs";
998
- import { resolve as resolve3 } from "path";
1305
+ import { resolve as resolve4 } from "path";
999
1306
 
1000
1307
  // src/cache.ts
1001
1308
  import { statSync } from "fs";
@@ -1075,24 +1382,24 @@ function mergeOverride(base, override) {
1075
1382
  result.cache = {
1076
1383
  ...result.cache,
1077
1384
  ...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)
1385
+ openai: mergeRecordBlock2(result.cache?.openai, override.cache.openai),
1386
+ anthropic: mergeRecordBlock2(result.cache?.anthropic, override.cache.anthropic),
1387
+ gemini: mergeRecordBlock2(result.cache?.gemini, override.cache.gemini),
1388
+ google: mergeRecordBlock2(result.cache?.google, override.cache.google)
1082
1389
  };
1083
1390
  }
1084
1391
  if (override.raw !== void 0) {
1085
1392
  result.raw = {
1086
1393
  ...result.raw,
1087
1394
  ...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)
1395
+ openai: mergeRecordBlock2(result.raw?.openai, override.raw.openai),
1396
+ "openai-responses": mergeRecordBlock2(result.raw?.["openai-responses"], override.raw["openai-responses"]),
1397
+ openai_responses: mergeRecordBlock2(result.raw?.openai_responses, override.raw.openai_responses),
1398
+ anthropic: mergeRecordBlock2(result.raw?.anthropic, override.raw.anthropic),
1399
+ gemini: mergeRecordBlock2(result.raw?.gemini, override.raw.gemini),
1400
+ google: mergeRecordBlock2(result.raw?.google, override.raw.google),
1401
+ openrouter: mergeRecordBlock2(result.raw?.openrouter, override.raw.openrouter),
1402
+ llmasaservice: mergeRecordBlock2(result.raw?.llmasaservice, override.raw.llmasaservice)
1096
1403
  };
1097
1404
  }
1098
1405
  if (override.provider_options !== void 0) {
@@ -1119,7 +1426,7 @@ function mergeOverride(base, override) {
1119
1426
  }
1120
1427
  return result;
1121
1428
  }
1122
- function mergeRecordBlock(base, override) {
1429
+ function mergeRecordBlock2(base, override) {
1123
1430
  if (override === void 0) return base;
1124
1431
  return { ...base, ...override };
1125
1432
  }
@@ -1135,9 +1442,9 @@ var sharedPromptCache = new PromptCache();
1135
1442
 
1136
1443
  // src/cli/commands/defaults-root.ts
1137
1444
  import { existsSync as existsSync2 } from "fs";
1138
- import { dirname as dirname3, join as join2, resolve as resolve4 } from "path";
1445
+ import { dirname as dirname4, join as join2, resolve as resolve5 } from "path";
1139
1446
  function findDefaultsRoot(filePath) {
1140
- let current = resolve4(dirname3(filePath));
1447
+ let current = resolve5(dirname4(filePath));
1141
1448
  let root = current;
1142
1449
  let foundDefaults = false;
1143
1450
  while (true) {
@@ -1147,7 +1454,7 @@ function findDefaultsRoot(filePath) {
1147
1454
  } else if (foundDefaults) {
1148
1455
  return root;
1149
1456
  }
1150
- const parent = dirname3(current);
1457
+ const parent = dirname4(current);
1151
1458
  if (parent === current) {
1152
1459
  return root;
1153
1460
  }
@@ -1242,7 +1549,7 @@ function getFlag(args, ...flags) {
1242
1549
  async function collectPromptFiles(target) {
1243
1550
  const targetStats = await stat(target);
1244
1551
  if (targetStats.isFile()) {
1245
- if (extname(target) !== ".md" || target.endsWith("defaults.md") || target.endsWith(".test.md")) {
1552
+ if (extname2(target) !== ".md" || target.endsWith("defaults.md") || target.endsWith(".test.md")) {
1246
1553
  return [];
1247
1554
  }
1248
1555
  return [target];
@@ -1250,7 +1557,7 @@ async function collectPromptFiles(target) {
1250
1557
  const results = [];
1251
1558
  const entries = await readdir(target, { withFileTypes: true, recursive: true });
1252
1559
  for (const entry of entries) {
1253
- if (entry.isFile() && extname(entry.name) === ".md" && !entry.name.endsWith(".test.md") && entry.name !== "defaults.md") {
1560
+ if (entry.isFile() && extname2(entry.name) === ".md" && !entry.name.endsWith(".test.md") && entry.name !== "defaults.md") {
1254
1561
  results.push(join3(entry.parentPath ?? target, entry.name));
1255
1562
  }
1256
1563
  }
@@ -1259,7 +1566,7 @@ async function collectPromptFiles(target) {
1259
1566
 
1260
1567
  // src/cli/commands/compile.ts
1261
1568
  import { readdir as readdir2, writeFile, mkdir, rm } from "fs/promises";
1262
- import { join as join4, extname as extname2, relative, dirname as dirname5 } from "path";
1569
+ import { join as join4, extname as extname3, relative as relative2, dirname as dirname6 } from "path";
1263
1570
  var HELP2 = `
1264
1571
  promptopskit compile [sourceDir] [outputDir] [options]
1265
1572
 
@@ -1299,7 +1606,7 @@ async function compile(args) {
1299
1606
  let compiled = 0;
1300
1607
  let errors = 0;
1301
1608
  for (const file of files) {
1302
- const rel = relative(sourceDir, file).replace(/\.md$/, "");
1609
+ const rel = relative2(sourceDir, file).replace(/\.md$/, "");
1303
1610
  const outExt = format === "esm" ? ".mjs" : ".json";
1304
1611
  const outPath = join4(outputDir, rel + outExt);
1305
1612
  try {
@@ -1312,7 +1619,7 @@ async function compile(args) {
1312
1619
  if (dryRun) {
1313
1620
  console.log(` Would create: ${outPath}`);
1314
1621
  } else {
1315
- await mkdir(dirname5(outPath), { recursive: true });
1622
+ await mkdir(dirname6(outPath), { recursive: true });
1316
1623
  if (format === "esm") {
1317
1624
  const esmContent = `export default ${JSON.stringify(asset, null, 2)};
1318
1625
  `;
@@ -1368,7 +1675,7 @@ async function collectPromptFiles2(dir) {
1368
1675
  const results = [];
1369
1676
  const entries = await readdir2(dir, { withFileTypes: true, recursive: true });
1370
1677
  for (const entry of entries) {
1371
- if (entry.isFile() && extname2(entry.name) === ".md" && !entry.name.endsWith(".test.md") && entry.name !== "defaults.md") {
1678
+ if (entry.isFile() && extname3(entry.name) === ".md" && !entry.name.endsWith(".test.md") && entry.name !== "defaults.md") {
1372
1679
  results.push(join4(entry.parentPath ?? dir, entry.name));
1373
1680
  }
1374
1681
  }
@@ -1376,7 +1683,7 @@ async function collectPromptFiles2(dir) {
1376
1683
  }
1377
1684
 
1378
1685
  // src/cli/commands/render.ts
1379
- import { readFile as readFile4 } from "fs/promises";
1686
+ import { readFile as readFile5 } from "fs/promises";
1380
1687
  import { existsSync as existsSync3 } from "fs";
1381
1688
  var HELP3 = `
1382
1689
  promptopskit render <file> [options]
@@ -1407,13 +1714,13 @@ async function render(args) {
1407
1714
  const jsonOutput = args.includes("--json");
1408
1715
  let variables = {};
1409
1716
  if (varsFile) {
1410
- const varsContent = await readFile4(varsFile, "utf-8");
1717
+ const varsContent = await readFile5(varsFile, "utf-8");
1411
1718
  variables = JSON.parse(varsContent);
1412
1719
  } else {
1413
1720
  const sidecarPath = file.replace(/\.md$/, ".test.yaml");
1414
1721
  if (existsSync3(sidecarPath)) {
1415
1722
  const { default: yaml } = await import("gray-matter");
1416
- const sidecarContent = await readFile4(sidecarPath, "utf-8");
1723
+ const sidecarContent = await readFile5(sidecarPath, "utf-8");
1417
1724
  const parsed2 = yaml(`---
1418
1725
  ${sidecarContent}---
1419
1726
  `);
@@ -1512,7 +1819,7 @@ async function inspect(args) {
1512
1819
 
1513
1820
  // src/cli/commands/init.ts
1514
1821
  import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
1515
- import { join as join5, dirname as dirname6, relative as relative2 } from "path";
1822
+ import { join as join5, dirname as dirname7, relative as relative3 } from "path";
1516
1823
  import { existsSync as existsSync4, readFileSync } from "fs";
1517
1824
  var HELP5 = `
1518
1825
  promptopskit init [dir]
@@ -1663,8 +1970,8 @@ async function init(args) {
1663
1970
  return;
1664
1971
  }
1665
1972
  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) || ".";
1973
+ const testFilePath = join5(dirname7(dir), "tests", "hello.prompt.test.mjs");
1974
+ const promptsDirFromTest = relative3(dirname7(testFilePath), dir) || ".";
1668
1975
  const helloPromptTest = createHelloPromptTest(promptsDirFromTest);
1669
1976
  const files = [
1670
1977
  { path: join5(dir, "defaults.md"), content: DEFAULTS },
@@ -1682,7 +1989,7 @@ async function init(args) {
1682
1989
  skipped++;
1683
1990
  continue;
1684
1991
  }
1685
- await mkdir2(dirname6(file.path), { recursive: true });
1992
+ await mkdir2(dirname7(file.path), { recursive: true });
1686
1993
  await writeFile2(file.path, file.content, "utf-8");
1687
1994
  console.log(` \u2713 ${file.path}`);
1688
1995
  created++;
@@ -1750,8 +2057,8 @@ test('hello prompt can return a deterministic response without calling a model',
1750
2057
  }
1751
2058
 
1752
2059
  // 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";
2060
+ import { writeFile as writeFile3, readFile as readFile6, mkdir as mkdir3 } from "fs/promises";
2061
+ import { dirname as dirname8 } from "path";
1755
2062
  import { existsSync as existsSync5 } from "fs";
1756
2063
  var MARKER_START = "<!-- promptopskit:start -->";
1757
2064
  var MARKER_END = "<!-- promptopskit:end -->";
@@ -1839,7 +2146,7 @@ async function skill(args) {
1839
2146
  }
1840
2147
  const markedContent = config.wrap(wrapMarkers(STUB_CONTENT));
1841
2148
  if (existsSync5(filePath) && !force) {
1842
- const existing = await readFile5(filePath, "utf-8");
2149
+ const existing = await readFile6(filePath, "utf-8");
1843
2150
  const merged = mergeContent(existing, markedContent);
1844
2151
  if (merged === existing) {
1845
2152
  console.log(` skip ${filePath} (already up to date)`);
@@ -1850,7 +2157,7 @@ async function skill(args) {
1850
2157
  written++;
1851
2158
  continue;
1852
2159
  }
1853
- await mkdir3(dirname7(filePath), { recursive: true });
2160
+ await mkdir3(dirname8(filePath), { recursive: true });
1854
2161
  await writeFile3(filePath, markedContent, "utf-8");
1855
2162
  console.log(` \u2713 ${filePath}`);
1856
2163
  written++;
@@ -1863,12 +2170,12 @@ async function skill(args) {
1863
2170
  async function deployClaude(filePath, force) {
1864
2171
  const content = CLAUDE_LINE + "\n";
1865
2172
  if (!existsSync5(filePath) || force) {
1866
- await mkdir3(dirname7(filePath), { recursive: true });
2173
+ await mkdir3(dirname8(filePath), { recursive: true });
1867
2174
  await writeFile3(filePath, content, "utf-8");
1868
2175
  console.log(` \u2713 ${filePath}`);
1869
2176
  return 1;
1870
2177
  }
1871
- const existing = await readFile5(filePath, "utf-8");
2178
+ const existing = await readFile6(filePath, "utf-8");
1872
2179
  if (existing.split("\n").some((line) => line.trim() === CLAUDE_LINE)) {
1873
2180
  console.log(` skip ${filePath} (already up to date)`);
1874
2181
  return 0;