promptopskit 0.3.9 → 0.4.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 +91 -7
- package/SKILL.md +69 -6
- package/dist/chunk-5AAYDOPU.js +60 -0
- package/dist/chunk-5AAYDOPU.js.map +1 -0
- package/dist/{chunk-YNIAA4ZJ.js → chunk-7MRMN3SW.js} +5 -3
- package/dist/chunk-7MRMN3SW.js.map +1 -0
- package/dist/{chunk-4LEPCCSA.js → chunk-JKBWC5WJ.js} +176 -4
- package/dist/chunk-JKBWC5WJ.js.map +1 -0
- package/dist/{chunk-AN3OR7YC.js → chunk-MI3M6KDD.js} +7 -5
- package/dist/chunk-MI3M6KDD.js.map +1 -0
- package/dist/{chunk-LMURMZUA.js → chunk-XL76NHZR.js} +5 -3
- package/dist/chunk-XL76NHZR.js.map +1 -0
- package/dist/{chunk-V7QY7MRM.js → chunk-YI4LILPY.js} +50 -3
- package/dist/chunk-YI4LILPY.js.map +1 -0
- package/dist/{chunk-O6RQZYS4.js → chunk-Z4IPWEUU.js} +16 -5
- package/dist/chunk-Z4IPWEUU.js.map +1 -0
- package/dist/cli/index.js +440 -52
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +430 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +162 -10
- package/dist/index.js.map +1 -1
- package/dist/providers/anthropic.cjs +232 -6
- 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 +223 -6
- 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/openai-responses.cjs +221 -4
- 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 +221 -4
- 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 +256 -7
- 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-BKkg93rL.d.cts → schema-B9A1zLRk.d.cts} +482 -2
- package/dist/{schema-BKkg93rL.d.ts → schema-B9A1zLRk.d.ts} +482 -2
- package/dist/testing.cjs +217 -4
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +59 -2
- package/dist/testing.d.ts +59 -2
- package/dist/testing.js +70 -2
- package/dist/testing.js.map +1 -1
- package/dist/{types-CsQ8tON_.d.ts → types-DjGAwJxC.d.ts} +1 -1
- package/dist/{types-7cC17GWp.d.cts → types-DpbzzZO2.d.cts} +1 -1
- package/dist/usagetap/index.d.cts +2 -2
- package/dist/usagetap/index.d.ts +2 -2
- package/package.json +2 -2
- package/dist/chunk-4LEPCCSA.js.map +0 -1
- package/dist/chunk-AN3OR7YC.js.map +0 -1
- package/dist/chunk-LMURMZUA.js.map +0 -1
- package/dist/chunk-O6RQZYS4.js.map +0 -1
- package/dist/chunk-V7QY7MRM.js.map +0 -1
- package/dist/chunk-YAD6COQX.js +0 -26
- package/dist/chunk-YAD6COQX.js.map +0 -1
- package/dist/chunk-YNIAA4ZJ.js.map +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
import { readFileSync as readFileSync2 } from "fs";
|
|
5
5
|
|
|
6
6
|
// src/cli/commands/validate.ts
|
|
7
|
-
import { readdir } from "fs/promises";
|
|
8
|
-
import { join as
|
|
7
|
+
import { readdir, stat } from "fs/promises";
|
|
8
|
+
import { join as join3, extname } from "path";
|
|
9
9
|
|
|
10
10
|
// src/parser/parser.ts
|
|
11
11
|
import matter from "gray-matter";
|
|
@@ -42,23 +42,42 @@ var ResponseSchema = z.object({
|
|
|
42
42
|
stream: z.boolean().optional(),
|
|
43
43
|
schema: z.record(z.unknown()).optional(),
|
|
44
44
|
schema_name: z.string().optional(),
|
|
45
|
+
schema_description: z.string().optional(),
|
|
45
46
|
schema_strict: z.boolean().optional()
|
|
46
47
|
});
|
|
47
48
|
var AnthropicProviderOptionsSchema = z.object({
|
|
48
49
|
top_k: z.number().int().min(0).optional(),
|
|
49
|
-
tool_choice: z.record(z.unknown()).optional()
|
|
50
|
+
tool_choice: z.record(z.unknown()).optional(),
|
|
51
|
+
output_config: z.record(z.unknown()).optional()
|
|
50
52
|
});
|
|
51
53
|
var GeminiProviderOptionsSchema = z.object({
|
|
52
54
|
candidate_count: z.number().int().positive().optional(),
|
|
53
55
|
top_k: z.number().int().min(0).optional(),
|
|
54
56
|
seed: z.number().int().optional(),
|
|
55
57
|
response_schema: z.record(z.unknown()).optional(),
|
|
58
|
+
response_json_schema: z.record(z.unknown()).optional(),
|
|
56
59
|
response_modalities: z.array(z.string()).optional(),
|
|
57
60
|
thinking_budget_tokens: z.number().int().positive().optional()
|
|
58
61
|
});
|
|
62
|
+
var OpenRouterProviderOptionsSchema = z.object({
|
|
63
|
+
provider: z.record(z.unknown()).optional(),
|
|
64
|
+
transforms: z.array(z.string()).optional(),
|
|
65
|
+
plugins: z.array(z.record(z.unknown())).optional(),
|
|
66
|
+
models: z.array(z.string()).optional()
|
|
67
|
+
});
|
|
59
68
|
var ProviderOptionsSchema = z.object({
|
|
60
69
|
anthropic: AnthropicProviderOptionsSchema.optional(),
|
|
61
|
-
gemini: GeminiProviderOptionsSchema.optional()
|
|
70
|
+
gemini: GeminiProviderOptionsSchema.optional(),
|
|
71
|
+
openrouter: OpenRouterProviderOptionsSchema.optional()
|
|
72
|
+
});
|
|
73
|
+
var RawProviderBodySchema = z.object({
|
|
74
|
+
openai: z.record(z.unknown()).optional(),
|
|
75
|
+
"openai-responses": z.record(z.unknown()).optional(),
|
|
76
|
+
openai_responses: z.record(z.unknown()).optional(),
|
|
77
|
+
anthropic: z.record(z.unknown()).optional(),
|
|
78
|
+
gemini: z.record(z.unknown()).optional(),
|
|
79
|
+
google: z.record(z.unknown()).optional(),
|
|
80
|
+
openrouter: z.record(z.unknown()).optional()
|
|
62
81
|
});
|
|
63
82
|
var OpenAICacheSchema = z.object({
|
|
64
83
|
prompt_cache_key: z.string().min(1).optional(),
|
|
@@ -131,6 +150,7 @@ var PromptAssetOverridesSchema = z.object({
|
|
|
131
150
|
sampling: SamplingSchema.optional(),
|
|
132
151
|
response: ResponseSchema.optional(),
|
|
133
152
|
cache: CacheSchema.optional(),
|
|
153
|
+
raw: RawProviderBodySchema.optional(),
|
|
134
154
|
tools: z.array(ToolRefSchema).optional(),
|
|
135
155
|
provider_options: ProviderOptionsSchema.optional()
|
|
136
156
|
});
|
|
@@ -163,6 +183,7 @@ var PromptAssetSchema = z.object({
|
|
|
163
183
|
sampling: SamplingSchema.optional(),
|
|
164
184
|
response: ResponseSchema.optional(),
|
|
165
185
|
cache: CacheSchema.optional(),
|
|
186
|
+
raw: RawProviderBodySchema.optional(),
|
|
166
187
|
tools: z.array(ToolRefSchema).optional(),
|
|
167
188
|
provider_options: ProviderOptionsSchema.optional(),
|
|
168
189
|
mcp: MCPSchema.optional(),
|
|
@@ -176,6 +197,123 @@ var PromptAssetSchema = z.object({
|
|
|
176
197
|
source: SourceSchema.optional()
|
|
177
198
|
});
|
|
178
199
|
|
|
200
|
+
// src/parser/frontmatter-guard.ts
|
|
201
|
+
var FRONT_MATTER_DELIMITER = /^---[ \t]*$/;
|
|
202
|
+
var REGEX_FIELD = /^(?<indent>\s*)(?<field>allow_regex|deny_regex)\s*:\s*(?<value>.*)$/;
|
|
203
|
+
var PATTERN_FIELD = /^(?<indent>\s*)pattern\s*:\s*(?<value>.*)$/;
|
|
204
|
+
var INLINE_PATTERN_FIELD = /(?:^|[{,]\s*)pattern\s*:\s*(?<value>"(?:\\.|[^"\\])*")/;
|
|
205
|
+
function assertRegexFrontMatterQuoting(content, filePath) {
|
|
206
|
+
const frontMatter = extractFrontMatter(content);
|
|
207
|
+
if (!frontMatter) {
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
let pendingRegex;
|
|
211
|
+
for (const [index, line] of frontMatter.lines.entries()) {
|
|
212
|
+
const lineNumber = frontMatter.startLine + index;
|
|
213
|
+
const regexMatch = line.match(REGEX_FIELD);
|
|
214
|
+
if (regexMatch?.groups) {
|
|
215
|
+
const field = regexMatch.groups.field;
|
|
216
|
+
const indent2 = regexMatch.groups.indent.length;
|
|
217
|
+
const value = regexMatch.groups.value.trim();
|
|
218
|
+
pendingRegex = value === "" ? { field, indent: indent2 } : void 0;
|
|
219
|
+
assertNoUnescapedBackslashInDoubleQuotedRegex(value, field, filePath, lineNumber);
|
|
220
|
+
assertNoUnescapedBackslashInInlinePattern(value, field, filePath, lineNumber);
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
if (!pendingRegex) {
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
if (line.trim() === "" || line.trimStart().startsWith("#")) {
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
const indent = line.match(/^\s*/)?.[0].length ?? 0;
|
|
230
|
+
if (indent <= pendingRegex.indent) {
|
|
231
|
+
pendingRegex = void 0;
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
const patternMatch = line.match(PATTERN_FIELD);
|
|
235
|
+
if (!patternMatch?.groups) {
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
assertNoUnescapedBackslashInDoubleQuotedRegex(
|
|
239
|
+
patternMatch.groups.value.trim(),
|
|
240
|
+
`${pendingRegex.field}.pattern`,
|
|
241
|
+
filePath,
|
|
242
|
+
lineNumber
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
function assertNoUnescapedBackslashInInlinePattern(value, field, filePath, lineNumber) {
|
|
247
|
+
const match = value.match(INLINE_PATTERN_FIELD);
|
|
248
|
+
const pattern = match?.groups?.value;
|
|
249
|
+
if (!pattern) {
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
assertNoUnescapedBackslashInDoubleQuotedRegex(pattern, `${field}.pattern`, filePath, lineNumber);
|
|
253
|
+
}
|
|
254
|
+
function extractFrontMatter(content) {
|
|
255
|
+
const lines = content.split(/\r?\n/);
|
|
256
|
+
if (!FRONT_MATTER_DELIMITER.test(lines[0] ?? "")) {
|
|
257
|
+
return void 0;
|
|
258
|
+
}
|
|
259
|
+
for (let index = 1; index < lines.length; index += 1) {
|
|
260
|
+
if (FRONT_MATTER_DELIMITER.test(lines[index] ?? "")) {
|
|
261
|
+
return {
|
|
262
|
+
lines: lines.slice(1, index),
|
|
263
|
+
startLine: 2
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
return void 0;
|
|
268
|
+
}
|
|
269
|
+
function assertNoUnescapedBackslashInDoubleQuotedRegex(value, field, filePath, lineNumber) {
|
|
270
|
+
if (!value.startsWith('"')) {
|
|
271
|
+
return;
|
|
272
|
+
}
|
|
273
|
+
const quoted = readDoubleQuotedScalar(value);
|
|
274
|
+
if (!quoted) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
if (!hasUnescapedBackslash(quoted)) {
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
const location = filePath ? `${filePath}:${lineNumber}` : `line ${lineNumber}`;
|
|
281
|
+
throw new Error(
|
|
282
|
+
`POK013: Invalid context regex YAML at ${location}, field "${field}": double-quoted regex strings treat backslashes as YAML escapes. Use unquoted /pattern/i literal form, single quotes, or double each backslash.`
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
function readDoubleQuotedScalar(value) {
|
|
286
|
+
let result = "";
|
|
287
|
+
for (let index = 1; index < value.length; index += 1) {
|
|
288
|
+
const char = value[index];
|
|
289
|
+
if (char === '"') {
|
|
290
|
+
return result;
|
|
291
|
+
}
|
|
292
|
+
result += char;
|
|
293
|
+
if (char === "\\" && index + 1 < value.length) {
|
|
294
|
+
index += 1;
|
|
295
|
+
result += value[index];
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return void 0;
|
|
299
|
+
}
|
|
300
|
+
function hasUnescapedBackslash(value) {
|
|
301
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
302
|
+
if (value[index] !== "\\") {
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
let count = 1;
|
|
306
|
+
while (value[index + count] === "\\") {
|
|
307
|
+
count += 1;
|
|
308
|
+
}
|
|
309
|
+
if (count % 2 === 1) {
|
|
310
|
+
return true;
|
|
311
|
+
}
|
|
312
|
+
index += count - 1;
|
|
313
|
+
}
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
|
|
179
317
|
// src/parser/sections.ts
|
|
180
318
|
var SECTION_MAP = {
|
|
181
319
|
"system instructions": "system_instructions",
|
|
@@ -216,6 +354,7 @@ function extractSections(body) {
|
|
|
216
354
|
|
|
217
355
|
// src/parser/parser.ts
|
|
218
356
|
function parsePrompt(content, filePath) {
|
|
357
|
+
assertRegexFrontMatterQuoting(content, filePath);
|
|
219
358
|
const { data: frontMatter, content: body } = matter(content);
|
|
220
359
|
const sections = extractSections(body);
|
|
221
360
|
const raw = {
|
|
@@ -293,6 +432,7 @@ function mergeDefaults(base, local) {
|
|
|
293
432
|
return {
|
|
294
433
|
provider: local.provider ?? base.provider,
|
|
295
434
|
model: local.model ?? base.model,
|
|
435
|
+
cache: mergeCache(base.cache, local.cache),
|
|
296
436
|
metadata: {
|
|
297
437
|
...base.metadata ?? {},
|
|
298
438
|
...local.metadata ?? {}
|
|
@@ -304,10 +444,12 @@ function mergeDefaults(base, local) {
|
|
|
304
444
|
};
|
|
305
445
|
}
|
|
306
446
|
function applyDefaults(asset, defaults) {
|
|
447
|
+
const cache = mergeCache(defaults.cache, asset.cache);
|
|
307
448
|
const hasDefaultMetadata = defaults.metadata && Object.keys(defaults.metadata).length > 0;
|
|
308
449
|
const hasDefaultSystem = !!defaults.sections?.system_instructions;
|
|
309
450
|
const hasDefaultScalars = defaults.provider !== void 0 || defaults.model !== void 0;
|
|
310
|
-
|
|
451
|
+
const hasDefaultCache = cache !== void 0;
|
|
452
|
+
if (!hasDefaultMetadata && !hasDefaultSystem && !hasDefaultScalars && !hasDefaultCache) {
|
|
311
453
|
return asset;
|
|
312
454
|
}
|
|
313
455
|
const mergedMetadata = {
|
|
@@ -321,10 +463,38 @@ function applyDefaults(asset, defaults) {
|
|
|
321
463
|
...asset,
|
|
322
464
|
provider: asset.provider ?? defaults.provider,
|
|
323
465
|
model: asset.model ?? defaults.model,
|
|
466
|
+
cache,
|
|
324
467
|
metadata,
|
|
325
468
|
sections
|
|
326
469
|
};
|
|
327
470
|
}
|
|
471
|
+
function mergeCache(base, local) {
|
|
472
|
+
const merged = {
|
|
473
|
+
...base ?? {},
|
|
474
|
+
...local ?? {},
|
|
475
|
+
openai: {
|
|
476
|
+
...base?.openai ?? {},
|
|
477
|
+
...local?.openai ?? {}
|
|
478
|
+
},
|
|
479
|
+
anthropic: {
|
|
480
|
+
...base?.anthropic ?? {},
|
|
481
|
+
...local?.anthropic ?? {}
|
|
482
|
+
},
|
|
483
|
+
gemini: {
|
|
484
|
+
...base?.gemini ?? {},
|
|
485
|
+
...local?.gemini ?? {}
|
|
486
|
+
},
|
|
487
|
+
google: {
|
|
488
|
+
...base?.google ?? {},
|
|
489
|
+
...local?.google ?? {}
|
|
490
|
+
}
|
|
491
|
+
};
|
|
492
|
+
if (merged.openai && Object.keys(merged.openai).length === 0) delete merged.openai;
|
|
493
|
+
if (merged.anthropic && Object.keys(merged.anthropic).length === 0) delete merged.anthropic;
|
|
494
|
+
if (merged.gemini && Object.keys(merged.gemini).length === 0) delete merged.gemini;
|
|
495
|
+
if (merged.google && Object.keys(merged.google).length === 0) delete merged.google;
|
|
496
|
+
return Object.keys(merged).length > 0 ? merged : void 0;
|
|
497
|
+
}
|
|
328
498
|
|
|
329
499
|
// src/renderer/interpolate.ts
|
|
330
500
|
var VARIABLE_RE = /\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}/g;
|
|
@@ -542,8 +712,21 @@ var KNOWN_FRONT_MATTER_KEYS = /* @__PURE__ */ new Set([
|
|
|
542
712
|
"environments",
|
|
543
713
|
"tiers",
|
|
544
714
|
"metadata",
|
|
545
|
-
"cache"
|
|
715
|
+
"cache",
|
|
716
|
+
"provider_options",
|
|
717
|
+
"raw"
|
|
546
718
|
]);
|
|
719
|
+
var RISKY_UNBOUNDED_INPUT_NAMES = [
|
|
720
|
+
"message",
|
|
721
|
+
"prompt",
|
|
722
|
+
"history",
|
|
723
|
+
"transcript",
|
|
724
|
+
"document",
|
|
725
|
+
"content",
|
|
726
|
+
"input",
|
|
727
|
+
"body",
|
|
728
|
+
"context"
|
|
729
|
+
];
|
|
547
730
|
function validateAsset(asset, frontMatterKeys, filePath) {
|
|
548
731
|
const errors = [];
|
|
549
732
|
const warnings = [];
|
|
@@ -614,7 +797,32 @@ function validateAsset(asset, frontMatterKeys, filePath) {
|
|
|
614
797
|
});
|
|
615
798
|
}
|
|
616
799
|
}
|
|
800
|
+
if (usedVars.size > 0 && (!asset.context?.inputs || asset.context.inputs.length === 0)) {
|
|
801
|
+
warnings.push({
|
|
802
|
+
code: "POK046",
|
|
803
|
+
message: `Template uses ${usedVars.size === 1 ? "a variable" : "variables"} but context.inputs is not declared.`,
|
|
804
|
+
filePath,
|
|
805
|
+
suggestion: "Declare context.inputs to enable input policy validation."
|
|
806
|
+
});
|
|
807
|
+
}
|
|
617
808
|
for (const input of getContextInputs(asset)) {
|
|
809
|
+
const lowerName = input.name.toLowerCase();
|
|
810
|
+
if (input.max_size === void 0 && RISKY_UNBOUNDED_INPUT_NAMES.some((needle) => lowerName.includes(needle))) {
|
|
811
|
+
warnings.push({
|
|
812
|
+
code: "POK040",
|
|
813
|
+
message: `Context input "${input.name}" has no max_size and appears unbounded.`,
|
|
814
|
+
filePath,
|
|
815
|
+
suggestion: "Add max_size to constrain prompt payload growth."
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
if (input.allow_regex === void 0 && input.deny_regex === void 0 && input.non_empty === void 0 && input.reject_secrets === void 0) {
|
|
819
|
+
warnings.push({
|
|
820
|
+
code: "POK041",
|
|
821
|
+
message: `Context input "${input.name}" has no input hardening validators.`,
|
|
822
|
+
filePath,
|
|
823
|
+
suggestion: "Consider non_empty/reject_secrets and allow/deny regex validators."
|
|
824
|
+
});
|
|
825
|
+
}
|
|
618
826
|
if (input.trim !== void 0 && input.trim !== false && input.max_size === void 0) {
|
|
619
827
|
warnings.push({
|
|
620
828
|
code: "POK014",
|
|
@@ -642,6 +850,91 @@ function validateAsset(asset, frontMatterKeys, filePath) {
|
|
|
642
850
|
}
|
|
643
851
|
}
|
|
644
852
|
}
|
|
853
|
+
if (asset.provider) {
|
|
854
|
+
let providerCache;
|
|
855
|
+
let cacheSuggestionField;
|
|
856
|
+
switch (asset.provider) {
|
|
857
|
+
case "openai":
|
|
858
|
+
providerCache = asset.cache?.openai;
|
|
859
|
+
cacheSuggestionField = "cache.openai";
|
|
860
|
+
break;
|
|
861
|
+
case "anthropic":
|
|
862
|
+
providerCache = asset.cache?.anthropic;
|
|
863
|
+
cacheSuggestionField = "cache.anthropic";
|
|
864
|
+
break;
|
|
865
|
+
case "gemini":
|
|
866
|
+
case "google":
|
|
867
|
+
providerCache = asset.cache?.gemini ?? asset.cache?.google;
|
|
868
|
+
cacheSuggestionField = "cache.gemini";
|
|
869
|
+
break;
|
|
870
|
+
default:
|
|
871
|
+
break;
|
|
872
|
+
}
|
|
873
|
+
if (cacheSuggestionField && providerCache === void 0) {
|
|
874
|
+
warnings.push({
|
|
875
|
+
code: "POK042",
|
|
876
|
+
message: `Provider "${asset.provider}" has no provider-specific cache settings.`,
|
|
877
|
+
filePath,
|
|
878
|
+
suggestion: `Consider configuring ${cacheSuggestionField} for better cache-hit behavior.`
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
if (!asset.model) {
|
|
882
|
+
warnings.push({
|
|
883
|
+
code: "POK044",
|
|
884
|
+
message: `Provider "${asset.provider}" is configured without a model.`,
|
|
885
|
+
filePath,
|
|
886
|
+
suggestion: "Set model in prompt or defaults to avoid adapter-time errors."
|
|
887
|
+
});
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
if (asset.cache?.gemini?.cached_content && asset.cache.google?.cached_content && asset.cache.gemini.cached_content !== asset.cache.google.cached_content) {
|
|
891
|
+
warnings.push({
|
|
892
|
+
code: "POK043",
|
|
893
|
+
message: "cache.gemini.cached_content and cache.google.cached_content are both set to different values.",
|
|
894
|
+
filePath,
|
|
895
|
+
suggestion: "Use one canonical value; Gemini prefers cache.gemini.cached_content."
|
|
896
|
+
});
|
|
897
|
+
}
|
|
898
|
+
for (const [envName, overrides] of Object.entries(asset.environments ?? {})) {
|
|
899
|
+
if (asset.cache && (!isRecord(overrides) || !overrides.cache)) {
|
|
900
|
+
warnings.push({
|
|
901
|
+
code: "POK045",
|
|
902
|
+
message: `Environment "${envName}" does not override cache while prompt-level cache is defined.`,
|
|
903
|
+
filePath,
|
|
904
|
+
suggestion: "Confirm cache strategy is intentionally shared across environments."
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
for (const [tierName, overrides] of Object.entries(asset.tiers ?? {})) {
|
|
909
|
+
if (asset.cache && (!isRecord(overrides) || !overrides.cache)) {
|
|
910
|
+
warnings.push({
|
|
911
|
+
code: "POK045",
|
|
912
|
+
message: `Tier "${tierName}" does not override cache while prompt-level cache is defined.`,
|
|
913
|
+
filePath,
|
|
914
|
+
suggestion: "Confirm cache strategy is intentionally shared across tiers."
|
|
915
|
+
});
|
|
916
|
+
}
|
|
917
|
+
}
|
|
918
|
+
for (const tool of asset.tools ?? []) {
|
|
919
|
+
if (isRecord(tool)) {
|
|
920
|
+
if (!tool.description) {
|
|
921
|
+
warnings.push({
|
|
922
|
+
code: "POK047",
|
|
923
|
+
message: `Inline tool "${tool.name}" is missing a description.`,
|
|
924
|
+
filePath,
|
|
925
|
+
suggestion: "Add description to improve model tool-selection quality."
|
|
926
|
+
});
|
|
927
|
+
}
|
|
928
|
+
if (!tool.input_schema) {
|
|
929
|
+
warnings.push({
|
|
930
|
+
code: "POK047",
|
|
931
|
+
message: `Inline tool "${tool.name}" is missing input_schema.`,
|
|
932
|
+
filePath,
|
|
933
|
+
suggestion: "Add input_schema so tool inputs are strongly typed."
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
}
|
|
645
938
|
return {
|
|
646
939
|
valid: errors.length === 0,
|
|
647
940
|
errors,
|
|
@@ -678,6 +971,9 @@ function findClosestMatch(input, candidates) {
|
|
|
678
971
|
}
|
|
679
972
|
return best;
|
|
680
973
|
}
|
|
974
|
+
function isRecord(value) {
|
|
975
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
976
|
+
}
|
|
681
977
|
|
|
682
978
|
// src/prompt-resolution.ts
|
|
683
979
|
import { readFile as readFile3 } from "fs/promises";
|
|
@@ -696,8 +992,8 @@ var PromptCache = class {
|
|
|
696
992
|
const entry = this.cache.get(filePath);
|
|
697
993
|
if (!entry) return void 0;
|
|
698
994
|
try {
|
|
699
|
-
const
|
|
700
|
-
if (
|
|
995
|
+
const stat2 = statSync(filePath);
|
|
996
|
+
if (stat2.mtimeMs !== entry.mtime) {
|
|
701
997
|
this.cache.delete(filePath);
|
|
702
998
|
return void 0;
|
|
703
999
|
}
|
|
@@ -711,14 +1007,14 @@ var PromptCache = class {
|
|
|
711
1007
|
}
|
|
712
1008
|
set(filePath, value) {
|
|
713
1009
|
try {
|
|
714
|
-
const
|
|
1010
|
+
const stat2 = statSync(filePath);
|
|
715
1011
|
if (this.cache.has(filePath)) {
|
|
716
1012
|
this.cache.delete(filePath);
|
|
717
1013
|
} else if (this.cache.size >= this.maxSize) {
|
|
718
1014
|
const oldest = this.cache.keys().next().value;
|
|
719
1015
|
if (oldest) this.cache.delete(oldest);
|
|
720
1016
|
}
|
|
721
|
-
this.cache.set(filePath, { value, mtime:
|
|
1017
|
+
this.cache.set(filePath, { value, mtime: stat2.mtimeMs });
|
|
722
1018
|
} catch {
|
|
723
1019
|
}
|
|
724
1020
|
}
|
|
@@ -758,6 +1054,29 @@ function mergeOverride(base, override) {
|
|
|
758
1054
|
if (override.response !== void 0) {
|
|
759
1055
|
result.response = { ...result.response, ...override.response };
|
|
760
1056
|
}
|
|
1057
|
+
if (override.cache !== void 0) {
|
|
1058
|
+
result.cache = {
|
|
1059
|
+
...result.cache,
|
|
1060
|
+
...override.cache,
|
|
1061
|
+
openai: mergeRecordBlock(result.cache?.openai, override.cache.openai),
|
|
1062
|
+
anthropic: mergeRecordBlock(result.cache?.anthropic, override.cache.anthropic),
|
|
1063
|
+
gemini: mergeRecordBlock(result.cache?.gemini, override.cache.gemini),
|
|
1064
|
+
google: mergeRecordBlock(result.cache?.google, override.cache.google)
|
|
1065
|
+
};
|
|
1066
|
+
}
|
|
1067
|
+
if (override.raw !== void 0) {
|
|
1068
|
+
result.raw = {
|
|
1069
|
+
...result.raw,
|
|
1070
|
+
...override.raw,
|
|
1071
|
+
openai: mergeRecordBlock(result.raw?.openai, override.raw.openai),
|
|
1072
|
+
"openai-responses": mergeRecordBlock(result.raw?.["openai-responses"], override.raw["openai-responses"]),
|
|
1073
|
+
openai_responses: mergeRecordBlock(result.raw?.openai_responses, override.raw.openai_responses),
|
|
1074
|
+
anthropic: mergeRecordBlock(result.raw?.anthropic, override.raw.anthropic),
|
|
1075
|
+
gemini: mergeRecordBlock(result.raw?.gemini, override.raw.gemini),
|
|
1076
|
+
google: mergeRecordBlock(result.raw?.google, override.raw.google),
|
|
1077
|
+
openrouter: mergeRecordBlock(result.raw?.openrouter, override.raw.openrouter)
|
|
1078
|
+
};
|
|
1079
|
+
}
|
|
761
1080
|
if (override.provider_options !== void 0) {
|
|
762
1081
|
result.provider_options = {
|
|
763
1082
|
...result.provider_options,
|
|
@@ -769,11 +1088,19 @@ function mergeOverride(base, override) {
|
|
|
769
1088
|
gemini: {
|
|
770
1089
|
...result.provider_options?.gemini,
|
|
771
1090
|
...override.provider_options.gemini
|
|
1091
|
+
},
|
|
1092
|
+
openrouter: {
|
|
1093
|
+
...result.provider_options?.openrouter,
|
|
1094
|
+
...override.provider_options.openrouter
|
|
772
1095
|
}
|
|
773
1096
|
};
|
|
774
1097
|
}
|
|
775
1098
|
return result;
|
|
776
1099
|
}
|
|
1100
|
+
function mergeRecordBlock(base, override) {
|
|
1101
|
+
if (override === void 0) return base;
|
|
1102
|
+
return { ...base, ...override };
|
|
1103
|
+
}
|
|
777
1104
|
|
|
778
1105
|
// src/prompt-resolution.ts
|
|
779
1106
|
var DEFAULT_PROMPTS_DIR = "./prompts";
|
|
@@ -784,11 +1111,33 @@ function defaultCompiledDirForFormat(format) {
|
|
|
784
1111
|
}
|
|
785
1112
|
var sharedPromptCache = new PromptCache();
|
|
786
1113
|
|
|
1114
|
+
// src/cli/commands/defaults-root.ts
|
|
1115
|
+
import { existsSync as existsSync2 } from "fs";
|
|
1116
|
+
import { dirname as dirname3, join as join2, resolve as resolve4 } from "path";
|
|
1117
|
+
function findDefaultsRoot(filePath) {
|
|
1118
|
+
let current = resolve4(dirname3(filePath));
|
|
1119
|
+
let root = current;
|
|
1120
|
+
let foundDefaults = false;
|
|
1121
|
+
while (true) {
|
|
1122
|
+
if (existsSync2(join2(current, "defaults.md"))) {
|
|
1123
|
+
root = current;
|
|
1124
|
+
foundDefaults = true;
|
|
1125
|
+
} else if (foundDefaults) {
|
|
1126
|
+
return root;
|
|
1127
|
+
}
|
|
1128
|
+
const parent = dirname3(current);
|
|
1129
|
+
if (parent === current) {
|
|
1130
|
+
return root;
|
|
1131
|
+
}
|
|
1132
|
+
current = parent;
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
|
|
787
1136
|
// src/cli/commands/validate.ts
|
|
788
1137
|
var HELP = `
|
|
789
1138
|
promptopskit validate [sourceDir] [options]
|
|
790
1139
|
|
|
791
|
-
Validate
|
|
1140
|
+
Validate prompt .md files in a directory or a single prompt file.
|
|
792
1141
|
|
|
793
1142
|
Options:
|
|
794
1143
|
--source, -s Source directory (default: ./prompts)
|
|
@@ -801,18 +1150,19 @@ async function validate(args) {
|
|
|
801
1150
|
return;
|
|
802
1151
|
}
|
|
803
1152
|
const positional = getPositionalArgs(args, /* @__PURE__ */ new Set(["--source", "-s"]));
|
|
804
|
-
const
|
|
1153
|
+
const target = getFlag(args, "--source", "-s") ?? positional[0] ?? DEFAULT_PROMPTS_DIR;
|
|
805
1154
|
const strict = args.includes("--strict");
|
|
806
|
-
const files = await collectPromptFiles(
|
|
1155
|
+
const files = await collectPromptFiles(target);
|
|
807
1156
|
if (files.length === 0) {
|
|
808
|
-
console.log(`No .md prompt files found in ${
|
|
1157
|
+
console.log(`No .md prompt files found in ${target}`);
|
|
809
1158
|
return;
|
|
810
1159
|
}
|
|
811
1160
|
let errorCount = 0;
|
|
812
1161
|
let warnCount = 0;
|
|
813
1162
|
for (const file of files) {
|
|
814
1163
|
try {
|
|
815
|
-
const
|
|
1164
|
+
const defaultsRoot = files.length === 1 && file === target ? findDefaultsRoot(file) : target;
|
|
1165
|
+
const { asset, raw } = await loadPromptFile(file, { defaultsRoot });
|
|
816
1166
|
const result = await validateAssetWithIncludes(asset, file, Object.keys(raw.frontMatter));
|
|
817
1167
|
if (result.errors.length > 0) {
|
|
818
1168
|
errorCount += result.errors.length;
|
|
@@ -867,12 +1217,19 @@ function getFlag(args, ...flags) {
|
|
|
867
1217
|
}
|
|
868
1218
|
return void 0;
|
|
869
1219
|
}
|
|
870
|
-
async function collectPromptFiles(
|
|
1220
|
+
async function collectPromptFiles(target) {
|
|
1221
|
+
const targetStats = await stat(target);
|
|
1222
|
+
if (targetStats.isFile()) {
|
|
1223
|
+
if (extname(target) !== ".md" || target.endsWith("defaults.md") || target.endsWith(".test.md")) {
|
|
1224
|
+
return [];
|
|
1225
|
+
}
|
|
1226
|
+
return [target];
|
|
1227
|
+
}
|
|
871
1228
|
const results = [];
|
|
872
|
-
const entries = await readdir(
|
|
1229
|
+
const entries = await readdir(target, { withFileTypes: true, recursive: true });
|
|
873
1230
|
for (const entry of entries) {
|
|
874
1231
|
if (entry.isFile() && extname(entry.name) === ".md" && !entry.name.endsWith(".test.md") && entry.name !== "defaults.md") {
|
|
875
|
-
results.push(
|
|
1232
|
+
results.push(join3(entry.parentPath ?? target, entry.name));
|
|
876
1233
|
}
|
|
877
1234
|
}
|
|
878
1235
|
return results.sort();
|
|
@@ -880,7 +1237,7 @@ async function collectPromptFiles(dir) {
|
|
|
880
1237
|
|
|
881
1238
|
// src/cli/commands/compile.ts
|
|
882
1239
|
import { readdir as readdir2, writeFile, mkdir, rm } from "fs/promises";
|
|
883
|
-
import { join as
|
|
1240
|
+
import { join as join4, extname as extname2, relative, dirname as dirname5 } from "path";
|
|
884
1241
|
var HELP2 = `
|
|
885
1242
|
promptopskit compile [sourceDir] [outputDir] [options]
|
|
886
1243
|
|
|
@@ -922,7 +1279,7 @@ async function compile(args) {
|
|
|
922
1279
|
for (const file of files) {
|
|
923
1280
|
const rel = relative(sourceDir, file).replace(/\.md$/, "");
|
|
924
1281
|
const outExt = format === "esm" ? ".mjs" : ".json";
|
|
925
|
-
const outPath =
|
|
1282
|
+
const outPath = join4(outputDir, rel + outExt);
|
|
926
1283
|
try {
|
|
927
1284
|
const { asset: parsed, raw } = await loadPromptFile(file, { defaultsRoot: sourceDir });
|
|
928
1285
|
const validation = await validateAssetWithIncludes(parsed, file, Object.keys(raw.frontMatter));
|
|
@@ -933,7 +1290,7 @@ async function compile(args) {
|
|
|
933
1290
|
if (dryRun) {
|
|
934
1291
|
console.log(` Would create: ${outPath}`);
|
|
935
1292
|
} else {
|
|
936
|
-
await mkdir(
|
|
1293
|
+
await mkdir(dirname5(outPath), { recursive: true });
|
|
937
1294
|
if (format === "esm") {
|
|
938
1295
|
const esmContent = `export default ${JSON.stringify(asset, null, 2)};
|
|
939
1296
|
`;
|
|
@@ -990,7 +1347,7 @@ async function collectPromptFiles2(dir) {
|
|
|
990
1347
|
const entries = await readdir2(dir, { withFileTypes: true, recursive: true });
|
|
991
1348
|
for (const entry of entries) {
|
|
992
1349
|
if (entry.isFile() && extname2(entry.name) === ".md" && !entry.name.endsWith(".test.md") && entry.name !== "defaults.md") {
|
|
993
|
-
results.push(
|
|
1350
|
+
results.push(join4(entry.parentPath ?? dir, entry.name));
|
|
994
1351
|
}
|
|
995
1352
|
}
|
|
996
1353
|
return results.sort();
|
|
@@ -999,30 +1356,6 @@ async function collectPromptFiles2(dir) {
|
|
|
999
1356
|
// src/cli/commands/render.ts
|
|
1000
1357
|
import { readFile as readFile4 } from "fs/promises";
|
|
1001
1358
|
import { existsSync as existsSync3 } from "fs";
|
|
1002
|
-
|
|
1003
|
-
// src/cli/commands/defaults-root.ts
|
|
1004
|
-
import { existsSync as existsSync2 } from "fs";
|
|
1005
|
-
import { dirname as dirname4, join as join4, resolve as resolve4 } from "path";
|
|
1006
|
-
function findDefaultsRoot(filePath) {
|
|
1007
|
-
let current = resolve4(dirname4(filePath));
|
|
1008
|
-
let root = current;
|
|
1009
|
-
let foundDefaults = false;
|
|
1010
|
-
while (true) {
|
|
1011
|
-
if (existsSync2(join4(current, "defaults.md"))) {
|
|
1012
|
-
root = current;
|
|
1013
|
-
foundDefaults = true;
|
|
1014
|
-
} else if (foundDefaults) {
|
|
1015
|
-
return root;
|
|
1016
|
-
}
|
|
1017
|
-
const parent = dirname4(current);
|
|
1018
|
-
if (parent === current) {
|
|
1019
|
-
return root;
|
|
1020
|
-
}
|
|
1021
|
-
current = parent;
|
|
1022
|
-
}
|
|
1023
|
-
}
|
|
1024
|
-
|
|
1025
|
-
// src/cli/commands/render.ts
|
|
1026
1359
|
var HELP3 = `
|
|
1027
1360
|
promptopskit render <file> [options]
|
|
1028
1361
|
|
|
@@ -1157,7 +1490,7 @@ async function inspect(args) {
|
|
|
1157
1490
|
|
|
1158
1491
|
// src/cli/commands/init.ts
|
|
1159
1492
|
import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
1160
|
-
import { join as join5, dirname as
|
|
1493
|
+
import { join as join5, dirname as dirname6, relative as relative2 } from "path";
|
|
1161
1494
|
import { existsSync as existsSync4, readFileSync } from "fs";
|
|
1162
1495
|
var HELP5 = `
|
|
1163
1496
|
promptopskit init [dir]
|
|
@@ -1229,10 +1562,14 @@ var TEST_SIDECAR = `cases:
|
|
|
1229
1562
|
variables:
|
|
1230
1563
|
name: "World"
|
|
1231
1564
|
app_context: "Welcome screen"
|
|
1565
|
+
response:
|
|
1566
|
+
message: "Hello, World! How can I help you today?"
|
|
1232
1567
|
- name: named-greeting
|
|
1233
1568
|
variables:
|
|
1234
1569
|
name: "Alice"
|
|
1235
1570
|
app_context: "Settings page"
|
|
1571
|
+
response:
|
|
1572
|
+
message: "Hello, Alice! How can I help you today?"
|
|
1236
1573
|
`;
|
|
1237
1574
|
var EXAMPLE_USAGE = `// Example: render the hello prompt and send it to OpenAI
|
|
1238
1575
|
// Full docs: https://promptopskit.com/docs/index.html#/
|
|
@@ -1304,12 +1641,16 @@ async function init(args) {
|
|
|
1304
1641
|
return;
|
|
1305
1642
|
}
|
|
1306
1643
|
const dir = args.find((a) => !a.startsWith("--")) ?? "./prompts";
|
|
1644
|
+
const testFilePath = join5(dirname6(dir), "tests", "hello.prompt.test.mjs");
|
|
1645
|
+
const promptsDirFromTest = relative2(dirname6(testFilePath), dir) || ".";
|
|
1646
|
+
const helloPromptTest = createHelloPromptTest(promptsDirFromTest);
|
|
1307
1647
|
const files = [
|
|
1308
1648
|
{ path: join5(dir, "defaults.md"), content: DEFAULTS },
|
|
1309
1649
|
{ path: join5(dir, "hello.md"), content: HELLO_PROMPT },
|
|
1310
1650
|
{ path: join5(dir, "hello.test.yaml"), content: TEST_SIDECAR },
|
|
1311
1651
|
{ path: join5(dir, "shared", "tone.md"), content: TONE_INCLUDE },
|
|
1312
|
-
{ path: join5(dir, "example-usage.ts"), content: EXAMPLE_USAGE }
|
|
1652
|
+
{ path: join5(dir, "example-usage.ts"), content: EXAMPLE_USAGE },
|
|
1653
|
+
{ path: testFilePath, content: helloPromptTest }
|
|
1313
1654
|
];
|
|
1314
1655
|
let created = 0;
|
|
1315
1656
|
let skipped = 0;
|
|
@@ -1319,7 +1660,7 @@ async function init(args) {
|
|
|
1319
1660
|
skipped++;
|
|
1320
1661
|
continue;
|
|
1321
1662
|
}
|
|
1322
|
-
await mkdir2(
|
|
1663
|
+
await mkdir2(dirname6(file.path), { recursive: true });
|
|
1323
1664
|
await writeFile2(file.path, file.content, "utf-8");
|
|
1324
1665
|
console.log(` \u2713 ${file.path}`);
|
|
1325
1666
|
created++;
|
|
@@ -1334,14 +1675,61 @@ async function init(args) {
|
|
|
1334
1675
|
console.log(`Tip: Add to your package.json scripts:`);
|
|
1335
1676
|
console.log(` "build:prompts": "promptopskit compile ${dir}"`);
|
|
1336
1677
|
}
|
|
1678
|
+
if (!pkg.scripts?.test) {
|
|
1679
|
+
console.log();
|
|
1680
|
+
console.log(`Tip: Add a test script to run the generated prompt test:`);
|
|
1681
|
+
console.log(` "test": "node --test tests/*.test.mjs"`);
|
|
1682
|
+
}
|
|
1337
1683
|
} catch {
|
|
1338
1684
|
}
|
|
1339
1685
|
}
|
|
1340
1686
|
}
|
|
1687
|
+
function createHelloPromptTest(promptsDirFromTest) {
|
|
1688
|
+
return `import assert from 'node:assert/strict';
|
|
1689
|
+
import { dirname, resolve } from 'node:path';
|
|
1690
|
+
import test from 'node:test';
|
|
1691
|
+
import { fileURLToPath } from 'node:url';
|
|
1692
|
+
import { createPromptOpsKit } from 'promptopskit';
|
|
1693
|
+
import {
|
|
1694
|
+
getHardcodedPromptResponse,
|
|
1695
|
+
loadPromptTestSidecar,
|
|
1696
|
+
renderPromptTestCase,
|
|
1697
|
+
} from 'promptopskit/testing';
|
|
1698
|
+
|
|
1699
|
+
const testDir = dirname(fileURLToPath(import.meta.url));
|
|
1700
|
+
const promptsDir = resolve(testDir, ${JSON.stringify(promptsDirFromTest)});
|
|
1701
|
+
|
|
1702
|
+
test('hello prompt renders every sidecar case', async () => {
|
|
1703
|
+
const kit = createPromptOpsKit({ sourceDir: promptsDir, cache: false });
|
|
1704
|
+
const sidecar = await loadPromptTestSidecar(resolve(promptsDir, 'hello.test.yaml'));
|
|
1705
|
+
|
|
1706
|
+
for (const testCase of sidecar.cases) {
|
|
1707
|
+
const { rendered } = await renderPromptTestCase(kit, {
|
|
1708
|
+
sidecar,
|
|
1709
|
+
caseName: testCase.name,
|
|
1710
|
+
path: 'hello',
|
|
1711
|
+
provider: 'openai',
|
|
1712
|
+
environment: 'dev',
|
|
1713
|
+
strict: true,
|
|
1714
|
+
});
|
|
1715
|
+
|
|
1716
|
+
assert.ok(rendered.request?.body?.messages);
|
|
1717
|
+
}
|
|
1718
|
+
});
|
|
1719
|
+
|
|
1720
|
+
test('hello prompt can return a deterministic response without calling a model', async () => {
|
|
1721
|
+
const sidecar = await loadPromptTestSidecar(resolve(promptsDir, 'hello.test.yaml'));
|
|
1722
|
+
|
|
1723
|
+
assert.deepEqual(getHardcodedPromptResponse(sidecar, 'basic-greeting'), {
|
|
1724
|
+
message: 'Hello, World! How can I help you today?',
|
|
1725
|
+
});
|
|
1726
|
+
});
|
|
1727
|
+
`;
|
|
1728
|
+
}
|
|
1341
1729
|
|
|
1342
1730
|
// src/cli/commands/skill.ts
|
|
1343
1731
|
import { writeFile as writeFile3, readFile as readFile5, mkdir as mkdir3 } from "fs/promises";
|
|
1344
|
-
import { dirname as
|
|
1732
|
+
import { dirname as dirname7 } from "path";
|
|
1345
1733
|
import { existsSync as existsSync5 } from "fs";
|
|
1346
1734
|
var MARKER_START = "<!-- promptopskit:start -->";
|
|
1347
1735
|
var MARKER_END = "<!-- promptopskit:end -->";
|
|
@@ -1440,7 +1828,7 @@ async function skill(args) {
|
|
|
1440
1828
|
written++;
|
|
1441
1829
|
continue;
|
|
1442
1830
|
}
|
|
1443
|
-
await mkdir3(
|
|
1831
|
+
await mkdir3(dirname7(filePath), { recursive: true });
|
|
1444
1832
|
await writeFile3(filePath, markedContent, "utf-8");
|
|
1445
1833
|
console.log(` \u2713 ${filePath}`);
|
|
1446
1834
|
written++;
|
|
@@ -1453,7 +1841,7 @@ async function skill(args) {
|
|
|
1453
1841
|
async function deployClaude(filePath, force) {
|
|
1454
1842
|
const content = CLAUDE_LINE + "\n";
|
|
1455
1843
|
if (!existsSync5(filePath) || force) {
|
|
1456
|
-
await mkdir3(
|
|
1844
|
+
await mkdir3(dirname7(filePath), { recursive: true });
|
|
1457
1845
|
await writeFile3(filePath, content, "utf-8");
|
|
1458
1846
|
console.log(` \u2713 ${filePath}`);
|
|
1459
1847
|
return 1;
|