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.
- package/README.md +9 -7
- package/SKILL.md +16 -10
- package/dist/{chunk-6VLKZCNS.js → chunk-55B7QQBK.js} +2 -2
- package/dist/{chunk-6BOUQVSX.js → chunk-FNCIBZJF.js} +3 -3
- package/dist/{chunk-D75HKF64.js → chunk-GLT3YYWK.js} +2 -2
- package/dist/{chunk-IN4KT5EU.js → chunk-IPFD5Y3O.js} +3 -3
- package/dist/{chunk-KN7H4JRK.js → chunk-JR7252DG.js} +393 -386
- package/dist/chunk-JR7252DG.js.map +1 -0
- package/dist/{chunk-K6ZU3CGV.js → chunk-UY5OKMVP.js} +281 -17
- package/dist/chunk-UY5OKMVP.js.map +1 -0
- package/dist/{chunk-QK3WKE3K.js → chunk-VMLGUI4F.js} +2 -2
- package/dist/{chunk-LNGHGGJN.js → chunk-XEZJZ3IG.js} +2 -2
- package/dist/cli/index.js +410 -103
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +971 -660
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -4
- package/dist/index.d.ts +5 -4
- package/dist/index.js +55 -15
- package/dist/index.js.map +1 -1
- package/dist/providers/anthropic.cjs +675 -407
- 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 +675 -407
- 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 +675 -407
- package/dist/providers/llmasaservice.cjs.map +1 -1
- package/dist/providers/llmasaservice.d.cts +2 -2
- package/dist/providers/llmasaservice.d.ts +2 -2
- package/dist/providers/llmasaservice.js +4 -4
- package/dist/providers/openai-responses.cjs +675 -407
- 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 +675 -407
- 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 +675 -407
- 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-D-RI4w-k.d.cts → schema-CuYsv8JA.d.cts} +540 -206
- package/dist/{schema-D-RI4w-k.d.ts → schema-CuYsv8JA.d.ts} +540 -206
- package/dist/testing.cjs +43 -5
- 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-B3dxBGu6.d.ts → types-DpK0mTHL.d.ts} +1 -1
- package/dist/{types-DbTAjlTN.d.cts → types-UA_p9uJy.d.cts} +1 -1
- package/dist/usagetap/index.d.cts +2 -2
- package/dist/usagetap/index.d.ts +2 -2
- package/package.json +9 -2
- package/dist/chunk-K6ZU3CGV.js.map +0 -1
- package/dist/chunk-KN7H4JRK.js.map +0 -1
- /package/dist/{chunk-6VLKZCNS.js.map → chunk-55B7QQBK.js.map} +0 -0
- /package/dist/{chunk-6BOUQVSX.js.map → chunk-FNCIBZJF.js.map} +0 -0
- /package/dist/{chunk-D75HKF64.js.map → chunk-GLT3YYWK.js.map} +0 -0
- /package/dist/{chunk-IN4KT5EU.js.map → chunk-IPFD5Y3O.js.map} +0 -0
- /package/dist/{chunk-QK3WKE3K.js.map → chunk-VMLGUI4F.js.map} +0 -0
- /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:
|
|
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:
|
|
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
|
|
526
|
+
const content = await readFile2(filePath, "utf-8");
|
|
399
527
|
const parsed = parsePrompt(content, filePath);
|
|
400
|
-
const root = options.defaultsRoot ??
|
|
528
|
+
const root = options.defaultsRoot ?? dirname2(filePath);
|
|
401
529
|
const defaults = await loadDefaultsForPath(filePath, root);
|
|
402
|
-
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;
|
|
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
|
|
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 =
|
|
428
|
-
const boundary = defaultsRoot ?
|
|
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 ===
|
|
567
|
+
if (boundary && current === boundary || current === dirname2(current)) {
|
|
432
568
|
break;
|
|
433
569
|
}
|
|
434
|
-
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
|
|
469
|
-
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) {
|
|
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
|
|
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
|
-
|
|
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
|
|
1107
|
+
for (const input of contextInputs) {
|
|
826
1108
|
const lowerName = input.name.toLowerCase();
|
|
827
|
-
|
|
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
|
|
1303
|
+
import { readFile as readFile4 } from "fs/promises";
|
|
997
1304
|
import { existsSync, statSync as statSync2 } from "fs";
|
|
998
|
-
import { resolve as
|
|
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:
|
|
1079
|
-
anthropic:
|
|
1080
|
-
gemini:
|
|
1081
|
-
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:
|
|
1089
|
-
"openai-responses":
|
|
1090
|
-
openai_responses:
|
|
1091
|
-
anthropic:
|
|
1092
|
-
gemini:
|
|
1093
|
-
google:
|
|
1094
|
-
openrouter:
|
|
1095
|
-
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
|
|
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
|
|
1445
|
+
import { dirname as dirname4, join as join2, resolve as resolve5 } from "path";
|
|
1139
1446
|
function findDefaultsRoot(filePath) {
|
|
1140
|
-
let current =
|
|
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 =
|
|
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 (
|
|
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() &&
|
|
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
|
|
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 =
|
|
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(
|
|
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() &&
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
1667
|
-
const promptsDirFromTest =
|
|
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(
|
|
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
|
|
1754
|
-
import { dirname as
|
|
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
|
|
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(
|
|
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(
|
|
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
|
|
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;
|