promptopskit 0.0.2 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +211 -211
- package/README.md +382 -335
- package/dist/{chunk-VYVEVJC3.js → chunk-B5UFGNDV.js} +2 -2
- package/dist/chunk-B5UFGNDV.js.map +1 -0
- package/dist/{chunk-5BI5FP5L.js → chunk-FSOJVXC4.js} +2 -2
- package/dist/chunk-FSOJVXC4.js.map +1 -0
- package/dist/{chunk-6KGBPHSY.js → chunk-HEFOFQ2K.js} +1 -1
- package/dist/chunk-HEFOFQ2K.js.map +1 -0
- package/dist/{chunk-NIZENC7D.js → chunk-HMYPMHTG.js} +90 -3
- package/dist/chunk-HMYPMHTG.js.map +1 -0
- package/dist/{chunk-UYTUGV7Z.js → chunk-V375NQ6C.js} +2 -2
- package/dist/chunk-V375NQ6C.js.map +1 -0
- package/dist/{chunk-PU3UPUND.js → chunk-YCRWYE6N.js} +2 -2
- package/dist/chunk-YCRWYE6N.js.map +1 -0
- package/dist/cli/index.js +167 -36
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +100 -14
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +8 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.js +7 -8
- package/dist/index.js.map +1 -1
- package/dist/providers/anthropic.cjs.map +1 -1
- package/dist/providers/anthropic.js +2 -2
- package/dist/providers/gemini.cjs.map +1 -1
- package/dist/providers/gemini.js +2 -2
- package/dist/providers/openai.cjs.map +1 -1
- package/dist/providers/openai.js +2 -2
- package/dist/providers/openrouter.cjs.map +1 -1
- package/dist/providers/openrouter.js +3 -3
- package/dist/testing.cjs +8 -0
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.js +1 -1
- package/dist/testing.js.map +1 -1
- package/package.json +117 -116
- package/dist/chunk-5BI5FP5L.js.map +0 -1
- package/dist/chunk-6KGBPHSY.js.map +0 -1
- package/dist/chunk-NIZENC7D.js.map +0 -1
- package/dist/chunk-PU3UPUND.js.map +0 -1
- package/dist/chunk-UYTUGV7Z.js.map +0 -1
- package/dist/chunk-VYVEVJC3.js.map +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/cli/commands/validate.ts
|
|
4
|
-
import { readdir
|
|
5
|
-
import { join, extname } from "path";
|
|
4
|
+
import { readdir } from "fs/promises";
|
|
5
|
+
import { join as join2, extname } from "path";
|
|
6
6
|
|
|
7
7
|
// src/parser/parser.ts
|
|
8
8
|
import matter from "gray-matter";
|
|
@@ -71,6 +71,12 @@ var SectionsSchema = z.object({
|
|
|
71
71
|
prompt_template: z.string().optional(),
|
|
72
72
|
notes: z.string().optional()
|
|
73
73
|
});
|
|
74
|
+
var PromptDefaultsSchema = z.object({
|
|
75
|
+
metadata: MetadataSchema.optional(),
|
|
76
|
+
sections: z.object({
|
|
77
|
+
system_instructions: z.string().optional()
|
|
78
|
+
}).optional()
|
|
79
|
+
});
|
|
74
80
|
var PromptAssetSchema = z.object({
|
|
75
81
|
id: z.string(),
|
|
76
82
|
schema_version: z.number().int().positive().default(1),
|
|
@@ -152,6 +158,91 @@ function parsePrompt(content, filePath) {
|
|
|
152
158
|
|
|
153
159
|
// src/parser/loader.ts
|
|
154
160
|
import { readFile } from "fs/promises";
|
|
161
|
+
import { dirname, join, resolve } from "path";
|
|
162
|
+
import matter2 from "gray-matter";
|
|
163
|
+
var DEFAULTS_FILE_NAME = "defaults.md";
|
|
164
|
+
async function loadPromptFile(filePath, options = {}) {
|
|
165
|
+
const content = await readFile(filePath, "utf-8");
|
|
166
|
+
const parsed = parsePrompt(content, filePath);
|
|
167
|
+
const root = options.defaultsRoot ?? dirname(filePath);
|
|
168
|
+
const defaults = await loadDefaultsForPath(filePath, root);
|
|
169
|
+
const asset = applyDefaults(parsed.asset, defaults);
|
|
170
|
+
return {
|
|
171
|
+
...parsed,
|
|
172
|
+
asset
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
async function loadDefaultsForPath(filePath, defaultsRoot) {
|
|
176
|
+
const directories = getDirectoriesToCheck(filePath, defaultsRoot);
|
|
177
|
+
let merged = {};
|
|
178
|
+
for (const dir of directories) {
|
|
179
|
+
const defaultsPath = join(dir, DEFAULTS_FILE_NAME);
|
|
180
|
+
try {
|
|
181
|
+
const defaultsContent = await readFile(defaultsPath, "utf-8");
|
|
182
|
+
const defaults = parseDefaults(defaultsContent);
|
|
183
|
+
merged = mergeDefaults(merged, defaults);
|
|
184
|
+
} catch (error) {
|
|
185
|
+
if (error.code !== "ENOENT") {
|
|
186
|
+
throw error;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
return merged;
|
|
191
|
+
}
|
|
192
|
+
function getDirectoriesToCheck(filePath, defaultsRoot) {
|
|
193
|
+
const dirs = [];
|
|
194
|
+
let current = resolve(dirname(filePath));
|
|
195
|
+
const boundary = defaultsRoot ? resolve(defaultsRoot) : void 0;
|
|
196
|
+
while (true) {
|
|
197
|
+
dirs.unshift(current);
|
|
198
|
+
if (boundary && current === boundary || current === dirname(current)) {
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
current = dirname(current);
|
|
202
|
+
}
|
|
203
|
+
return dirs;
|
|
204
|
+
}
|
|
205
|
+
function parseDefaults(content) {
|
|
206
|
+
const { data: frontMatter, content: body } = matter2(content);
|
|
207
|
+
const sections = extractSections(body);
|
|
208
|
+
return PromptDefaultsSchema.parse({
|
|
209
|
+
...frontMatter,
|
|
210
|
+
sections: {
|
|
211
|
+
system_instructions: sections.system_instructions
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
function mergeDefaults(base, local) {
|
|
216
|
+
return {
|
|
217
|
+
metadata: {
|
|
218
|
+
...base.metadata ?? {},
|
|
219
|
+
...local.metadata ?? {}
|
|
220
|
+
},
|
|
221
|
+
sections: {
|
|
222
|
+
...base.sections ?? {},
|
|
223
|
+
...local.sections ?? {}
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
function applyDefaults(asset, defaults) {
|
|
228
|
+
const hasDefaultMetadata = defaults.metadata && Object.keys(defaults.metadata).length > 0;
|
|
229
|
+
const hasDefaultSystem = !!defaults.sections?.system_instructions;
|
|
230
|
+
if (!hasDefaultMetadata && !hasDefaultSystem) {
|
|
231
|
+
return asset;
|
|
232
|
+
}
|
|
233
|
+
const mergedMetadata = {
|
|
234
|
+
...defaults.metadata ?? {},
|
|
235
|
+
...asset.metadata ?? {}
|
|
236
|
+
};
|
|
237
|
+
const metadata = Object.keys(mergedMetadata).length > 0 ? mergedMetadata : void 0;
|
|
238
|
+
const systemInstructions = asset.sections?.system_instructions ?? defaults.sections?.system_instructions;
|
|
239
|
+
const sections = asset.sections ? { ...asset.sections, system_instructions: systemInstructions } : systemInstructions ? { system_instructions: systemInstructions } : void 0;
|
|
240
|
+
return {
|
|
241
|
+
...asset,
|
|
242
|
+
metadata,
|
|
243
|
+
sections
|
|
244
|
+
};
|
|
245
|
+
}
|
|
155
246
|
|
|
156
247
|
// src/renderer/interpolate.ts
|
|
157
248
|
var VARIABLE_RE = /\{\{\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*\}\}/g;
|
|
@@ -184,20 +275,20 @@ function extractVariables(template) {
|
|
|
184
275
|
|
|
185
276
|
// src/composition/resolve-includes.ts
|
|
186
277
|
import { readFile as readFile2 } from "fs/promises";
|
|
187
|
-
import { resolve, dirname } from "path";
|
|
278
|
+
import { resolve as resolve2, dirname as dirname2 } from "path";
|
|
188
279
|
async function resolveIncludes(asset, basePath, visited = /* @__PURE__ */ new Set()) {
|
|
189
280
|
if (!asset.includes || asset.includes.length === 0) {
|
|
190
281
|
return asset;
|
|
191
282
|
}
|
|
192
|
-
const baseDir =
|
|
193
|
-
const resolvedPath =
|
|
283
|
+
const baseDir = dirname2(basePath);
|
|
284
|
+
const resolvedPath = resolve2(basePath);
|
|
194
285
|
if (visited.has(resolvedPath)) {
|
|
195
286
|
throw new Error(`Circular include detected: ${resolvedPath}`);
|
|
196
287
|
}
|
|
197
288
|
visited.add(resolvedPath);
|
|
198
289
|
let mergedSystemInstructions = "";
|
|
199
290
|
for (const includePath of asset.includes) {
|
|
200
|
-
const fullPath =
|
|
291
|
+
const fullPath = resolve2(baseDir, includePath);
|
|
201
292
|
if (visited.has(fullPath)) {
|
|
202
293
|
throw new Error(`Circular include detected: ${fullPath} (included from ${basePath})`);
|
|
203
294
|
}
|
|
@@ -399,8 +490,7 @@ async function validate(args) {
|
|
|
399
490
|
let warnCount = 0;
|
|
400
491
|
for (const file of files) {
|
|
401
492
|
try {
|
|
402
|
-
const
|
|
403
|
-
const { asset, raw } = parsePrompt(content, file);
|
|
493
|
+
const { asset, raw } = await loadPromptFile(file, { defaultsRoot: dir });
|
|
404
494
|
const result = await validateAssetWithIncludes(asset, file, Object.keys(raw.frontMatter));
|
|
405
495
|
if (result.errors.length > 0) {
|
|
406
496
|
errorCount += result.errors.length;
|
|
@@ -435,16 +525,16 @@ async function collectPromptFiles(dir) {
|
|
|
435
525
|
const results = [];
|
|
436
526
|
const entries = await readdir(dir, { withFileTypes: true, recursive: true });
|
|
437
527
|
for (const entry of entries) {
|
|
438
|
-
if (entry.isFile() && extname(entry.name) === ".md" && !entry.name.endsWith(".test.md")) {
|
|
439
|
-
results.push(
|
|
528
|
+
if (entry.isFile() && extname(entry.name) === ".md" && !entry.name.endsWith(".test.md") && entry.name !== "defaults.md") {
|
|
529
|
+
results.push(join2(entry.parentPath ?? dir, entry.name));
|
|
440
530
|
}
|
|
441
531
|
}
|
|
442
532
|
return results.sort();
|
|
443
533
|
}
|
|
444
534
|
|
|
445
535
|
// src/cli/commands/compile.ts
|
|
446
|
-
import { readdir as readdir2,
|
|
447
|
-
import { join as
|
|
536
|
+
import { readdir as readdir2, writeFile, mkdir, rm } from "fs/promises";
|
|
537
|
+
import { join as join3, extname as extname2, relative, dirname as dirname3 } from "path";
|
|
448
538
|
var HELP2 = `
|
|
449
539
|
promptopskit compile <sourceDir> <outputDir> [options]
|
|
450
540
|
|
|
@@ -489,15 +579,14 @@ async function compile(args) {
|
|
|
489
579
|
for (const file of files) {
|
|
490
580
|
const rel = relative(sourceDir, file).replace(/\.md$/, "");
|
|
491
581
|
const outExt = format === "esm" ? ".mjs" : ".json";
|
|
492
|
-
const outPath =
|
|
582
|
+
const outPath = join3(outputDir, rel + outExt);
|
|
493
583
|
try {
|
|
494
|
-
const
|
|
495
|
-
const { asset: parsed } = parsePrompt(content, file);
|
|
584
|
+
const { asset: parsed } = await loadPromptFile(file, { defaultsRoot: sourceDir });
|
|
496
585
|
const asset = parsed.includes && parsed.includes.length > 0 ? await resolveIncludes(parsed, file) : parsed;
|
|
497
586
|
if (dryRun) {
|
|
498
587
|
console.log(` Would create: ${outPath}`);
|
|
499
588
|
} else {
|
|
500
|
-
await mkdir(
|
|
589
|
+
await mkdir(dirname3(outPath), { recursive: true });
|
|
501
590
|
if (format === "esm") {
|
|
502
591
|
const esmContent = `export default ${JSON.stringify(asset, null, 2)};
|
|
503
592
|
`;
|
|
@@ -536,15 +625,15 @@ async function collectPromptFiles2(dir) {
|
|
|
536
625
|
const results = [];
|
|
537
626
|
const entries = await readdir2(dir, { withFileTypes: true, recursive: true });
|
|
538
627
|
for (const entry of entries) {
|
|
539
|
-
if (entry.isFile() && extname2(entry.name) === ".md" && !entry.name.endsWith(".test.md")) {
|
|
540
|
-
results.push(
|
|
628
|
+
if (entry.isFile() && extname2(entry.name) === ".md" && !entry.name.endsWith(".test.md") && entry.name !== "defaults.md") {
|
|
629
|
+
results.push(join3(entry.parentPath ?? dir, entry.name));
|
|
541
630
|
}
|
|
542
631
|
}
|
|
543
632
|
return results.sort();
|
|
544
633
|
}
|
|
545
634
|
|
|
546
635
|
// src/cli/commands/render.ts
|
|
547
|
-
import { readFile as
|
|
636
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
548
637
|
import { existsSync } from "fs";
|
|
549
638
|
|
|
550
639
|
// src/overrides/apply-overrides.ts
|
|
@@ -607,13 +696,13 @@ async function render(args) {
|
|
|
607
696
|
const jsonOutput = args.includes("--json");
|
|
608
697
|
let variables = {};
|
|
609
698
|
if (varsFile) {
|
|
610
|
-
const varsContent = await
|
|
699
|
+
const varsContent = await readFile3(varsFile, "utf-8");
|
|
611
700
|
variables = JSON.parse(varsContent);
|
|
612
701
|
} else {
|
|
613
702
|
const sidecarPath = file.replace(/\.md$/, ".test.yaml");
|
|
614
703
|
if (existsSync(sidecarPath)) {
|
|
615
704
|
const { default: yaml } = await import("gray-matter");
|
|
616
|
-
const sidecarContent = await
|
|
705
|
+
const sidecarContent = await readFile3(sidecarPath, "utf-8");
|
|
617
706
|
const parsed2 = yaml(`---
|
|
618
707
|
${sidecarContent}---
|
|
619
708
|
`);
|
|
@@ -623,8 +712,7 @@ ${sidecarContent}---
|
|
|
623
712
|
}
|
|
624
713
|
}
|
|
625
714
|
}
|
|
626
|
-
const
|
|
627
|
-
const { asset: parsed } = parsePrompt(content, file);
|
|
715
|
+
const { asset: parsed } = await loadPromptFile(file);
|
|
628
716
|
const resolved = parsed.includes && parsed.includes.length > 0 ? await resolveIncludes(parsed, file) : parsed;
|
|
629
717
|
const overridden = applyOverrides(resolved, {
|
|
630
718
|
environment: env,
|
|
@@ -671,7 +759,6 @@ function getFlag2(args, flag) {
|
|
|
671
759
|
}
|
|
672
760
|
|
|
673
761
|
// src/cli/commands/inspect.ts
|
|
674
|
-
import { readFile as readFile6 } from "fs/promises";
|
|
675
762
|
var HELP4 = `
|
|
676
763
|
promptopskit inspect <file>
|
|
677
764
|
|
|
@@ -690,15 +777,14 @@ async function inspect(args) {
|
|
|
690
777
|
console.error("Error: Please provide a prompt file to inspect.");
|
|
691
778
|
process.exit(1);
|
|
692
779
|
}
|
|
693
|
-
const
|
|
694
|
-
const { asset: parsed } = parsePrompt(content, file);
|
|
780
|
+
const { asset: parsed } = await loadPromptFile(file);
|
|
695
781
|
const asset = parsed.includes && parsed.includes.length > 0 ? await resolveIncludes(parsed, file) : parsed;
|
|
696
782
|
console.log(JSON.stringify(asset, null, 2));
|
|
697
783
|
}
|
|
698
784
|
|
|
699
785
|
// src/cli/commands/init.ts
|
|
700
786
|
import { writeFile as writeFile2, mkdir as mkdir2 } from "fs/promises";
|
|
701
|
-
import { join as
|
|
787
|
+
import { join as join4, dirname as dirname4 } from "path";
|
|
702
788
|
import { existsSync as existsSync2, readFileSync } from "fs";
|
|
703
789
|
var HELP5 = `
|
|
704
790
|
promptopskit init [dir]
|
|
@@ -716,6 +802,7 @@ model: gpt-5.4
|
|
|
716
802
|
context:
|
|
717
803
|
inputs:
|
|
718
804
|
- name
|
|
805
|
+
- app_context
|
|
719
806
|
includes:
|
|
720
807
|
- ./shared/tone.md
|
|
721
808
|
---
|
|
@@ -723,6 +810,7 @@ includes:
|
|
|
723
810
|
# System instructions
|
|
724
811
|
|
|
725
812
|
You are a friendly assistant. Be helpful and concise.
|
|
813
|
+
Current app context: {{ app_context }}.
|
|
726
814
|
|
|
727
815
|
# Prompt template
|
|
728
816
|
|
|
@@ -737,13 +825,25 @@ schema_version: 1
|
|
|
737
825
|
|
|
738
826
|
Always be polite, professional, and concise. Avoid jargon unless the user uses it first.
|
|
739
827
|
`.trimStart();
|
|
828
|
+
var DEFAULTS = `---
|
|
829
|
+
metadata:
|
|
830
|
+
owner: my-team
|
|
831
|
+
review_required: true
|
|
832
|
+
---
|
|
833
|
+
|
|
834
|
+
# System instructions
|
|
835
|
+
|
|
836
|
+
You are a helpful AI assistant. Follow company guidelines at all times.
|
|
837
|
+
`.trimStart();
|
|
740
838
|
var TEST_SIDECAR = `cases:
|
|
741
839
|
- name: basic-greeting
|
|
742
840
|
variables:
|
|
743
841
|
name: "World"
|
|
842
|
+
app_context: "Welcome screen"
|
|
744
843
|
- name: named-greeting
|
|
745
844
|
variables:
|
|
746
845
|
name: "Alice"
|
|
846
|
+
app_context: "Settings page"
|
|
747
847
|
`;
|
|
748
848
|
async function init(args) {
|
|
749
849
|
if (args.includes("--help") || args.includes("-h")) {
|
|
@@ -752,9 +852,10 @@ async function init(args) {
|
|
|
752
852
|
}
|
|
753
853
|
const dir = args.find((a) => !a.startsWith("--")) ?? "./prompts";
|
|
754
854
|
const files = [
|
|
755
|
-
{ path:
|
|
756
|
-
{ path:
|
|
757
|
-
{ path:
|
|
855
|
+
{ path: join4(dir, "defaults.md"), content: DEFAULTS },
|
|
856
|
+
{ path: join4(dir, "hello.md"), content: HELLO_PROMPT },
|
|
857
|
+
{ path: join4(dir, "hello.test.yaml"), content: TEST_SIDECAR },
|
|
858
|
+
{ path: join4(dir, "shared", "tone.md"), content: TONE_INCLUDE }
|
|
758
859
|
];
|
|
759
860
|
let created = 0;
|
|
760
861
|
let skipped = 0;
|
|
@@ -764,7 +865,7 @@ async function init(args) {
|
|
|
764
865
|
skipped++;
|
|
765
866
|
continue;
|
|
766
867
|
}
|
|
767
|
-
await mkdir2(
|
|
868
|
+
await mkdir2(dirname4(file.path), { recursive: true });
|
|
768
869
|
await writeFile2(file.path, file.content, "utf-8");
|
|
769
870
|
console.log(` \u2713 ${file.path}`);
|
|
770
871
|
created++;
|
|
@@ -786,7 +887,7 @@ async function init(args) {
|
|
|
786
887
|
|
|
787
888
|
// src/cli/commands/skill.ts
|
|
788
889
|
import { writeFile as writeFile3, mkdir as mkdir3 } from "fs/promises";
|
|
789
|
-
import { dirname as
|
|
890
|
+
import { dirname as dirname5 } from "path";
|
|
790
891
|
import { existsSync as existsSync3 } from "fs";
|
|
791
892
|
var HELP6 = `
|
|
792
893
|
promptopskit skill [--target <target>] [--force]
|
|
@@ -851,7 +952,7 @@ async function skill(args) {
|
|
|
851
952
|
return;
|
|
852
953
|
}
|
|
853
954
|
const content = config.wrap(SKILL_CONTENT);
|
|
854
|
-
await mkdir3(
|
|
955
|
+
await mkdir3(dirname5(filePath), { recursive: true });
|
|
855
956
|
await writeFile3(filePath, content, "utf-8");
|
|
856
957
|
console.log(` \u2713 ${filePath}`);
|
|
857
958
|
console.log();
|
|
@@ -963,6 +1064,35 @@ Included files are parsed and their \`system_instructions\` are **prepended**
|
|
|
963
1064
|
before the including file's own system instructions. Includes resolve
|
|
964
1065
|
recursively. Circular includes are detected and rejected.
|
|
965
1066
|
|
|
1067
|
+
> **Note:** Included files do not inherit folder defaults. Only the top-level
|
|
1068
|
+
> prompt that is loaded via \`loadPromptFile\` receives defaults.
|
|
1069
|
+
|
|
1070
|
+
---
|
|
1071
|
+
|
|
1072
|
+
## Folder defaults (\`defaults.md\`)
|
|
1073
|
+
|
|
1074
|
+
Define shared defaults for a prompt tree by adding a \`defaults.md\` file in any
|
|
1075
|
+
folder:
|
|
1076
|
+
|
|
1077
|
+
\`\`\`text
|
|
1078
|
+
prompts/
|
|
1079
|
+
\u251C\u2500\u2500 defaults.md # global metadata + system instructions
|
|
1080
|
+
\u2514\u2500\u2500 support/
|
|
1081
|
+
\u251C\u2500\u2500 defaults.md # overrides for support/*
|
|
1082
|
+
\u2514\u2500\u2500 reply.md # inherits from support/defaults.md
|
|
1083
|
+
\`\`\`
|
|
1084
|
+
|
|
1085
|
+
Supported default fields:
|
|
1086
|
+
- \`metadata\` (front matter) \u2014 merged with prompt-local metadata
|
|
1087
|
+
- \`# System instructions\` (body section) \u2014 used when the prompt has none
|
|
1088
|
+
|
|
1089
|
+
Rules:
|
|
1090
|
+
- Nearest subfolder \`defaults.md\` overrides parent defaults
|
|
1091
|
+
- Prompt-local values always take precedence over defaults
|
|
1092
|
+
- \`defaults.md\` files are skipped during compilation and validation
|
|
1093
|
+
- \`loadPromptFile\` defaults the search boundary to the file's own directory;
|
|
1094
|
+
pass \`defaultsRoot\` to enable ancestor traversal
|
|
1095
|
+
|
|
966
1096
|
---
|
|
967
1097
|
|
|
968
1098
|
## Environment & tier overrides
|
|
@@ -1125,7 +1255,7 @@ Hello {{ name }}
|
|
|
1125
1255
|
|
|
1126
1256
|
| Command | Description |
|
|
1127
1257
|
|---------|-------------|
|
|
1128
|
-
| \`promptopskit init [dir]\` | Scaffold a prompts directory with starter files |
|
|
1258
|
+
| \`promptopskit init [dir]\` | Scaffold a prompts directory with starter files (including \`defaults.md\`) |
|
|
1129
1259
|
| \`promptopskit validate <dir>\` | Validate all prompt files in a directory |
|
|
1130
1260
|
| \`promptopskit compile <src> <out>\` | Compile .md prompts to JSON artifacts |
|
|
1131
1261
|
| \`promptopskit render <file> [--set key=value]\` | Render a prompt preview |
|
|
@@ -1143,8 +1273,9 @@ Hello {{ name }}
|
|
|
1143
1273
|
6. **Use environment overrides** for dev/staging/prod model differences
|
|
1144
1274
|
7. **Add test sidecars** (\`.test.yaml\`) for critical prompts
|
|
1145
1275
|
8. **Run \`promptopskit validate\`** before committing changes
|
|
1146
|
-
9. **
|
|
1147
|
-
10. **
|
|
1276
|
+
9. **Use \`defaults.md\`** to share metadata and system instructions across a folder
|
|
1277
|
+
10. **Variable names** should be \`snake_case\`
|
|
1278
|
+
11. **Prompt file names** should be \`kebab-case.md\`
|
|
1148
1279
|
`.trimEnd();
|
|
1149
1280
|
|
|
1150
1281
|
// src/cli/index.ts
|