promptopskit 0.2.3 → 0.2.5
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 +8 -4
- package/SKILL.md +363 -0
- package/dist/cli/index.js +31 -331
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/cli/index.js
CHANGED
|
@@ -413,7 +413,7 @@ function validateAsset(asset, frontMatterKeys, filePath) {
|
|
|
413
413
|
}
|
|
414
414
|
}
|
|
415
415
|
for (const v of usedVars) {
|
|
416
|
-
if (
|
|
416
|
+
if (!declaredInputs.has(v)) {
|
|
417
417
|
warnings.push({
|
|
418
418
|
code: "POK011",
|
|
419
419
|
message: `Variable "{{ ${v} }}" is used but not declared in context.inputs`,
|
|
@@ -990,6 +990,12 @@ Options:
|
|
|
990
990
|
--force, -f Overwrite entire file instead of merging
|
|
991
991
|
--help, -h Show this help
|
|
992
992
|
`.trim();
|
|
993
|
+
var STUB_CONTENT = `# promptopskit
|
|
994
|
+
|
|
995
|
+
This project uses **promptopskit** to manage LLM prompts as code.
|
|
996
|
+
Read the full guide at \`node_modules/promptopskit/SKILL.md\` before
|
|
997
|
+
creating or editing prompt files.`;
|
|
998
|
+
var CLAUDE_LINE = "@AGENTS.md";
|
|
993
999
|
var TARGETS = {
|
|
994
1000
|
agents: {
|
|
995
1001
|
path: "AGENTS.md",
|
|
@@ -997,7 +1003,7 @@ var TARGETS = {
|
|
|
997
1003
|
},
|
|
998
1004
|
claude: {
|
|
999
1005
|
path: "CLAUDE.md",
|
|
1000
|
-
wrap: () => "
|
|
1006
|
+
wrap: () => CLAUDE_LINE + "\n"
|
|
1001
1007
|
},
|
|
1002
1008
|
copilot: {
|
|
1003
1009
|
path: ".github/instructions/promptopskit.instructions.md",
|
|
@@ -1041,7 +1047,11 @@ async function skill(args) {
|
|
|
1041
1047
|
for (const target of targets) {
|
|
1042
1048
|
const config = TARGETS[target];
|
|
1043
1049
|
const filePath = config.path;
|
|
1044
|
-
|
|
1050
|
+
if (target === "claude") {
|
|
1051
|
+
written += await deployClaude(filePath, force);
|
|
1052
|
+
continue;
|
|
1053
|
+
}
|
|
1054
|
+
const markedContent = config.wrap(wrapMarkers(STUB_CONTENT));
|
|
1045
1055
|
if (existsSync3(filePath) && !force) {
|
|
1046
1056
|
const existing = await readFile4(filePath, "utf-8");
|
|
1047
1057
|
const merged = mergeContent(existing, markedContent);
|
|
@@ -1064,6 +1074,24 @@ async function skill(args) {
|
|
|
1064
1074
|
console.log(`AI agents will now understand how to create and manage prompts with promptopskit.`);
|
|
1065
1075
|
}
|
|
1066
1076
|
}
|
|
1077
|
+
async function deployClaude(filePath, force) {
|
|
1078
|
+
const content = CLAUDE_LINE + "\n";
|
|
1079
|
+
if (!existsSync3(filePath) || force) {
|
|
1080
|
+
await mkdir3(dirname5(filePath), { recursive: true });
|
|
1081
|
+
await writeFile3(filePath, content, "utf-8");
|
|
1082
|
+
console.log(` \u2713 ${filePath}`);
|
|
1083
|
+
return 1;
|
|
1084
|
+
}
|
|
1085
|
+
const existing = await readFile4(filePath, "utf-8");
|
|
1086
|
+
if (existing.split("\n").some((line) => line.trim() === CLAUDE_LINE)) {
|
|
1087
|
+
console.log(` skip ${filePath} (already up to date)`);
|
|
1088
|
+
return 0;
|
|
1089
|
+
}
|
|
1090
|
+
const separator = existing.endsWith("\n") ? "\n" : "\n\n";
|
|
1091
|
+
await writeFile3(filePath, existing + separator + content, "utf-8");
|
|
1092
|
+
console.log(` \u2713 ${filePath} (merged)`);
|
|
1093
|
+
return 1;
|
|
1094
|
+
}
|
|
1067
1095
|
function wrapMarkers(content) {
|
|
1068
1096
|
return `${MARKER_START}
|
|
1069
1097
|
${content}
|
|
@@ -1078,334 +1106,6 @@ function mergeContent(existing, markedContent) {
|
|
|
1078
1106
|
const separator = existing.endsWith("\n") ? "\n" : "\n\n";
|
|
1079
1107
|
return existing + separator + markedContent + "\n";
|
|
1080
1108
|
}
|
|
1081
|
-
var SKILL_CONTENT = `# promptopskit \u2014 Prompt Engineering Skill
|
|
1082
|
-
|
|
1083
|
-
This project uses **promptopskit** to manage LLM prompts as code.
|
|
1084
|
-
Prompts live in markdown files with YAML front matter, are validated against
|
|
1085
|
-
a schema, and render into provider-specific request bodies (OpenAI, Anthropic,
|
|
1086
|
-
Gemini, OpenRouter). Follow these instructions when creating or editing prompts.
|
|
1087
|
-
|
|
1088
|
-
---
|
|
1089
|
-
|
|
1090
|
-
## Prompt file format
|
|
1091
|
-
|
|
1092
|
-
Every prompt is a \`.md\` file with two parts:
|
|
1093
|
-
|
|
1094
|
-
1. **YAML front matter** \u2014 model settings, provider config, variables, overrides
|
|
1095
|
-
2. **Markdown body** \u2014 sections separated by H1 headings
|
|
1096
|
-
|
|
1097
|
-
### Minimal example
|
|
1098
|
-
|
|
1099
|
-
\`\`\`markdown
|
|
1100
|
-
---
|
|
1101
|
-
id: greeting
|
|
1102
|
-
schema_version: 1
|
|
1103
|
-
provider: openai
|
|
1104
|
-
model: gpt-5.4
|
|
1105
|
-
context:
|
|
1106
|
-
inputs:
|
|
1107
|
-
- name
|
|
1108
|
-
---
|
|
1109
|
-
|
|
1110
|
-
# System instructions
|
|
1111
|
-
|
|
1112
|
-
You are a helpful assistant.
|
|
1113
|
-
|
|
1114
|
-
# Prompt template
|
|
1115
|
-
|
|
1116
|
-
Hello {{ name }}, how can I help you?
|
|
1117
|
-
\`\`\`
|
|
1118
|
-
|
|
1119
|
-
---
|
|
1120
|
-
|
|
1121
|
-
## Front matter reference
|
|
1122
|
-
|
|
1123
|
-
| Field | Type | Required | Description |
|
|
1124
|
-
|-------|------|----------|-------------|
|
|
1125
|
-
| \`id\` | string | **yes** | Unique identifier for the prompt |
|
|
1126
|
-
| \`schema_version\` | number | yes | Always \`1\` |
|
|
1127
|
-
| \`description\` | string | no | Human-readable description |
|
|
1128
|
-
| \`provider\` | enum | no | \`openai\`, \`anthropic\`, \`google\`, \`gemini\`, \`openrouter\`, or \`any\` |
|
|
1129
|
-
| \`model\` | string | no | Model identifier (e.g. \`gpt-5.4\`, \`claude-sonnet-4-20250514\`) |
|
|
1130
|
-
| \`fallback_models\` | string[] | no | Ordered fallback model list |
|
|
1131
|
-
| \`reasoning\` | object | no | \`{ effort: low|medium|high, budget_tokens: number }\` |
|
|
1132
|
-
| \`sampling\` | object | no | \`{ temperature, top_p, frequency_penalty, presence_penalty, stop, max_output_tokens }\` |
|
|
1133
|
-
| \`response\` | object | no | \`{ format: text|json|markdown, stream: boolean }\` |
|
|
1134
|
-
| \`tools\` | array | no | Tool names (strings) or inline definitions with \`{ name, description, input_schema }\` |
|
|
1135
|
-
| \`mcp\` | object | no | \`{ servers: [string | { name, config }] }\` |
|
|
1136
|
-
| \`context.inputs\` | string[] | no | Declared variable names used in templates |
|
|
1137
|
-
| \`context.history\` | object | no | \`{ max_items: number }\` |
|
|
1138
|
-
| \`includes\` | string[] | no | Relative paths to other prompt files to include |
|
|
1139
|
-
| \`environments\` | object | no | Per-environment overrides (see Overrides) |
|
|
1140
|
-
| \`tiers\` | object | no | Per-tier overrides (see Overrides) |
|
|
1141
|
-
| \`metadata\` | object | no | \`{ owner, tags, review_required, stable }\` |
|
|
1142
|
-
|
|
1143
|
-
---
|
|
1144
|
-
|
|
1145
|
-
## Sections (markdown body)
|
|
1146
|
-
|
|
1147
|
-
Use H1 headings to define sections. The parser recognizes these headings
|
|
1148
|
-
(case-insensitive):
|
|
1149
|
-
|
|
1150
|
-
| Heading | Maps to | Purpose |
|
|
1151
|
-
|---------|---------|---------|
|
|
1152
|
-
| \`# System instructions\` | \`system_instructions\` | System/developer message |
|
|
1153
|
-
| \`# Prompt template\` | \`prompt_template\` | User message template |
|
|
1154
|
-
| \`# Notes\` | \`notes\` | Documentation only \u2014 not sent to the model |
|
|
1155
|
-
|
|
1156
|
-
If the body has **no H1 headings**, the entire body becomes the \`prompt_template\`.
|
|
1157
|
-
|
|
1158
|
-
---
|
|
1159
|
-
|
|
1160
|
-
## Variable interpolation
|
|
1161
|
-
|
|
1162
|
-
Use \`{{ variable_name }}\` syntax in system instructions and prompt template
|
|
1163
|
-
sections. Variables are replaced at render time.
|
|
1164
|
-
|
|
1165
|
-
Rules:
|
|
1166
|
-
- Declare all variables in \`context.inputs\` \u2014 validation warns on undeclared usage
|
|
1167
|
-
- Escape literal braces with \`\\\\{{\` and \`\\\\}}\`
|
|
1168
|
-
- In strict mode, missing variables throw an error
|
|
1169
|
-
- In permissive mode, unresolved placeholders are left intact
|
|
1170
|
-
|
|
1171
|
-
---
|
|
1172
|
-
|
|
1173
|
-
## Includes (composition)
|
|
1174
|
-
|
|
1175
|
-
Compose prompts from shared fragments:
|
|
1176
|
-
|
|
1177
|
-
\`\`\`yaml
|
|
1178
|
-
includes:
|
|
1179
|
-
- ./shared/tone.md
|
|
1180
|
-
- ./shared/safety.md
|
|
1181
|
-
\`\`\`
|
|
1182
|
-
|
|
1183
|
-
Included files are parsed and their \`system_instructions\` are **prepended**
|
|
1184
|
-
before the including file's own system instructions. Includes resolve
|
|
1185
|
-
recursively. Circular includes are detected and rejected.
|
|
1186
|
-
|
|
1187
|
-
> **Note:** Included files do not inherit folder defaults. Only the top-level
|
|
1188
|
-
> prompt that is loaded via \`loadPromptFile\` receives defaults.
|
|
1189
|
-
|
|
1190
|
-
---
|
|
1191
|
-
|
|
1192
|
-
## Folder defaults (\`defaults.md\`)
|
|
1193
|
-
|
|
1194
|
-
Define shared defaults for a prompt tree by adding a \`defaults.md\` file in any
|
|
1195
|
-
folder:
|
|
1196
|
-
|
|
1197
|
-
\`\`\`text
|
|
1198
|
-
prompts/
|
|
1199
|
-
\u251C\u2500\u2500 defaults.md # global provider, model, metadata + system instructions
|
|
1200
|
-
\u2514\u2500\u2500 support/
|
|
1201
|
-
\u251C\u2500\u2500 defaults.md # overrides for support/*
|
|
1202
|
-
\u2514\u2500\u2500 reply.md # inherits from support/defaults.md
|
|
1203
|
-
\`\`\`
|
|
1204
|
-
|
|
1205
|
-
Supported default fields:
|
|
1206
|
-
- \`provider\` (front matter) \u2014 default provider for the folder
|
|
1207
|
-
- \`model\` (front matter) \u2014 default model for the folder
|
|
1208
|
-
- \`metadata\` (front matter) \u2014 merged with prompt-local metadata
|
|
1209
|
-
- \`# System instructions\` (body section) \u2014 used when the prompt has none
|
|
1210
|
-
|
|
1211
|
-
This lets you configure app-wide settings like \`provider\` and \`model\`
|
|
1212
|
-
in a single root \`defaults.md\`, so individual prompts only declare what\u2019s unique to them.
|
|
1213
|
-
|
|
1214
|
-
Rules:
|
|
1215
|
-
- Nearest subfolder \`defaults.md\` overrides parent defaults
|
|
1216
|
-
- Prompt-local values always take precedence over defaults
|
|
1217
|
-
- \`defaults.md\` files are skipped during compilation and validation
|
|
1218
|
-
- \`loadPromptFile\` defaults the search boundary to the file's own directory;
|
|
1219
|
-
pass \`defaultsRoot\` to enable ancestor traversal
|
|
1220
|
-
|
|
1221
|
-
---
|
|
1222
|
-
|
|
1223
|
-
## Environment & tier overrides
|
|
1224
|
-
|
|
1225
|
-
Override model settings per environment or tier:
|
|
1226
|
-
|
|
1227
|
-
\`\`\`yaml
|
|
1228
|
-
environments:
|
|
1229
|
-
development:
|
|
1230
|
-
model: gpt-4.1-mini
|
|
1231
|
-
reasoning:
|
|
1232
|
-
effort: low
|
|
1233
|
-
sampling:
|
|
1234
|
-
temperature: 0.9
|
|
1235
|
-
production:
|
|
1236
|
-
model: gpt-5.4
|
|
1237
|
-
reasoning:
|
|
1238
|
-
effort: high
|
|
1239
|
-
sampling:
|
|
1240
|
-
temperature: 0.3
|
|
1241
|
-
|
|
1242
|
-
tiers:
|
|
1243
|
-
free:
|
|
1244
|
-
model: gpt-4.1-mini
|
|
1245
|
-
sampling:
|
|
1246
|
-
max_output_tokens: 500
|
|
1247
|
-
premium:
|
|
1248
|
-
model: gpt-5.4
|
|
1249
|
-
\`\`\`
|
|
1250
|
-
|
|
1251
|
-
Overridable fields: \`model\`, \`fallback_models\`, \`reasoning\`, \`sampling\`,
|
|
1252
|
-
\`response\`, \`tools\`.
|
|
1253
|
-
|
|
1254
|
-
Override application order: **base \u2192 environment \u2192 tier \u2192 runtime**.
|
|
1255
|
-
|
|
1256
|
-
---
|
|
1257
|
-
|
|
1258
|
-
## Test sidecars
|
|
1259
|
-
|
|
1260
|
-
Create a \`.test.yaml\` file alongside a prompt to define test cases:
|
|
1261
|
-
|
|
1262
|
-
\`\`\`yaml
|
|
1263
|
-
# greeting.test.yaml
|
|
1264
|
-
cases:
|
|
1265
|
-
- name: basic
|
|
1266
|
-
variables:
|
|
1267
|
-
name: "World"
|
|
1268
|
-
- name: formal
|
|
1269
|
-
variables:
|
|
1270
|
-
name: "Dr. Smith"
|
|
1271
|
-
\`\`\`
|
|
1272
|
-
|
|
1273
|
-
---
|
|
1274
|
-
|
|
1275
|
-
## Using the library (TypeScript / JavaScript)
|
|
1276
|
-
|
|
1277
|
-
### Quick start
|
|
1278
|
-
|
|
1279
|
-
\`\`\`typescript
|
|
1280
|
-
import { createPromptOpsKit } from 'promptopskit';
|
|
1281
|
-
|
|
1282
|
-
const kit = createPromptOpsKit({ sourceDir: './prompts' });
|
|
1283
|
-
|
|
1284
|
-
// Load \u2192 resolve includes \u2192 apply overrides \u2192 render
|
|
1285
|
-
const request = await kit.renderPrompt('greeting', {
|
|
1286
|
-
variables: { name: 'Alice' },
|
|
1287
|
-
environment: 'production',
|
|
1288
|
-
});
|
|
1289
|
-
|
|
1290
|
-
// request.body is ready for the provider's API
|
|
1291
|
-
const response = await fetch('https://api.openai.com/v1/chat/completions', {
|
|
1292
|
-
method: 'POST',
|
|
1293
|
-
headers: {
|
|
1294
|
-
'Content-Type': 'application/json',
|
|
1295
|
-
Authorization: \`Bearer \${apiKey}\`,
|
|
1296
|
-
},
|
|
1297
|
-
body: JSON.stringify(request.body),
|
|
1298
|
-
});
|
|
1299
|
-
\`\`\`
|
|
1300
|
-
|
|
1301
|
-
### Step-by-step API
|
|
1302
|
-
|
|
1303
|
-
\`\`\`typescript
|
|
1304
|
-
import {
|
|
1305
|
-
parsePrompt,
|
|
1306
|
-
resolveIncludes,
|
|
1307
|
-
applyOverrides,
|
|
1308
|
-
getAdapter,
|
|
1309
|
-
} from 'promptopskit';
|
|
1310
|
-
import { readFileSync } from 'fs';
|
|
1311
|
-
|
|
1312
|
-
// 1. Parse a prompt file
|
|
1313
|
-
const source = readFileSync('./prompts/greeting.md', 'utf-8');
|
|
1314
|
-
const asset = parsePrompt(source, 'greeting.md');
|
|
1315
|
-
|
|
1316
|
-
// 2. Resolve includes
|
|
1317
|
-
const resolved = await resolveIncludes(asset, './prompts');
|
|
1318
|
-
|
|
1319
|
-
// 3. Apply overrides
|
|
1320
|
-
const configured = applyOverrides(resolved, {
|
|
1321
|
-
environment: 'production',
|
|
1322
|
-
tier: 'premium',
|
|
1323
|
-
});
|
|
1324
|
-
|
|
1325
|
-
// 4. Get provider adapter and render
|
|
1326
|
-
const adapter = getAdapter(configured.provider ?? 'openai');
|
|
1327
|
-
const request = adapter.render(configured, {
|
|
1328
|
-
variables: { name: 'Alice' },
|
|
1329
|
-
history: [
|
|
1330
|
-
{ role: 'user', content: 'Previous message' },
|
|
1331
|
-
{ role: 'assistant', content: 'Previous response' },
|
|
1332
|
-
],
|
|
1333
|
-
});
|
|
1334
|
-
\`\`\`
|
|
1335
|
-
|
|
1336
|
-
### Available provider adapters
|
|
1337
|
-
|
|
1338
|
-
| Provider | Import path | Provider request format |
|
|
1339
|
-
|----------|------------|----------------------|
|
|
1340
|
-
| OpenAI | \`promptopskit\` or \`promptopskit/openai\` | Chat Completions API |
|
|
1341
|
-
| Anthropic | \`promptopskit/anthropic\` | Messages API |
|
|
1342
|
-
| Gemini | \`promptopskit/gemini\` | GenerateContent API |
|
|
1343
|
-
| OpenRouter | \`promptopskit/openrouter\` | OpenAI-compatible + extras |
|
|
1344
|
-
|
|
1345
|
-
### Validation
|
|
1346
|
-
|
|
1347
|
-
\`\`\`typescript
|
|
1348
|
-
import { validateAsset, parsePrompt } from 'promptopskit';
|
|
1349
|
-
|
|
1350
|
-
const asset = parsePrompt(source);
|
|
1351
|
-
const result = validateAsset(asset);
|
|
1352
|
-
|
|
1353
|
-
if (!result.valid) {
|
|
1354
|
-
console.error(result.errors); // Error codes: POK001-POK021
|
|
1355
|
-
}
|
|
1356
|
-
\`\`\`
|
|
1357
|
-
|
|
1358
|
-
### Testing helpers
|
|
1359
|
-
|
|
1360
|
-
\`\`\`typescript
|
|
1361
|
-
import { createMockAsset, parseTestPrompt } from 'promptopskit/testing';
|
|
1362
|
-
|
|
1363
|
-
// Create a mock asset for unit tests
|
|
1364
|
-
const mock = createMockAsset({ model: 'gpt-4.1-mini' });
|
|
1365
|
-
|
|
1366
|
-
// Parse an inline prompt string for tests
|
|
1367
|
-
const asset = parseTestPrompt(\`
|
|
1368
|
-
---
|
|
1369
|
-
id: test
|
|
1370
|
-
schema_version: 1
|
|
1371
|
-
provider: openai
|
|
1372
|
-
model: gpt-5.4
|
|
1373
|
-
---
|
|
1374
|
-
|
|
1375
|
-
# Prompt template
|
|
1376
|
-
|
|
1377
|
-
Hello {{ name }}
|
|
1378
|
-
\`);
|
|
1379
|
-
\`\`\`
|
|
1380
|
-
|
|
1381
|
-
---
|
|
1382
|
-
|
|
1383
|
-
## CLI commands
|
|
1384
|
-
|
|
1385
|
-
| Command | Description |
|
|
1386
|
-
|---------|-------------|
|
|
1387
|
-
| \`promptopskit init [dir]\` | Scaffold a prompts directory with starter files (including \`defaults.md\`) |
|
|
1388
|
-
| \`promptopskit validate <dir>\` | Validate all prompt files in a directory |
|
|
1389
|
-
| \`promptopskit compile <src> <out>\` | Compile .md prompts to JSON artifacts |
|
|
1390
|
-
| \`promptopskit render <file> [--set key=value]\` | Render a prompt preview |
|
|
1391
|
-
| \`promptopskit inspect <file>\` | Print the normalized prompt asset |
|
|
1392
|
-
|
|
1393
|
-
---
|
|
1394
|
-
|
|
1395
|
-
## Conventions to follow
|
|
1396
|
-
|
|
1397
|
-
1. **One prompt per file** \u2014 each \`.md\` file is a single prompt asset
|
|
1398
|
-
2. **Always set \`id\` and \`schema_version: 1\`** in front matter (or inherit \`schema_version\` from \`defaults.md\`)
|
|
1399
|
-
3. **Declare all variables** in \`context.inputs\` that appear in templates
|
|
1400
|
-
4. **Use includes** for shared system instructions (tone, safety, formatting)
|
|
1401
|
-
5. **Keep prompt templates focused** \u2014 compose behavior via includes, not duplication
|
|
1402
|
-
6. **Use environment overrides** for dev/staging/prod model differences
|
|
1403
|
-
7. **Add test sidecars** (\`.test.yaml\`) for critical prompts
|
|
1404
|
-
8. **Run \`promptopskit validate\`** before committing changes
|
|
1405
|
-
9. **Use \`defaults.md\`** to share provider, model, metadata, and system instructions across a folder
|
|
1406
|
-
10. **Variable names** should be \`snake_case\`
|
|
1407
|
-
11. **Prompt file names** should be \`kebab-case.md\`
|
|
1408
|
-
`.trimEnd();
|
|
1409
1109
|
|
|
1410
1110
|
// src/cli/index.ts
|
|
1411
1111
|
var HELP7 = `
|