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/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 (declaredInputs.size > 0 && !declaredInputs.has(v)) {
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: () => "@AGENTS.md\n"
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
- const markedContent = config.wrap(wrapMarkers(SKILL_CONTENT));
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 = `