promptopskit 0.2.0 → 0.2.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 CHANGED
@@ -15,7 +15,7 @@ Provider adapters for OpenAI, Anthropic, Gemini, and OpenRouter produce a ready-
15
15
  - **Centralized, not scattered** — each prompt is a single Markdown file that captures prompt text, model config, tool bindings, and context rules together.
16
16
  - **Operational, not just templated** — model name, temperature, reasoning effort, tools, and response format are declared alongside the prompt they govern.
17
17
  - **Reusable, not duplicated** — `includes` lets you define shared tone, policy, or safety instructions once and compose them into any prompt.
18
- - **Layered defaults, not repetition** — `defaults.md` in any folder sets shared `metadata` and `# System instructions` for that subtree, with nearest-folder override behavior.
18
+ - **Layered defaults, not repetition** — `defaults.md` in any folder sets shared `provider`, `model`, `metadata`, and `# System instructions` for that subtree, with nearest-folder override behavior.
19
19
  - **Release-aware, not ad hoc** — environment and tier overrides swap models and parameters without forking prompt files.
20
20
  - **Provider-portable** — write once, render for OpenAI, Anthropic, Gemini, or OpenRouter with correct body shapes.
21
21
  - **Validate early** — Zod schema validation, Levenshtein-based "did you mean?" suggestions for typos, and variable usage checks catch mistakes before runtime.
@@ -40,6 +40,7 @@ This creates:
40
40
 
41
41
  ```
42
42
  prompts/
43
+ ├── defaults.md # Folder-level defaults (provider, model, metadata, system instructions)
43
44
  ├── hello.md # Sample prompt with variables
44
45
  ├── hello.test.yaml # Test sidecar with sample inputs
45
46
  └── shared/
@@ -50,7 +51,7 @@ prompts/
50
51
 
51
52
  ```markdown
52
53
  ---
53
- id: support.reply
54
+ id: support/reply
54
55
  schema_version: 1
55
56
  provider: openai
56
57
  model: gpt-5.4
@@ -107,7 +108,7 @@ const response = await fetch('https://api.openai.com/v1/chat/completions', {
107
108
  - **Prompts as Markdown** — YAML front matter for settings, H1 headings for sections (`# System instructions`, `# Prompt template`, `# Notes`)
108
109
  - **Variable interpolation** — `{{ variable }}` syntax with strict and permissive modes
109
110
  - **Composition** — `includes` to share system instructions across prompts, with circular detection
110
- - **Folder defaults** — `defaults.md` inheritance for shared metadata and system instructions
111
+ - **Folder defaults** — `defaults.md` inheritance for shared provider, model, metadata, and system instructions
111
112
  - **Overrides** — Environment and tier-based overrides (base → env → tier → runtime)
112
113
  - **4 provider adapters** — OpenAI, Anthropic, Gemini, OpenRouter — body-only output
113
114
  - **Validation** — Zod schema validation, Levenshtein-based "did you mean?" for typos, variable usage checks
@@ -169,15 +170,19 @@ Define environment and tier overrides in front matter. Precedence: **base → en
169
170
 
170
171
  ```markdown
171
172
  ---
172
- id: support.reply
173
+ id: support/reply
173
174
  schema_version: 1
174
175
  provider: openai
175
176
  model: gpt-5.4
177
+ reasoning:
178
+ effort: high
176
179
  sampling:
177
180
  temperature: 0.7
178
181
  environments:
179
182
  dev:
180
183
  model: gpt-5.4-mini
184
+ reasoning:
185
+ effort: low
181
186
  sampling:
182
187
  temperature: 0.2
183
188
  prod:
@@ -206,7 +211,7 @@ Share system instructions across prompts using `includes`. Included system instr
206
211
 
207
212
  ```markdown
208
213
  ---
209
- id: support.reply
214
+ id: support/reply
210
215
  schema_version: 1
211
216
  includes:
212
217
  - ./shared/tone.md
@@ -221,6 +226,7 @@ Handle support requests carefully.
221
226
 
222
227
  Define a `defaults.md` file in `prompts/` (and optional subfolders) to provide inherited defaults for prompts:
223
228
 
229
+ - Shared `provider` and `model` in front matter
224
230
  - Shared `metadata` defaults in front matter
225
231
  - Shared `# System instructions` in body
226
232
  - Nearest subfolder `defaults.md` overrides parent defaults
@@ -255,32 +261,30 @@ promptopskit render <file> [--env <name>] [--tier <name>] [--vars <file>] [--jso
255
261
  # Print normalized asset as JSON
256
262
  promptopskit inspect <file>
257
263
 
258
- # Deploy AI agent instructions into your project
259
- promptopskit skill [--target copilot|cursor|generic] [--force]
264
+ # Deploy AI agent instructions for all major coding assistants
265
+ promptopskit skill [--target agents|claude|copilot|cursor] [--force]
260
266
  ```
261
267
 
262
268
  ## AI Agent Instructions
263
269
 
264
- The `skill` command deploys a comprehensive instructions file so AI coding assistants (GitHub Copilot, Cursor, etc.) automatically understand how to create and manage prompts with promptopskit.
270
+ The `skill` command deploys instruction files so AI coding assistants automatically understand how to create and manage prompts with promptopskit. By default it generates files for **all** major vendors:
265
271
 
266
272
  ```bash
267
- # Deploy for GitHub Copilot (default)
273
+ # Deploy for all AI coding assistants (default)
268
274
  promptopskit skill
269
- # → .github/instructions/promptopskit.instructions.md
270
-
271
- # Deploy for Cursor
272
- promptopskit skill --target cursor
273
- # → .cursor/rules/promptopskit.mdc
275
+ # → AGENTS.md (Codex, OpenCode, Cursor, Copilot)
276
+ # → CLAUDE.md (Claude Code — imports AGENTS.md)
277
+ # .github/instructions/promptopskit.instructions.md (GitHub Copilot)
278
+ # .cursor/rules/promptopskit.mdc (Cursor)
274
279
 
275
- # Deploy a generic instructions file
276
- promptopskit skill --target generic
277
- # → .ai/promptopskit-skill.md
280
+ # Deploy only a specific target
281
+ promptopskit skill --target copilot
278
282
 
279
- # Overwrite an existing instructions file
283
+ # Overwrite existing instructions files
280
284
  promptopskit skill --force
281
285
  ```
282
286
 
283
- The deployed file covers the prompt format, front matter schema, variable interpolation, includes, overrides, the TypeScript API, provider adapters, and project conventions — everything an AI agent needs to write correct prompts on the first try.
287
+ The `CLAUDE.md` file uses Claude Code's `@AGENTS.md` import syntax to avoid duplicating content. The deployed files cover the prompt format, front matter schema, variable interpolation, includes, overrides, the TypeScript API, provider adapters, and project conventions — everything an AI agent needs to write correct prompts on the first try.
284
288
 
285
289
  ## Inline Source
286
290
 
@@ -63,6 +63,8 @@ var SectionsSchema = z.object({
63
63
  notes: z.string().optional()
64
64
  });
65
65
  var PromptDefaultsSchema = z.object({
66
+ provider: z.enum(["openai", "anthropic", "google", "gemini", "openrouter", "any"]).optional(),
67
+ model: z.string().optional(),
66
68
  metadata: MetadataSchema.optional(),
67
69
  sections: z.object({
68
70
  system_instructions: z.string().optional()
@@ -97,7 +99,7 @@ var SECTION_MAP = {
97
99
  "notes": "notes"
98
100
  };
99
101
  function extractSections(body) {
100
- const lines = body.split("\n");
102
+ const lines = body.split(/\r?\n/);
101
103
  const sections = {};
102
104
  let currentKey = null;
103
105
  let currentLines = [];
@@ -206,6 +208,8 @@ function parseDefaults(content) {
206
208
  }
207
209
  function mergeDefaults(base, local) {
208
210
  return {
211
+ provider: local.provider ?? base.provider,
212
+ model: local.model ?? base.model,
209
213
  metadata: {
210
214
  ...base.metadata ?? {},
211
215
  ...local.metadata ?? {}
@@ -219,7 +223,8 @@ function mergeDefaults(base, local) {
219
223
  function applyDefaults(asset, defaults) {
220
224
  const hasDefaultMetadata = defaults.metadata && Object.keys(defaults.metadata).length > 0;
221
225
  const hasDefaultSystem = !!defaults.sections?.system_instructions;
222
- if (!hasDefaultMetadata && !hasDefaultSystem) {
226
+ const hasDefaultScalars = defaults.provider !== void 0 || defaults.model !== void 0;
227
+ if (!hasDefaultMetadata && !hasDefaultSystem && !hasDefaultScalars) {
223
228
  return asset;
224
229
  }
225
230
  const mergedMetadata = {
@@ -231,6 +236,8 @@ function applyDefaults(asset, defaults) {
231
236
  const sections = asset.sections ? { ...asset.sections, system_instructions: systemInstructions } : systemInstructions ? { system_instructions: systemInstructions } : void 0;
232
237
  return {
233
238
  ...asset,
239
+ provider: asset.provider ?? defaults.provider,
240
+ model: asset.model ?? defaults.model,
234
241
  metadata,
235
242
  sections
236
243
  };
@@ -243,4 +250,4 @@ export {
243
250
  parsePrompt,
244
251
  loadPromptFile
245
252
  };
246
- //# sourceMappingURL=chunk-QDGJFINW.js.map
253
+ //# sourceMappingURL=chunk-CX6KVBW4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/schema/schema.ts","../src/parser/sections.ts","../src/parser/parser.ts","../src/parser/loader.ts"],"sourcesContent":["import { z } from 'zod';\n\n// --- Tool definitions ---\n\nexport const InlineToolDefSchema = z.object({\n name: z.string(),\n description: z.string().optional(),\n input_schema: z.record(z.unknown()).optional(),\n});\n\nexport type InlineToolDef = z.infer<typeof InlineToolDefSchema>;\n\nexport const ToolRefSchema = z.union([z.string(), InlineToolDefSchema]);\n\n// --- MCP ---\n\nexport const MCPServerRefSchema = z.union([\n z.string(),\n z.object({\n name: z.string(),\n config: z.record(z.unknown()).optional(),\n }),\n]);\n\nexport type MCPServerRef = z.infer<typeof MCPServerRefSchema>;\n\n// --- Reasoning ---\n\nexport const ReasoningSchema = z.object({\n effort: z.enum(['low', 'medium', 'high']).optional(),\n budget_tokens: z.number().int().positive().optional(),\n});\n\n// --- Sampling ---\n\nexport const SamplingSchema = z.object({\n temperature: z.number().min(0).max(2).optional(),\n top_p: z.number().min(0).max(1).optional(),\n frequency_penalty: z.number().optional(),\n presence_penalty: z.number().optional(),\n stop: z.array(z.string()).optional(),\n max_output_tokens: z.number().int().positive().optional(),\n});\n\n// --- Response ---\n\nexport const ResponseSchema = z.object({\n format: z.enum(['text', 'json', 'markdown']).optional(),\n stream: z.boolean().optional(),\n});\n\n// --- Context ---\n\nexport const HistorySchema = z.object({\n max_items: z.number().int().positive().optional(),\n});\n\nexport const ContextSchema = z.object({\n inputs: z.array(z.string()).optional(),\n history: HistorySchema.optional(),\n});\n\n// --- Metadata ---\n\nexport const MetadataSchema = z.object({\n owner: z.string().optional(),\n tags: z.array(z.string()).optional(),\n review_required: z.boolean().optional(),\n stable: z.boolean().optional(),\n});\n\n// --- MCP block ---\n\nexport const MCPSchema = z.object({\n servers: z.array(MCPServerRefSchema).optional(),\n});\n\n// --- Overrides (subset allowed in environments/tiers) ---\n\nexport const PromptAssetOverridesSchema = z.object({\n model: z.string().optional(),\n fallback_models: z.array(z.string()).optional(),\n reasoning: ReasoningSchema.optional(),\n sampling: SamplingSchema.optional(),\n response: ResponseSchema.optional(),\n tools: z.array(ToolRefSchema).optional(),\n});\n\nexport type PromptAssetOverrides = z.infer<typeof PromptAssetOverridesSchema>;\n\n// --- Source tracking ---\n\nexport const SourceSchema = z.object({\n file_path: z.string().optional(),\n checksum: z.string().optional(),\n});\n\n// --- Sections (populated by parser) ---\n\nexport const SectionsSchema = z.object({\n system_instructions: z.string().optional(),\n prompt_template: z.string().optional(),\n notes: z.string().optional(),\n});\n\n// --- Defaults files (folder-level inheritance) ---\n\nexport const PromptDefaultsSchema = z.object({\n provider: z.enum(['openai', 'anthropic', 'google', 'gemini', 'openrouter', 'any']).optional(),\n model: z.string().optional(),\n metadata: MetadataSchema.optional(),\n sections: z.object({\n system_instructions: z.string().optional(),\n }).optional(),\n});\n\nexport type PromptDefaults = z.infer<typeof PromptDefaultsSchema>;\n\n// --- Top-level PromptAsset ---\n\nexport const PromptAssetSchema = z.object({\n id: z.string(),\n schema_version: z.number().int().positive().default(1),\n description: z.string().optional(),\n\n provider: z.enum(['openai', 'anthropic', 'google', 'gemini', 'openrouter', 'any']).optional(),\n model: z.string().optional(),\n fallback_models: z.array(z.string()).optional(),\n\n reasoning: ReasoningSchema.optional(),\n sampling: SamplingSchema.optional(),\n response: ResponseSchema.optional(),\n\n tools: z.array(ToolRefSchema).optional(),\n mcp: MCPSchema.optional(),\n\n context: ContextSchema.optional(),\n\n includes: z.array(z.string()).optional(),\n\n environments: z.record(PromptAssetOverridesSchema).optional(),\n tiers: z.record(PromptAssetOverridesSchema).optional(),\n\n metadata: MetadataSchema.optional(),\n\n // Populated by parser, not authored in YAML\n sections: SectionsSchema.optional(),\n source: SourceSchema.optional(),\n});\n\nexport type PromptAsset = z.infer<typeof PromptAssetSchema>;\n\n// --- Resolved asset (after includes, overrides applied) ---\n\nexport interface ResolvedPromptAsset extends PromptAsset {\n sections: {\n system_instructions?: string;\n prompt_template?: string;\n notes?: string;\n };\n source: {\n file_path?: string;\n checksum?: string;\n };\n}\n","export interface Sections {\n system_instructions?: string;\n prompt_template?: string;\n notes?: string;\n}\n\nconst SECTION_MAP: Record<string, keyof Sections> = {\n 'system instructions': 'system_instructions',\n 'prompt template': 'prompt_template',\n 'notes': 'notes',\n};\n\n/**\n * Extract named sections from the markdown body using H1 headings.\n * Case-insensitive. H2+ within a section is treated as content.\n * If no H1 headings are found, the entire body is treated as prompt_template.\n */\nexport function extractSections(body: string): Sections {\n const lines = body.split(/\\r?\\n/);\n const sections: Sections = {};\n\n let currentKey: keyof Sections | null = null;\n let currentLines: string[] = [];\n let foundAnyH1 = false;\n\n for (const line of lines) {\n const h1Match = line.match(/^#\\s+(.+)$/);\n if (h1Match) {\n // Flush previous section\n if (currentKey) {\n sections[currentKey] = currentLines.join('\\n').trim();\n }\n\n foundAnyH1 = true;\n const heading = h1Match[1].trim().toLowerCase();\n currentKey = SECTION_MAP[heading] ?? null;\n currentLines = [];\n } else {\n currentLines.push(line);\n }\n }\n\n // Flush last section\n if (currentKey) {\n sections[currentKey] = currentLines.join('\\n').trim();\n }\n\n // If no H1 headings found, treat entire body as prompt_template\n if (!foundAnyH1) {\n const trimmed = body.trim();\n if (trimmed) {\n sections.prompt_template = trimmed;\n }\n }\n\n return sections;\n}\n","import matter from 'gray-matter';\nimport { PromptAssetSchema } from '../schema/index.js';\nimport type { PromptAsset } from '../schema/index.js';\nimport { extractSections } from './sections.js';\n\nexport interface ParseResult {\n asset: PromptAsset;\n raw: {\n frontMatter: Record<string, unknown>;\n body: string;\n };\n}\n\n/**\n * Parse a prompt markdown string (YAML front matter + markdown body)\n * into a validated PromptAsset.\n */\nexport function parsePrompt(content: string, filePath?: string): ParseResult {\n const { data: frontMatter, content: body } = matter(content);\n\n const sections = extractSections(body);\n\n const raw = {\n ...frontMatter,\n sections,\n source: filePath ? { file_path: filePath } : undefined,\n };\n\n const asset = PromptAssetSchema.parse(raw);\n\n return {\n asset,\n raw: {\n frontMatter: frontMatter as Record<string, unknown>,\n body,\n },\n };\n}\n","import { readFile } from 'node:fs/promises';\nimport { dirname, join, resolve } from 'node:path';\nimport matter from 'gray-matter';\nimport { parsePrompt } from './parser.js';\nimport type { ParseResult } from './parser.js';\nimport { extractSections } from './sections.js';\nimport { PromptDefaultsSchema } from '../schema/index.js';\nimport type { PromptDefaults } from '../schema/index.js';\n\nconst DEFAULTS_FILE_NAME = 'defaults.md';\n\nexport interface LoadPromptOptions {\n /**\n * Optional boundary directory for defaults discovery.\n * If provided, defaults are loaded from this directory down to the prompt directory.\n */\n defaultsRoot?: string;\n}\n\n/**\n * Load and parse a prompt file from disk.\n */\nexport async function loadPromptFile(filePath: string, options: LoadPromptOptions = {}): Promise<ParseResult> {\n const content = await readFile(filePath, 'utf-8');\n const parsed = parsePrompt(content, filePath);\n // Default the boundary to the file's own directory so traversal never\n // walks above the prompt tree when no explicit root is provided.\n const root = options.defaultsRoot ?? dirname(filePath);\n const defaults = await loadDefaultsForPath(filePath, root);\n const asset = applyDefaults(parsed.asset, defaults);\n\n return {\n ...parsed,\n asset,\n };\n}\n\nasync function loadDefaultsForPath(filePath: string, defaultsRoot?: string): Promise<PromptDefaults> {\n const directories = getDirectoriesToCheck(filePath, defaultsRoot);\n let merged: PromptDefaults = {};\n\n for (const dir of directories) {\n const defaultsPath = join(dir, DEFAULTS_FILE_NAME);\n try {\n const defaultsContent = await readFile(defaultsPath, 'utf-8');\n const defaults = parseDefaults(defaultsContent);\n merged = mergeDefaults(merged, defaults);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw error;\n }\n }\n }\n\n return merged;\n}\n\nfunction getDirectoriesToCheck(filePath: string, defaultsRoot?: string): string[] {\n const dirs: string[] = [];\n let current = resolve(dirname(filePath));\n const boundary = defaultsRoot ? resolve(defaultsRoot) : undefined;\n\n while (true) {\n dirs.unshift(current);\n if ((boundary && current === boundary) || current === dirname(current)) {\n break;\n }\n current = dirname(current);\n }\n\n return dirs;\n}\n\nfunction parseDefaults(content: string): PromptDefaults {\n const { data: frontMatter, content: body } = matter(content);\n const sections = extractSections(body);\n\n return PromptDefaultsSchema.parse({\n ...frontMatter,\n sections: {\n system_instructions: sections.system_instructions,\n },\n });\n}\n\nfunction mergeDefaults(base: PromptDefaults, local: PromptDefaults): PromptDefaults {\n return {\n provider: local.provider ?? base.provider,\n model: local.model ?? base.model,\n metadata: {\n ...(base.metadata ?? {}),\n ...(local.metadata ?? {}),\n },\n sections: {\n ...(base.sections ?? {}),\n ...(local.sections ?? {}),\n },\n };\n}\n\nfunction applyDefaults(asset: ParseResult['asset'], defaults: PromptDefaults): ParseResult['asset'] {\n const hasDefaultMetadata = defaults.metadata && Object.keys(defaults.metadata).length > 0;\n const hasDefaultSystem = !!defaults.sections?.system_instructions;\n const hasDefaultScalars = defaults.provider !== undefined\n || defaults.model !== undefined;\n\n // Short-circuit: nothing to merge\n if (!hasDefaultMetadata && !hasDefaultSystem && !hasDefaultScalars) {\n return asset;\n }\n\n const mergedMetadata = {\n ...(defaults.metadata ?? {}),\n ...(asset.metadata ?? {}),\n };\n const metadata = Object.keys(mergedMetadata).length > 0 ? mergedMetadata : undefined;\n\n const systemInstructions = asset.sections?.system_instructions ?? defaults.sections?.system_instructions;\n\n const sections = asset.sections\n ? { ...asset.sections, system_instructions: systemInstructions }\n : systemInstructions\n ? { system_instructions: systemInstructions }\n : undefined;\n\n return {\n ...asset,\n provider: asset.provider ?? defaults.provider,\n model: asset.model ?? defaults.model,\n metadata,\n sections,\n };\n}\n"],"mappings":"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","names":["matter","matter"]}
package/dist/cli/index.js CHANGED
@@ -72,6 +72,8 @@ var SectionsSchema = z.object({
72
72
  notes: z.string().optional()
73
73
  });
74
74
  var PromptDefaultsSchema = z.object({
75
+ provider: z.enum(["openai", "anthropic", "google", "gemini", "openrouter", "any"]).optional(),
76
+ model: z.string().optional(),
75
77
  metadata: MetadataSchema.optional(),
76
78
  sections: z.object({
77
79
  system_instructions: z.string().optional()
@@ -106,7 +108,7 @@ var SECTION_MAP = {
106
108
  "notes": "notes"
107
109
  };
108
110
  function extractSections(body) {
109
- const lines = body.split("\n");
111
+ const lines = body.split(/\r?\n/);
110
112
  const sections = {};
111
113
  let currentKey = null;
112
114
  let currentLines = [];
@@ -214,6 +216,8 @@ function parseDefaults(content) {
214
216
  }
215
217
  function mergeDefaults(base, local) {
216
218
  return {
219
+ provider: local.provider ?? base.provider,
220
+ model: local.model ?? base.model,
217
221
  metadata: {
218
222
  ...base.metadata ?? {},
219
223
  ...local.metadata ?? {}
@@ -227,7 +231,8 @@ function mergeDefaults(base, local) {
227
231
  function applyDefaults(asset, defaults) {
228
232
  const hasDefaultMetadata = defaults.metadata && Object.keys(defaults.metadata).length > 0;
229
233
  const hasDefaultSystem = !!defaults.sections?.system_instructions;
230
- if (!hasDefaultMetadata && !hasDefaultSystem) {
234
+ const hasDefaultScalars = defaults.provider !== void 0 || defaults.model !== void 0;
235
+ if (!hasDefaultMetadata && !hasDefaultSystem && !hasDefaultScalars) {
231
236
  return asset;
232
237
  }
233
238
  const mergedMetadata = {
@@ -239,6 +244,8 @@ function applyDefaults(asset, defaults) {
239
244
  const sections = asset.sections ? { ...asset.sections, system_instructions: systemInstructions } : systemInstructions ? { system_instructions: systemInstructions } : void 0;
240
245
  return {
241
246
  ...asset,
247
+ provider: asset.provider ?? defaults.provider,
248
+ model: asset.model ?? defaults.model,
242
249
  metadata,
243
250
  sections
244
251
  };
@@ -796,18 +803,19 @@ Options:
796
803
  `.trim();
797
804
  var HELLO_PROMPT = `---
798
805
  id: hello
799
- schema_version: 1
800
- provider: openai
801
- model: gpt-5.4
802
806
  context:
803
807
  inputs:
804
808
  - name
805
809
  - app_context
806
810
  includes:
807
811
  - ./shared/tone.md
812
+ reasoning:
813
+ effort: high
808
814
  environments:
809
815
  dev:
810
816
  model: gpt-5.4-mini
817
+ reasoning:
818
+ effort: low
811
819
  sampling:
812
820
  temperature: 0.2
813
821
  ---
@@ -822,7 +830,7 @@ Current app context: {{ app_context }}.
822
830
  Say hello to {{ name }} and ask how you can help them today.
823
831
  `.trimStart();
824
832
  var TONE_INCLUDE = `---
825
- id: shared.tone
833
+ id: shared/tone
826
834
  schema_version: 1
827
835
  ---
828
836
 
@@ -831,6 +839,8 @@ schema_version: 1
831
839
  Always be polite, professional, and concise. Avoid jargon unless the user uses it first.
832
840
  `.trimStart();
833
841
  var DEFAULTS = `---
842
+ provider: openai
843
+ model: gpt-5.4
834
844
  metadata:
835
845
  owner: my-team
836
846
  review_required: true
@@ -851,7 +861,7 @@ var TEST_SIDECAR = `cases:
851
861
  app_context: "Settings page"
852
862
  `;
853
863
  var EXAMPLE_USAGE = `// Example: render the hello prompt and send it to OpenAI
854
- // Full docs: https://promptopskit.com/docs/#/getting-started
864
+ // Full docs: https://promptopskit.com/docs/index.html#/
855
865
 
856
866
  import { createPromptOpsKit } from 'promptopskit';
857
867
 
@@ -859,8 +869,8 @@ async function main() {
859
869
  const kit = createPromptOpsKit({ sourceDir: './prompts' });
860
870
 
861
871
  // Determine environment from ENV var (defaults to 'dev')
862
- // - dev: uses gpt-5.4-mini with temperature 0.2 (see hello.md environments)
863
- // - production: uses base model gpt-5.4 with default settings
872
+ // - dev: uses gpt-5.4-mini, low reasoning effort, temperature 0.2
873
+ // - production: uses base model gpt-5.4 with high reasoning effort
864
874
  const environment = process.env.NODE_ENV === 'production' ? 'prod' : 'dev';
865
875
 
866
876
  const { request } = await kit.renderPrompt({
@@ -873,6 +883,28 @@ async function main() {
873
883
  },
874
884
  });
875
885
 
886
+ // request.body is the fully transformed OpenAI Chat Completions payload.
887
+ // For the hello.md prompt in the dev environment it looks like:
888
+ //
889
+ // {
890
+ // "model": "gpt-5.4-mini",
891
+ // "reasoning_effort": "low",
892
+ // "temperature": 0.2,
893
+ // "messages": [
894
+ // {
895
+ // "role": "system",
896
+ // "content": "You are a friendly assistant. Be helpful and concise.
897
+ Current app context: Welcome screen.
898
+
899
+ Always be polite, professional, and concise. Avoid jargon unless the user uses it first."
900
+ // },
901
+ // {
902
+ // "role": "user",
903
+ // "content": "Say hello to World and ask how you can help them today."
904
+ // }
905
+ // ]
906
+ // }
907
+
876
908
  console.log('Model:', request.body.model);
877
909
 
878
910
  const res = await fetch('https://api.openai.com/v1/chat/completions', {
@@ -941,17 +973,27 @@ promptopskit skill [--target <target>] [--force]
941
973
  Deploy AI agent instructions so coding assistants know how to
942
974
  create and manage prompts using promptopskit.
943
975
 
944
- Targets:
945
- copilot (default) .github/instructions/promptopskit.instructions.md
946
- cursor .cursor/rules/promptopskit.mdc
947
- generic .ai/promptopskit-skill.md
976
+ By default, generates files for all major AI coding assistants:
977
+
978
+ AGENTS.md Codex, OpenCode, Cursor, Copilot
979
+ CLAUDE.md Claude Code (imports AGENTS.md)
980
+ .github/instructions/promptopskit.instructions.md GitHub Copilot (path-specific)
981
+ .cursor/rules/promptopskit.mdc Cursor (project rule)
948
982
 
949
983
  Options:
950
- --target, -t Target AI tool (copilot, cursor, generic)
951
- --force, -f Overwrite existing file
984
+ --target, -t Deploy only a specific target (agents, claude, copilot, cursor)
985
+ --force, -f Overwrite existing files
952
986
  --help, -h Show this help
953
987
  `.trim();
954
988
  var TARGETS = {
989
+ agents: {
990
+ path: "AGENTS.md",
991
+ wrap: (content) => content
992
+ },
993
+ claude: {
994
+ path: "CLAUDE.md",
995
+ wrap: () => "@AGENTS.md\n"
996
+ },
955
997
  copilot: {
956
998
  path: ".github/instructions/promptopskit.instructions.md",
957
999
  wrap: (content) => `---
@@ -969,40 +1011,45 @@ alwaysApply: true
969
1011
  ---
970
1012
 
971
1013
  ${content}`
972
- },
973
- generic: {
974
- path: ".ai/promptopskit-skill.md",
975
- wrap: (content) => content
976
1014
  }
977
1015
  };
1016
+ var ALL_TARGETS = ["agents", "claude", "copilot", "cursor"];
978
1017
  async function skill(args) {
979
1018
  if (args.includes("--help") || args.includes("-h")) {
980
1019
  console.log(HELP6);
981
1020
  return;
982
1021
  }
983
1022
  const force = args.includes("--force") || args.includes("-f");
984
- let target = "copilot";
1023
+ let targets = ALL_TARGETS;
985
1024
  const targetIdx = args.findIndex((a) => a === "--target" || a === "-t");
986
1025
  if (targetIdx !== -1 && args[targetIdx + 1]) {
987
- target = args[targetIdx + 1];
988
- }
989
- const config = TARGETS[target];
990
- if (!config) {
991
- console.error(`Unknown target: ${target}`);
992
- console.error(`Valid targets: ${Object.keys(TARGETS).join(", ")}`);
993
- process.exit(1);
1026
+ const target = args[targetIdx + 1];
1027
+ const config = TARGETS[target];
1028
+ if (!config) {
1029
+ console.error(`Unknown target: ${target}`);
1030
+ console.error(`Valid targets: ${Object.keys(TARGETS).join(", ")}`);
1031
+ process.exit(1);
1032
+ }
1033
+ targets = [target];
1034
+ }
1035
+ let written = 0;
1036
+ for (const target of targets) {
1037
+ const config = TARGETS[target];
1038
+ const filePath = config.path;
1039
+ if (existsSync3(filePath) && !force) {
1040
+ console.log(` skip ${filePath} (already exists, use --force to overwrite)`);
1041
+ continue;
1042
+ }
1043
+ const content = config.wrap(SKILL_CONTENT);
1044
+ await mkdir3(dirname5(filePath), { recursive: true });
1045
+ await writeFile3(filePath, content, "utf-8");
1046
+ console.log(` \u2713 ${filePath}`);
1047
+ written++;
994
1048
  }
995
- const filePath = config.path;
996
- if (existsSync3(filePath) && !force) {
997
- console.log(` skip ${filePath} (already exists, use --force to overwrite)`);
998
- return;
1049
+ if (written > 0) {
1050
+ console.log();
1051
+ console.log(`AI agents will now understand how to create and manage prompts with promptopskit.`);
999
1052
  }
1000
- const content = config.wrap(SKILL_CONTENT);
1001
- await mkdir3(dirname5(filePath), { recursive: true });
1002
- await writeFile3(filePath, content, "utf-8");
1003
- console.log(` \u2713 ${filePath}`);
1004
- console.log();
1005
- console.log(`AI agents will now understand how to create and manage prompts with promptopskit.`);
1006
1053
  }
1007
1054
  var SKILL_CONTENT = `# promptopskit \u2014 Prompt Engineering Skill
1008
1055
 
@@ -1122,16 +1169,21 @@ folder:
1122
1169
 
1123
1170
  \`\`\`text
1124
1171
  prompts/
1125
- \u251C\u2500\u2500 defaults.md # global metadata + system instructions
1172
+ \u251C\u2500\u2500 defaults.md # global provider, model, metadata + system instructions
1126
1173
  \u2514\u2500\u2500 support/
1127
1174
  \u251C\u2500\u2500 defaults.md # overrides for support/*
1128
1175
  \u2514\u2500\u2500 reply.md # inherits from support/defaults.md
1129
1176
  \`\`\`
1130
1177
 
1131
1178
  Supported default fields:
1179
+ - \`provider\` (front matter) \u2014 default provider for the folder
1180
+ - \`model\` (front matter) \u2014 default model for the folder
1132
1181
  - \`metadata\` (front matter) \u2014 merged with prompt-local metadata
1133
1182
  - \`# System instructions\` (body section) \u2014 used when the prompt has none
1134
1183
 
1184
+ This lets you configure app-wide settings like \`provider\` and \`model\`
1185
+ in a single root \`defaults.md\`, so individual prompts only declare what\u2019s unique to them.
1186
+
1135
1187
  Rules:
1136
1188
  - Nearest subfolder \`defaults.md\` overrides parent defaults
1137
1189
  - Prompt-local values always take precedence over defaults
@@ -1149,10 +1201,14 @@ Override model settings per environment or tier:
1149
1201
  environments:
1150
1202
  development:
1151
1203
  model: gpt-4.1-mini
1204
+ reasoning:
1205
+ effort: low
1152
1206
  sampling:
1153
1207
  temperature: 0.9
1154
1208
  production:
1155
1209
  model: gpt-5.4
1210
+ reasoning:
1211
+ effort: high
1156
1212
  sampling:
1157
1213
  temperature: 0.3
1158
1214
 
@@ -1312,14 +1368,14 @@ Hello {{ name }}
1312
1368
  ## Conventions to follow
1313
1369
 
1314
1370
  1. **One prompt per file** \u2014 each \`.md\` file is a single prompt asset
1315
- 2. **Always set \`id\` and \`schema_version: 1\`** in front matter
1371
+ 2. **Always set \`id\` and \`schema_version: 1\`** in front matter (or inherit \`schema_version\` from \`defaults.md\`)
1316
1372
  3. **Declare all variables** in \`context.inputs\` that appear in templates
1317
1373
  4. **Use includes** for shared system instructions (tone, safety, formatting)
1318
1374
  5. **Keep prompt templates focused** \u2014 compose behavior via includes, not duplication
1319
1375
  6. **Use environment overrides** for dev/staging/prod model differences
1320
1376
  7. **Add test sidecars** (\`.test.yaml\`) for critical prompts
1321
1377
  8. **Run \`promptopskit validate\`** before committing changes
1322
- 9. **Use \`defaults.md\`** to share metadata and system instructions across a folder
1378
+ 9. **Use \`defaults.md\`** to share provider, model, metadata, and system instructions across a folder
1323
1379
  10. **Variable names** should be \`snake_case\`
1324
1380
  11. **Prompt file names** should be \`kebab-case.md\`
1325
1381
  `.trimEnd();