promptopskit 0.4.2 → 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.
- package/README.md +105 -62
- package/SKILL.md +376 -16
- package/dist/{chunk-7MRMN3SW.js → chunk-55B7QQBK.js} +6 -4
- package/dist/chunk-55B7QQBK.js.map +1 -0
- package/dist/{chunk-HWEVRBKZ.js → chunk-AGTGIZMR.js} +6 -2
- package/dist/chunk-AGTGIZMR.js.map +1 -0
- package/dist/chunk-FNCIBZJF.js +130 -0
- package/dist/chunk-FNCIBZJF.js.map +1 -0
- package/dist/{chunk-XL76NHZR.js → chunk-GLT3YYWK.js} +6 -4
- package/dist/chunk-GLT3YYWK.js.map +1 -0
- package/dist/{chunk-5AAYDOPU.js → chunk-IPFD5Y3O.js} +4 -3
- package/dist/chunk-IPFD5Y3O.js.map +1 -0
- package/dist/{chunk-YI4LILPY.js → chunk-JR7252DG.js} +584 -520
- package/dist/chunk-JR7252DG.js.map +1 -0
- package/dist/{chunk-JKBWC5WJ.js → chunk-UY5OKMVP.js} +302 -21
- package/dist/chunk-UY5OKMVP.js.map +1 -0
- package/dist/{chunk-MI3M6KDD.js → chunk-VMLGUI4F.js} +6 -4
- package/dist/chunk-VMLGUI4F.js.map +1 -0
- package/dist/{chunk-Z4IPWEUU.js → chunk-XEZJZ3IG.js} +6 -4
- package/dist/chunk-XEZJZ3IG.js.map +1 -0
- package/dist/cli/index.js +435 -106
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +1159 -636
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +10 -9
- package/dist/index.d.ts +10 -9
- package/dist/index.js +79 -17
- package/dist/index.js.map +1 -1
- package/dist/providers/anthropic.cjs +875 -533
- package/dist/providers/anthropic.cjs.map +1 -1
- package/dist/providers/anthropic.d.cts +2 -2
- package/dist/providers/anthropic.d.ts +2 -2
- package/dist/providers/anthropic.js +3 -3
- package/dist/providers/gemini.cjs +875 -533
- package/dist/providers/gemini.cjs.map +1 -1
- package/dist/providers/gemini.d.cts +2 -2
- package/dist/providers/gemini.d.ts +2 -2
- package/dist/providers/gemini.js +3 -3
- package/dist/providers/llmasaservice.cjs +1735 -0
- package/dist/providers/llmasaservice.cjs.map +1 -0
- package/dist/providers/llmasaservice.d.cts +28 -0
- package/dist/providers/llmasaservice.d.ts +28 -0
- package/dist/providers/llmasaservice.js +20 -0
- package/dist/providers/llmasaservice.js.map +1 -0
- package/dist/providers/openai-responses.cjs +875 -533
- package/dist/providers/openai-responses.cjs.map +1 -1
- package/dist/providers/openai-responses.d.cts +2 -2
- package/dist/providers/openai-responses.d.ts +2 -2
- package/dist/providers/openai-responses.js +3 -3
- package/dist/providers/openai.cjs +875 -533
- package/dist/providers/openai.cjs.map +1 -1
- package/dist/providers/openai.d.cts +2 -2
- package/dist/providers/openai.d.ts +2 -2
- package/dist/providers/openai.js +3 -3
- package/dist/providers/openrouter.cjs +876 -533
- package/dist/providers/openrouter.cjs.map +1 -1
- package/dist/providers/openrouter.d.cts +2 -2
- package/dist/providers/openrouter.d.ts +2 -2
- package/dist/providers/openrouter.js +4 -4
- package/dist/{schema-B9A1zLRk.d.cts → schema-CuYsv8JA.d.cts} +853 -7
- package/dist/{schema-B9A1zLRk.d.ts → schema-CuYsv8JA.d.ts} +853 -7
- package/dist/testing.cjs +64 -9
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.js +1 -1
- package/dist/{types-DjGAwJxC.d.ts → types-DpK0mTHL.d.ts} +18 -6
- package/dist/{types-DpbzzZO2.d.cts → types-UA_p9uJy.d.cts} +18 -6
- package/dist/usagetap/index.cjs +6 -1
- package/dist/usagetap/index.cjs.map +1 -1
- package/dist/usagetap/index.d.cts +4 -3
- package/dist/usagetap/index.d.ts +4 -3
- package/dist/usagetap/index.js +3 -1
- package/package.json +19 -2
- package/dist/chunk-5AAYDOPU.js.map +0 -1
- package/dist/chunk-7MRMN3SW.js.map +0 -1
- package/dist/chunk-HWEVRBKZ.js.map +0 -1
- package/dist/chunk-JKBWC5WJ.js.map +0 -1
- package/dist/chunk-MI3M6KDD.js.map +0 -1
- package/dist/chunk-XL76NHZR.js.map +0 -1
- package/dist/chunk-YI4LILPY.js.map +0 -1
- package/dist/chunk-Z4IPWEUU.js.map +0 -1
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(),
|
|
@@ -65,10 +81,26 @@ var OpenRouterProviderOptionsSchema = z.object({
|
|
|
65
81
|
plugins: z.array(z.record(z.unknown())).optional(),
|
|
66
82
|
models: z.array(z.string()).optional()
|
|
67
83
|
});
|
|
84
|
+
var LLMAsAServiceCustomerSchema = z.object({
|
|
85
|
+
customer_id: z.string(),
|
|
86
|
+
customer_name: z.string().optional(),
|
|
87
|
+
customer_user_id: z.string().optional(),
|
|
88
|
+
customer_user_name: z.string().optional(),
|
|
89
|
+
customer_user_email: z.string().optional()
|
|
90
|
+
});
|
|
91
|
+
var LLMAsAServiceProviderOptionsSchema = z.object({
|
|
92
|
+
base_url: z.string().url().optional(),
|
|
93
|
+
project_id: z.string().optional(),
|
|
94
|
+
customer: LLMAsAServiceCustomerSchema.optional(),
|
|
95
|
+
conversationId: z.string().optional(),
|
|
96
|
+
conversationTitle: z.string().optional(),
|
|
97
|
+
projectId: z.string().optional()
|
|
98
|
+
});
|
|
68
99
|
var ProviderOptionsSchema = z.object({
|
|
69
100
|
anthropic: AnthropicProviderOptionsSchema.optional(),
|
|
70
101
|
gemini: GeminiProviderOptionsSchema.optional(),
|
|
71
|
-
openrouter: OpenRouterProviderOptionsSchema.optional()
|
|
102
|
+
openrouter: OpenRouterProviderOptionsSchema.optional(),
|
|
103
|
+
llmasaservice: LLMAsAServiceProviderOptionsSchema.optional()
|
|
72
104
|
});
|
|
73
105
|
var RawProviderBodySchema = z.object({
|
|
74
106
|
openai: z.record(z.unknown()).optional(),
|
|
@@ -77,7 +109,8 @@ var RawProviderBodySchema = z.object({
|
|
|
77
109
|
anthropic: z.record(z.unknown()).optional(),
|
|
78
110
|
gemini: z.record(z.unknown()).optional(),
|
|
79
111
|
google: z.record(z.unknown()).optional(),
|
|
80
|
-
openrouter: z.record(z.unknown()).optional()
|
|
112
|
+
openrouter: z.record(z.unknown()).optional(),
|
|
113
|
+
llmasaservice: z.record(z.unknown()).optional()
|
|
81
114
|
});
|
|
82
115
|
var OpenAICacheSchema = z.object({
|
|
83
116
|
prompt_cache_key: z.string().min(1).optional(),
|
|
@@ -119,6 +152,8 @@ var ContextBuiltInValidatorSchema = z.union([
|
|
|
119
152
|
]);
|
|
120
153
|
var ContextInputDefinitionObjectSchema = z.object({
|
|
121
154
|
name: z.string(),
|
|
155
|
+
optional: z.boolean().optional(),
|
|
156
|
+
warnings: z.boolean().optional(),
|
|
122
157
|
max_size: z.number().int().positive().optional(),
|
|
123
158
|
trim: z.union([z.boolean(), z.enum(["start", "end", "both"])]).optional(),
|
|
124
159
|
allow_regex: ContextRegexSchema.optional(),
|
|
@@ -148,7 +183,7 @@ var PromptAssetOverridesSchema = z.object({
|
|
|
148
183
|
fallback_models: z.array(z.string()).optional(),
|
|
149
184
|
reasoning: ReasoningSchema.optional(),
|
|
150
185
|
sampling: SamplingSchema.optional(),
|
|
151
|
-
response:
|
|
186
|
+
response: ResponseSchemaWithValidation.optional(),
|
|
152
187
|
cache: CacheSchema.optional(),
|
|
153
188
|
raw: RawProviderBodySchema.optional(),
|
|
154
189
|
tools: z.array(ToolRefSchema).optional(),
|
|
@@ -164,9 +199,21 @@ var SectionsSchema = z.object({
|
|
|
164
199
|
notes: z.string().optional()
|
|
165
200
|
});
|
|
166
201
|
var PromptDefaultsSchema = z.object({
|
|
167
|
-
provider: z.enum(["openai", "openai-responses", "anthropic", "google", "gemini", "openrouter", "any"]).optional(),
|
|
202
|
+
provider: z.enum(["openai", "openai-responses", "anthropic", "google", "gemini", "openrouter", "llmasaservice", "any"]).optional(),
|
|
168
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(),
|
|
169
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(),
|
|
170
217
|
metadata: MetadataSchema.optional(),
|
|
171
218
|
sections: z.object({
|
|
172
219
|
system_instructions: z.string().optional()
|
|
@@ -176,12 +223,12 @@ var PromptAssetSchema = z.object({
|
|
|
176
223
|
id: z.string(),
|
|
177
224
|
schema_version: z.number().int().positive().default(1),
|
|
178
225
|
description: z.string().optional(),
|
|
179
|
-
provider: z.enum(["openai", "openai-responses", "anthropic", "google", "gemini", "openrouter", "any"]).optional(),
|
|
226
|
+
provider: z.enum(["openai", "openai-responses", "anthropic", "google", "gemini", "openrouter", "llmasaservice", "any"]).optional(),
|
|
180
227
|
model: z.string().optional(),
|
|
181
228
|
fallback_models: z.array(z.string()).optional(),
|
|
182
229
|
reasoning: ReasoningSchema.optional(),
|
|
183
230
|
sampling: SamplingSchema.optional(),
|
|
184
|
-
response:
|
|
231
|
+
response: ResponseSchemaWithValidation.optional(),
|
|
185
232
|
cache: CacheSchema.optional(),
|
|
186
233
|
raw: RawProviderBodySchema.optional(),
|
|
187
234
|
tools: z.array(ToolRefSchema).optional(),
|
|
@@ -373,16 +420,122 @@ function parsePrompt(content, filePath) {
|
|
|
373
420
|
}
|
|
374
421
|
|
|
375
422
|
// src/parser/loader.ts
|
|
376
|
-
import { readFile } from "fs/promises";
|
|
377
|
-
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";
|
|
378
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
|
|
379
524
|
var DEFAULTS_FILE_NAME = "defaults.md";
|
|
380
525
|
async function loadPromptFile(filePath, options = {}) {
|
|
381
|
-
const content = await
|
|
526
|
+
const content = await readFile2(filePath, "utf-8");
|
|
382
527
|
const parsed = parsePrompt(content, filePath);
|
|
383
|
-
const root = options.defaultsRoot ??
|
|
528
|
+
const root = options.defaultsRoot ?? dirname2(filePath);
|
|
384
529
|
const defaults = await loadDefaultsForPath(filePath, root);
|
|
385
|
-
const
|
|
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;
|
|
386
539
|
return {
|
|
387
540
|
...parsed,
|
|
388
541
|
asset
|
|
@@ -394,8 +547,8 @@ async function loadDefaultsForPath(filePath, defaultsRoot) {
|
|
|
394
547
|
for (const dir of directories) {
|
|
395
548
|
const defaultsPath = join(dir, DEFAULTS_FILE_NAME);
|
|
396
549
|
try {
|
|
397
|
-
const defaultsContent = await
|
|
398
|
-
const defaults = parseDefaults(defaultsContent);
|
|
550
|
+
const defaultsContent = await readFile2(defaultsPath, "utf-8");
|
|
551
|
+
const defaults = normalizeDefaultIncludes(parseDefaults(defaultsContent), defaultsPath, filePath);
|
|
399
552
|
merged = mergeDefaults(merged, defaults);
|
|
400
553
|
} catch (error) {
|
|
401
554
|
if (error.code !== "ENOENT") {
|
|
@@ -407,14 +560,14 @@ async function loadDefaultsForPath(filePath, defaultsRoot) {
|
|
|
407
560
|
}
|
|
408
561
|
function getDirectoriesToCheck(filePath, defaultsRoot) {
|
|
409
562
|
const dirs = [];
|
|
410
|
-
let current =
|
|
411
|
-
const boundary = defaultsRoot ?
|
|
563
|
+
let current = resolve2(dirname2(filePath));
|
|
564
|
+
const boundary = defaultsRoot ? resolve2(defaultsRoot) : void 0;
|
|
412
565
|
while (true) {
|
|
413
566
|
dirs.unshift(current);
|
|
414
|
-
if (boundary && current === boundary || current ===
|
|
567
|
+
if (boundary && current === boundary || current === dirname2(current)) {
|
|
415
568
|
break;
|
|
416
569
|
}
|
|
417
|
-
current =
|
|
570
|
+
current = dirname2(current);
|
|
418
571
|
}
|
|
419
572
|
return dirs;
|
|
420
573
|
}
|
|
@@ -432,7 +585,19 @@ function mergeDefaults(base, local) {
|
|
|
432
585
|
return {
|
|
433
586
|
provider: local.provider ?? base.provider,
|
|
434
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),
|
|
435
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),
|
|
436
601
|
metadata: {
|
|
437
602
|
...base.metadata ?? {},
|
|
438
603
|
...local.metadata ?? {}
|
|
@@ -445,11 +610,20 @@ function mergeDefaults(base, local) {
|
|
|
445
610
|
}
|
|
446
611
|
function applyDefaults(asset, defaults) {
|
|
447
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);
|
|
448
622
|
const hasDefaultMetadata = defaults.metadata && Object.keys(defaults.metadata).length > 0;
|
|
449
623
|
const hasDefaultSystem = !!defaults.sections?.system_instructions;
|
|
450
|
-
const hasDefaultScalars = defaults.provider !== void 0 || defaults.model !== void 0;
|
|
451
|
-
const
|
|
452
|
-
if (!hasDefaultMetadata && !hasDefaultSystem && !hasDefaultScalars && !
|
|
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) {
|
|
453
627
|
return asset;
|
|
454
628
|
}
|
|
455
629
|
const mergedMetadata = {
|
|
@@ -463,11 +637,52 @@ function applyDefaults(asset, defaults) {
|
|
|
463
637
|
...asset,
|
|
464
638
|
provider: asset.provider ?? defaults.provider,
|
|
465
639
|
model: asset.model ?? defaults.model,
|
|
640
|
+
fallback_models: asset.fallback_models ?? defaults.fallback_models,
|
|
641
|
+
reasoning,
|
|
642
|
+
sampling,
|
|
643
|
+
response,
|
|
466
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,
|
|
467
653
|
metadata,
|
|
468
654
|
sections
|
|
469
655
|
};
|
|
470
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
|
+
}
|
|
471
686
|
function mergeCache(base, local) {
|
|
472
687
|
const merged = {
|
|
473
688
|
...base ?? {},
|
|
@@ -495,6 +710,72 @@ function mergeCache(base, local) {
|
|
|
495
710
|
if (merged.google && Object.keys(merged.google).length === 0) delete merged.google;
|
|
496
711
|
return Object.keys(merged).length > 0 ? merged : void 0;
|
|
497
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
|
+
}
|
|
498
779
|
|
|
499
780
|
// src/renderer/interpolate.ts
|
|
500
781
|
var VARIABLE_RE = /\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}/g;
|
|
@@ -502,12 +783,13 @@ var ESCAPED_OPEN = /\\\{\\\{/g;
|
|
|
502
783
|
var ESCAPE_PLACEHOLDER = "\0ESCAPED_OPEN\0";
|
|
503
784
|
function interpolate(template, variables, options = {}) {
|
|
504
785
|
const { strict = false } = options;
|
|
786
|
+
const optionalVariables = new Set(options.optionalVariables ?? []);
|
|
505
787
|
let result = template.replace(ESCAPED_OPEN, ESCAPE_PLACEHOLDER);
|
|
506
788
|
result = result.replace(VARIABLE_RE, (match, name) => {
|
|
507
789
|
if (name in variables) {
|
|
508
790
|
return variables[name];
|
|
509
791
|
}
|
|
510
|
-
if (strict) {
|
|
792
|
+
if (strict && !optionalVariables.has(name)) {
|
|
511
793
|
throw new Error(`Missing required variable: "${name}"`);
|
|
512
794
|
}
|
|
513
795
|
return match;
|
|
@@ -525,59 +807,19 @@ function extractVariables(template) {
|
|
|
525
807
|
return [...vars];
|
|
526
808
|
}
|
|
527
809
|
|
|
528
|
-
// src/composition/resolve-includes.ts
|
|
529
|
-
import { readFile as readFile2 } from "fs/promises";
|
|
530
|
-
import { resolve as resolve2, dirname as dirname2 } from "path";
|
|
531
|
-
async function resolveIncludes(asset, basePath, visited = /* @__PURE__ */ new Set()) {
|
|
532
|
-
if (!asset.includes || asset.includes.length === 0) {
|
|
533
|
-
return asset;
|
|
534
|
-
}
|
|
535
|
-
const baseDir = dirname2(basePath);
|
|
536
|
-
const resolvedPath = resolve2(basePath);
|
|
537
|
-
if (visited.has(resolvedPath)) {
|
|
538
|
-
throw new Error(`Circular include detected: ${resolvedPath}`);
|
|
539
|
-
}
|
|
540
|
-
visited.add(resolvedPath);
|
|
541
|
-
let mergedSystemInstructions = "";
|
|
542
|
-
for (const includePath of asset.includes) {
|
|
543
|
-
const fullPath = resolve2(baseDir, includePath);
|
|
544
|
-
if (visited.has(fullPath)) {
|
|
545
|
-
throw new Error(`Circular include detected: ${fullPath} (included from ${basePath})`);
|
|
546
|
-
}
|
|
547
|
-
const content = await readFile2(fullPath, "utf-8");
|
|
548
|
-
const { asset: includedAsset } = parsePrompt(content, fullPath);
|
|
549
|
-
const resolved = await resolveIncludes(includedAsset, fullPath, new Set(visited));
|
|
550
|
-
if (resolved.sections?.system_instructions) {
|
|
551
|
-
mergedSystemInstructions += resolved.sections.system_instructions + "\n\n";
|
|
552
|
-
}
|
|
553
|
-
}
|
|
554
|
-
const localSystem = asset.sections?.system_instructions ?? "";
|
|
555
|
-
const combinedSystem = (mergedSystemInstructions + localSystem).trim() || void 0;
|
|
556
|
-
return {
|
|
557
|
-
...asset,
|
|
558
|
-
sections: {
|
|
559
|
-
...asset.sections,
|
|
560
|
-
system_instructions: combinedSystem
|
|
561
|
-
},
|
|
562
|
-
// Drop includes from the resolved asset — they've been inlined
|
|
563
|
-
includes: void 0
|
|
564
|
-
};
|
|
565
|
-
}
|
|
566
|
-
|
|
567
810
|
// src/context.ts
|
|
568
811
|
var textEncoder = new TextEncoder();
|
|
569
812
|
function getContextInputs(asset) {
|
|
570
813
|
return (asset.context?.inputs ?? []).map(normalizeContextInput);
|
|
571
814
|
}
|
|
572
|
-
function getContextInputNames(asset) {
|
|
573
|
-
return getContextInputs(asset).map((input) => input.name);
|
|
574
|
-
}
|
|
575
815
|
function normalizeContextInput(input) {
|
|
576
816
|
if (typeof input === "string") {
|
|
577
817
|
return { name: input };
|
|
578
818
|
}
|
|
579
819
|
return {
|
|
580
820
|
name: input.name,
|
|
821
|
+
optional: input.optional,
|
|
822
|
+
warnings: input.warnings,
|
|
581
823
|
max_size: input.max_size,
|
|
582
824
|
trim: input.trim,
|
|
583
825
|
allow_regex: normalizeContextRegex(input.allow_regex),
|
|
@@ -586,6 +828,9 @@ function normalizeContextInput(input) {
|
|
|
586
828
|
reject_secrets: normalizeBuiltInValidator(input.reject_secrets)
|
|
587
829
|
};
|
|
588
830
|
}
|
|
831
|
+
function areContextInputWarningsEnabled(input) {
|
|
832
|
+
return input.warnings !== false;
|
|
833
|
+
}
|
|
589
834
|
function normalizeContextRegex(value) {
|
|
590
835
|
if (value === void 0) {
|
|
591
836
|
return void 0;
|
|
@@ -672,6 +917,45 @@ function compileContextRegex(regex, details) {
|
|
|
672
917
|
}
|
|
673
918
|
}
|
|
674
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
|
+
|
|
675
959
|
// src/validation/levenshtein.ts
|
|
676
960
|
function levenshtein(a, b) {
|
|
677
961
|
const m = a.length;
|
|
@@ -716,6 +1000,18 @@ var KNOWN_FRONT_MATTER_KEYS = /* @__PURE__ */ new Set([
|
|
|
716
1000
|
"provider_options",
|
|
717
1001
|
"raw"
|
|
718
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
|
+
}
|
|
719
1015
|
var RISKY_UNBOUNDED_INPUT_NAMES = [
|
|
720
1016
|
"message",
|
|
721
1017
|
"prompt",
|
|
@@ -767,7 +1063,9 @@ function validateAsset(asset, frontMatterKeys, filePath) {
|
|
|
767
1063
|
}
|
|
768
1064
|
}
|
|
769
1065
|
}
|
|
770
|
-
const
|
|
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]));
|
|
771
1069
|
const usedVars = /* @__PURE__ */ new Set();
|
|
772
1070
|
if (asset.sections?.system_instructions) {
|
|
773
1071
|
for (const v of extractVariables(asset.sections.system_instructions)) {
|
|
@@ -789,7 +1087,8 @@ function validateAsset(asset, frontMatterKeys, filePath) {
|
|
|
789
1087
|
}
|
|
790
1088
|
}
|
|
791
1089
|
for (const v of declaredInputs) {
|
|
792
|
-
|
|
1090
|
+
const input = declaredInputsByName.get(v);
|
|
1091
|
+
if (!usedVars.has(v) && input?.optional !== true && areContextInputWarningsEnabled(input ?? {})) {
|
|
793
1092
|
warnings.push({
|
|
794
1093
|
code: "POK012",
|
|
795
1094
|
message: `Variable "${v}" is declared in context.inputs but never used`,
|
|
@@ -805,9 +1104,10 @@ function validateAsset(asset, frontMatterKeys, filePath) {
|
|
|
805
1104
|
suggestion: "Declare context.inputs to enable input policy validation."
|
|
806
1105
|
});
|
|
807
1106
|
}
|
|
808
|
-
for (const input of
|
|
1107
|
+
for (const input of contextInputs) {
|
|
809
1108
|
const lowerName = input.name.toLowerCase();
|
|
810
|
-
|
|
1109
|
+
const inputWarningsEnabled = areContextInputWarningsEnabled(input);
|
|
1110
|
+
if (inputWarningsEnabled && input.max_size === void 0 && RISKY_UNBOUNDED_INPUT_NAMES.some((needle) => lowerName.includes(needle))) {
|
|
811
1111
|
warnings.push({
|
|
812
1112
|
code: "POK040",
|
|
813
1113
|
message: `Context input "${input.name}" has no max_size and appears unbounded.`,
|
|
@@ -815,7 +1115,7 @@ function validateAsset(asset, frontMatterKeys, filePath) {
|
|
|
815
1115
|
suggestion: "Add max_size to constrain prompt payload growth."
|
|
816
1116
|
});
|
|
817
1117
|
}
|
|
818
|
-
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) {
|
|
819
1119
|
warnings.push({
|
|
820
1120
|
code: "POK041",
|
|
821
1121
|
message: `Context input "${input.name}" has no input hardening validators.`,
|
|
@@ -823,7 +1123,7 @@ function validateAsset(asset, frontMatterKeys, filePath) {
|
|
|
823
1123
|
suggestion: "Consider non_empty/reject_secrets and allow/deny regex validators."
|
|
824
1124
|
});
|
|
825
1125
|
}
|
|
826
|
-
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) {
|
|
827
1127
|
warnings.push({
|
|
828
1128
|
code: "POK014",
|
|
829
1129
|
message: `Context input "${input.name}" sets trim but has no max_size; trim-to-budget will be skipped.`,
|
|
@@ -850,6 +1150,30 @@ function validateAsset(asset, frontMatterKeys, filePath) {
|
|
|
850
1150
|
}
|
|
851
1151
|
}
|
|
852
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
|
+
}
|
|
853
1177
|
if (asset.provider) {
|
|
854
1178
|
let providerCache;
|
|
855
1179
|
let cacheSuggestionField;
|
|
@@ -976,9 +1300,9 @@ function isRecord(value) {
|
|
|
976
1300
|
}
|
|
977
1301
|
|
|
978
1302
|
// src/prompt-resolution.ts
|
|
979
|
-
import { readFile as
|
|
1303
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
980
1304
|
import { existsSync, statSync as statSync2 } from "fs";
|
|
981
|
-
import { resolve as
|
|
1305
|
+
import { resolve as resolve4 } from "path";
|
|
982
1306
|
|
|
983
1307
|
// src/cache.ts
|
|
984
1308
|
import { statSync } from "fs";
|
|
@@ -1058,23 +1382,24 @@ function mergeOverride(base, override) {
|
|
|
1058
1382
|
result.cache = {
|
|
1059
1383
|
...result.cache,
|
|
1060
1384
|
...override.cache,
|
|
1061
|
-
openai:
|
|
1062
|
-
anthropic:
|
|
1063
|
-
gemini:
|
|
1064
|
-
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)
|
|
1065
1389
|
};
|
|
1066
1390
|
}
|
|
1067
1391
|
if (override.raw !== void 0) {
|
|
1068
1392
|
result.raw = {
|
|
1069
1393
|
...result.raw,
|
|
1070
1394
|
...override.raw,
|
|
1071
|
-
openai:
|
|
1072
|
-
"openai-responses":
|
|
1073
|
-
openai_responses:
|
|
1074
|
-
anthropic:
|
|
1075
|
-
gemini:
|
|
1076
|
-
google:
|
|
1077
|
-
openrouter:
|
|
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)
|
|
1078
1403
|
};
|
|
1079
1404
|
}
|
|
1080
1405
|
if (override.provider_options !== void 0) {
|
|
@@ -1092,12 +1417,16 @@ function mergeOverride(base, override) {
|
|
|
1092
1417
|
openrouter: {
|
|
1093
1418
|
...result.provider_options?.openrouter,
|
|
1094
1419
|
...override.provider_options.openrouter
|
|
1420
|
+
},
|
|
1421
|
+
llmasaservice: {
|
|
1422
|
+
...result.provider_options?.llmasaservice,
|
|
1423
|
+
...override.provider_options.llmasaservice
|
|
1095
1424
|
}
|
|
1096
1425
|
};
|
|
1097
1426
|
}
|
|
1098
1427
|
return result;
|
|
1099
1428
|
}
|
|
1100
|
-
function
|
|
1429
|
+
function mergeRecordBlock2(base, override) {
|
|
1101
1430
|
if (override === void 0) return base;
|
|
1102
1431
|
return { ...base, ...override };
|
|
1103
1432
|
}
|
|
@@ -1113,9 +1442,9 @@ var sharedPromptCache = new PromptCache();
|
|
|
1113
1442
|
|
|
1114
1443
|
// src/cli/commands/defaults-root.ts
|
|
1115
1444
|
import { existsSync as existsSync2 } from "fs";
|
|
1116
|
-
import { dirname as
|
|
1445
|
+
import { dirname as dirname4, join as join2, resolve as resolve5 } from "path";
|
|
1117
1446
|
function findDefaultsRoot(filePath) {
|
|
1118
|
-
let current =
|
|
1447
|
+
let current = resolve5(dirname4(filePath));
|
|
1119
1448
|
let root = current;
|
|
1120
1449
|
let foundDefaults = false;
|
|
1121
1450
|
while (true) {
|
|
@@ -1125,7 +1454,7 @@ function findDefaultsRoot(filePath) {
|
|
|
1125
1454
|
} else if (foundDefaults) {
|
|
1126
1455
|
return root;
|
|
1127
1456
|
}
|
|
1128
|
-
const parent =
|
|
1457
|
+
const parent = dirname4(current);
|
|
1129
1458
|
if (parent === current) {
|
|
1130
1459
|
return root;
|
|
1131
1460
|
}
|
|
@@ -1220,7 +1549,7 @@ function getFlag(args, ...flags) {
|
|
|
1220
1549
|
async function collectPromptFiles(target) {
|
|
1221
1550
|
const targetStats = await stat(target);
|
|
1222
1551
|
if (targetStats.isFile()) {
|
|
1223
|
-
if (
|
|
1552
|
+
if (extname2(target) !== ".md" || target.endsWith("defaults.md") || target.endsWith(".test.md")) {
|
|
1224
1553
|
return [];
|
|
1225
1554
|
}
|
|
1226
1555
|
return [target];
|
|
@@ -1228,7 +1557,7 @@ async function collectPromptFiles(target) {
|
|
|
1228
1557
|
const results = [];
|
|
1229
1558
|
const entries = await readdir(target, { withFileTypes: true, recursive: true });
|
|
1230
1559
|
for (const entry of entries) {
|
|
1231
|
-
if (entry.isFile() &&
|
|
1560
|
+
if (entry.isFile() && extname2(entry.name) === ".md" && !entry.name.endsWith(".test.md") && entry.name !== "defaults.md") {
|
|
1232
1561
|
results.push(join3(entry.parentPath ?? target, entry.name));
|
|
1233
1562
|
}
|
|
1234
1563
|
}
|
|
@@ -1237,7 +1566,7 @@ async function collectPromptFiles(target) {
|
|
|
1237
1566
|
|
|
1238
1567
|
// src/cli/commands/compile.ts
|
|
1239
1568
|
import { readdir as readdir2, writeFile, mkdir, rm } from "fs/promises";
|
|
1240
|
-
import { join as join4, extname as
|
|
1569
|
+
import { join as join4, extname as extname3, relative as relative2, dirname as dirname6 } from "path";
|
|
1241
1570
|
var HELP2 = `
|
|
1242
1571
|
promptopskit compile [sourceDir] [outputDir] [options]
|
|
1243
1572
|
|
|
@@ -1277,7 +1606,7 @@ async function compile(args) {
|
|
|
1277
1606
|
let compiled = 0;
|
|
1278
1607
|
let errors = 0;
|
|
1279
1608
|
for (const file of files) {
|
|
1280
|
-
const rel =
|
|
1609
|
+
const rel = relative2(sourceDir, file).replace(/\.md$/, "");
|
|
1281
1610
|
const outExt = format === "esm" ? ".mjs" : ".json";
|
|
1282
1611
|
const outPath = join4(outputDir, rel + outExt);
|
|
1283
1612
|
try {
|
|
@@ -1290,7 +1619,7 @@ async function compile(args) {
|
|
|
1290
1619
|
if (dryRun) {
|
|
1291
1620
|
console.log(` Would create: ${outPath}`);
|
|
1292
1621
|
} else {
|
|
1293
|
-
await mkdir(
|
|
1622
|
+
await mkdir(dirname6(outPath), { recursive: true });
|
|
1294
1623
|
if (format === "esm") {
|
|
1295
1624
|
const esmContent = `export default ${JSON.stringify(asset, null, 2)};
|
|
1296
1625
|
`;
|
|
@@ -1346,7 +1675,7 @@ async function collectPromptFiles2(dir) {
|
|
|
1346
1675
|
const results = [];
|
|
1347
1676
|
const entries = await readdir2(dir, { withFileTypes: true, recursive: true });
|
|
1348
1677
|
for (const entry of entries) {
|
|
1349
|
-
if (entry.isFile() &&
|
|
1678
|
+
if (entry.isFile() && extname3(entry.name) === ".md" && !entry.name.endsWith(".test.md") && entry.name !== "defaults.md") {
|
|
1350
1679
|
results.push(join4(entry.parentPath ?? dir, entry.name));
|
|
1351
1680
|
}
|
|
1352
1681
|
}
|
|
@@ -1354,7 +1683,7 @@ async function collectPromptFiles2(dir) {
|
|
|
1354
1683
|
}
|
|
1355
1684
|
|
|
1356
1685
|
// src/cli/commands/render.ts
|
|
1357
|
-
import { readFile as
|
|
1686
|
+
import { readFile as readFile5 } from "fs/promises";
|
|
1358
1687
|
import { existsSync as existsSync3 } from "fs";
|
|
1359
1688
|
var HELP3 = `
|
|
1360
1689
|
promptopskit render <file> [options]
|
|
@@ -1385,13 +1714,13 @@ async function render(args) {
|
|
|
1385
1714
|
const jsonOutput = args.includes("--json");
|
|
1386
1715
|
let variables = {};
|
|
1387
1716
|
if (varsFile) {
|
|
1388
|
-
const varsContent = await
|
|
1717
|
+
const varsContent = await readFile5(varsFile, "utf-8");
|
|
1389
1718
|
variables = JSON.parse(varsContent);
|
|
1390
1719
|
} else {
|
|
1391
1720
|
const sidecarPath = file.replace(/\.md$/, ".test.yaml");
|
|
1392
1721
|
if (existsSync3(sidecarPath)) {
|
|
1393
1722
|
const { default: yaml } = await import("gray-matter");
|
|
1394
|
-
const sidecarContent = await
|
|
1723
|
+
const sidecarContent = await readFile5(sidecarPath, "utf-8");
|
|
1395
1724
|
const parsed2 = yaml(`---
|
|
1396
1725
|
${sidecarContent}---
|
|
1397
1726
|
`);
|
|
@@ -1490,7 +1819,7 @@ async function inspect(args) {
|
|
|
1490
1819
|
|
|
1491
1820
|
// src/cli/commands/init.ts
|
|
1492
1821
|
import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
1493
|
-
import { join as join5, dirname as
|
|
1822
|
+
import { join as join5, dirname as dirname7, relative as relative3 } from "path";
|
|
1494
1823
|
import { existsSync as existsSync4, readFileSync } from "fs";
|
|
1495
1824
|
var HELP5 = `
|
|
1496
1825
|
promptopskit init [dir]
|
|
@@ -1641,8 +1970,8 @@ async function init(args) {
|
|
|
1641
1970
|
return;
|
|
1642
1971
|
}
|
|
1643
1972
|
const dir = args.find((a) => !a.startsWith("--")) ?? "./prompts";
|
|
1644
|
-
const testFilePath = join5(
|
|
1645
|
-
const promptsDirFromTest =
|
|
1973
|
+
const testFilePath = join5(dirname7(dir), "tests", "hello.prompt.test.mjs");
|
|
1974
|
+
const promptsDirFromTest = relative3(dirname7(testFilePath), dir) || ".";
|
|
1646
1975
|
const helloPromptTest = createHelloPromptTest(promptsDirFromTest);
|
|
1647
1976
|
const files = [
|
|
1648
1977
|
{ path: join5(dir, "defaults.md"), content: DEFAULTS },
|
|
@@ -1660,7 +1989,7 @@ async function init(args) {
|
|
|
1660
1989
|
skipped++;
|
|
1661
1990
|
continue;
|
|
1662
1991
|
}
|
|
1663
|
-
await mkdir2(
|
|
1992
|
+
await mkdir2(dirname7(file.path), { recursive: true });
|
|
1664
1993
|
await writeFile2(file.path, file.content, "utf-8");
|
|
1665
1994
|
console.log(` \u2713 ${file.path}`);
|
|
1666
1995
|
created++;
|
|
@@ -1728,8 +2057,8 @@ test('hello prompt can return a deterministic response without calling a model',
|
|
|
1728
2057
|
}
|
|
1729
2058
|
|
|
1730
2059
|
// src/cli/commands/skill.ts
|
|
1731
|
-
import { writeFile as writeFile3, readFile as
|
|
1732
|
-
import { dirname as
|
|
2060
|
+
import { writeFile as writeFile3, readFile as readFile6, mkdir as mkdir3 } from "fs/promises";
|
|
2061
|
+
import { dirname as dirname8 } from "path";
|
|
1733
2062
|
import { existsSync as existsSync5 } from "fs";
|
|
1734
2063
|
var MARKER_START = "<!-- promptopskit:start -->";
|
|
1735
2064
|
var MARKER_END = "<!-- promptopskit:end -->";
|
|
@@ -1817,7 +2146,7 @@ async function skill(args) {
|
|
|
1817
2146
|
}
|
|
1818
2147
|
const markedContent = config.wrap(wrapMarkers(STUB_CONTENT));
|
|
1819
2148
|
if (existsSync5(filePath) && !force) {
|
|
1820
|
-
const existing = await
|
|
2149
|
+
const existing = await readFile6(filePath, "utf-8");
|
|
1821
2150
|
const merged = mergeContent(existing, markedContent);
|
|
1822
2151
|
if (merged === existing) {
|
|
1823
2152
|
console.log(` skip ${filePath} (already up to date)`);
|
|
@@ -1828,7 +2157,7 @@ async function skill(args) {
|
|
|
1828
2157
|
written++;
|
|
1829
2158
|
continue;
|
|
1830
2159
|
}
|
|
1831
|
-
await mkdir3(
|
|
2160
|
+
await mkdir3(dirname8(filePath), { recursive: true });
|
|
1832
2161
|
await writeFile3(filePath, markedContent, "utf-8");
|
|
1833
2162
|
console.log(` \u2713 ${filePath}`);
|
|
1834
2163
|
written++;
|
|
@@ -1841,12 +2170,12 @@ async function skill(args) {
|
|
|
1841
2170
|
async function deployClaude(filePath, force) {
|
|
1842
2171
|
const content = CLAUDE_LINE + "\n";
|
|
1843
2172
|
if (!existsSync5(filePath) || force) {
|
|
1844
|
-
await mkdir3(
|
|
2173
|
+
await mkdir3(dirname8(filePath), { recursive: true });
|
|
1845
2174
|
await writeFile3(filePath, content, "utf-8");
|
|
1846
2175
|
console.log(` \u2713 ${filePath}`);
|
|
1847
2176
|
return 1;
|
|
1848
2177
|
}
|
|
1849
|
-
const existing = await
|
|
2178
|
+
const existing = await readFile6(filePath, "utf-8");
|
|
1850
2179
|
if (existing.split("\n").some((line) => line.trim() === CLAUDE_LINE)) {
|
|
1851
2180
|
console.log(` skip ${filePath} (already up to date)`);
|
|
1852
2181
|
return 0;
|