Type Generation
The promptly generate CLI command fetches all your prompts and composers from the API and generates a TypeScript declaration file with typed template variables for every prompt and composer ID.
Run codegen
Section titled “Run codegen”bunx promptly generatenpx promptly generateyarn dlx promptly generatepnpm dlx promptly generateThis generates a promptly-env.d.ts declaration file. The CLI automatically places it in src/types/ or types/ if either directory exists in your project, otherwise it falls back to the project root.
Generated output
Section titled “Generated output”The generated file uses declaration merging to augment the PromptVariableMap interface. When composers are present, it also augments ComposerVariableMap (input variables) and ComposerPromptMap (named prompt keys):
// Auto-generated by @promptlycms/prompts - do not editimport '@promptlycms/prompts';
declare module '@promptlycms/prompts' { interface PromptVariableMap { 'review-prompt': { [V in 'latest' | '2.0.0' | '1.0.0']: { pickupLocation: string; items: string; }; }; 'welcome-email': { [V in 'latest' | '1.0.0']: { email: string; subject: string; }; }; } interface ComposerVariableMap { 'translation-pipeline': { [V in 'latest' | '1.0.0']: { text: string; targetLang: string; }; }; } interface ComposerPromptMap { 'translation-pipeline': 'translatePrompt' | 'reviewPrompt'; }}What this gives you
Section titled “What this gives you”With the generated file present, you get:
- Prompt ID autocomplete -
getPrompt('suggests all known prompt IDs - Composer ID autocomplete -
getComposer('suggests all known composer IDs - Typed template variables -
result.userMessage({ ... })only accepts the correct variable keys - Typed composer inputs -
getComposer('id', { input: { ... } })only accepts the correct input keys - Typed prompt names - destructured prompt names on
ComposerResultare checked at compile time - Version-aware types - different versions can have different variables, and the types track this
- Fallback for unknown IDs - untyped prompt or composer IDs fall back to
Record<string, string>
Before codegen
Section titled “Before codegen”// No autocomplete, no type checking on variablesconst result = await promptly.getPrompt('review-prompt');result.userMessage({ anything: 'goes' }); // No errorAfter codegen
Section titled “After codegen”// Autocomplete for prompt ID and variablesconst result = await promptly.getPrompt('review-prompt');
result.userMessage({ pickupLocation: 'London', // Typed items: 'sofa', // Typed});
result.userMessage({ wrong: 'key', // Type error});Output location
Section titled “Output location”By default, the CLI auto-detects the best location for the generated file:
| Priority | Directory | Output path |
|---|---|---|
| 1 | src/types/ exists | src/types/promptly-env.d.ts |
| 2 | types/ exists | types/promptly-env.d.ts |
| 3 | Fallback | ./promptly-env.d.ts |
To override, use the --output (-o) flag:
npx promptly generate --output ./custom/path/promptly-env.d.tsPass API key directly
Section titled “Pass API key directly”If you don’t have PROMPTLY_API_KEY set in your environment:
npx promptly generate --api-key pk_live_...Best practices
Section titled “Best practices”- Re-run codegen whenever you add, remove, or rename template variables in the CMS
- Commit the generated file so types are available without running codegen
- Add to your build script if you want to always regenerate on deploy
How it works
Section titled “How it works”Under the hood, promptly generate:
- Reads
PROMPTLY_API_KEYfrom the environment (or--api-keyflag) - Calls
GET /prompts?include_versions=trueandGET /composers?include_versions=truein parallel - Extracts
${variable}template patterns from each prompt’suserMessageand each composer’s prompt segments - Extracts prompt names from composer segments and converts them to camelCase keys
- Groups versions with identical variables together
- Sorts versions (latest first, then semver descending)
- Writes a
declare module '@promptlycms/prompts'augmentation file - Warns about any missing AI provider packages
The PromptVariableMap, ComposerVariableMap, and ComposerPromptMap interfaces are intentionally empty in the source package - they exist only as declaration merging targets. This is the same pattern used by Prisma and GraphQL Code Generator.
Next steps
Section titled “Next steps”- Learn about fetching prompts with typed results
- See the full CLI reference for all flags