Skip to content

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.

Terminal window
bunx promptly generate

This 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.

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):

promptly-env.d.ts
// Auto-generated by @promptlycms/prompts - do not edit
import '@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';
}
}

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 ComposerResult are 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>
// No autocomplete, no type checking on variables
const result = await promptly.getPrompt('review-prompt');
result.userMessage({ anything: 'goes' }); // No error
// Autocomplete for prompt ID and variables
const result = await promptly.getPrompt('review-prompt');
result.userMessage({
pickupLocation: 'London', // Typed
items: 'sofa', // Typed
});
result.userMessage({
wrong: 'key', // Type error
});

By default, the CLI auto-detects the best location for the generated file:

PriorityDirectoryOutput path
1src/types/ existssrc/types/promptly-env.d.ts
2types/ existstypes/promptly-env.d.ts
3Fallback./promptly-env.d.ts

To override, use the --output (-o) flag:

Terminal window
npx promptly generate --output ./custom/path/promptly-env.d.ts

If you don’t have PROMPTLY_API_KEY set in your environment:

Terminal window
npx promptly generate --api-key pk_live_...
  • 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

Under the hood, promptly generate:

  1. Reads PROMPTLY_API_KEY from the environment (or --api-key flag)
  2. Calls GET /prompts?include_versions=true and GET /composers?include_versions=true in parallel
  3. Extracts ${variable} template patterns from each prompt’s userMessage and each composer’s prompt segments
  4. Extracts prompt names from composer segments and converts them to camelCase keys
  5. Groups versions with identical variables together
  6. Sorts versions (latest first, then semver descending)
  7. Writes a declare module '@promptlycms/prompts' augmentation file
  8. 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.