npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

promptfmt

v0.1.2

Published

A composable prompt formatting library with runtime parameter substitution and conditional logic

Readme

promptfmt

A composable prompt formatting library with runtime parameter substitution and conditional logic.

Features

  • Component-based architecture: Build prompts from composable components (role, goal, input, output, context, persona, tone, few-shots, guardrails, constraints, tasks, steps)
  • Runtime parameter substitution: Use template strings with ${paramName} syntax
  • Conditional logic: Include/exclude components based on runtime conditions
  • Fluent API: Chain methods for intuitive prompt building
  • Type-safe: Full TypeScript support

Installation

npm install promptfmt

Quick Start

import { PromptBuilder, createCondition } from 'promptfmt';

const prompt = new PromptBuilder()
  .role('You are a wise numerology guide')
  .goal('Generate a numerology interpretation for ${userName}')
  .input({
    name: '${userName}',
    birthday: '${birthday}',
    numberType: '${numberType}',
    numberValue: '${numberValue}'
  })
  .persona((params) => {
    if (params.age > 10) {
      return 'The Steady Anchor persona';
    }
    return 'The Clear Mirror persona';
  })
  .output('Write ${maxSentences} sentences')
  .constraints('Do not include predictions')
  .build({ 
    userName: 'John',
    birthday: '1990-01-01',
    numberType: 'lifePath',
    numberValue: 5,
    age: 34,
    maxSentences: 10
  });

Component Types

Role

Defines the AI's role/identity.

builder.role('You are a helpful assistant');

Goal

Defines the objective of the prompt.

builder.goal('Answer user questions');

Input

Defines input data/parameters. Can accept strings or objects.

// String format
builder.input('Question: ${question}');

// Object format (auto-formatted with "- key: value")
builder.input({
  name: '${userName}',
  age: '${age}',
  metadata: { key: 'value' } // Non-string values are JSON.stringify'd
});
// Results in:
// "- name: John
// - age: 25
// - metadata: {\"key\":\"value\"}"

Output

Defines expected output format/requirements.

builder.output('Write ${maxSentences} sentences');

Context

Provides background information.

builder.context('Today is ${date}');

Persona

Defines character/personality traits.

builder.persona('The Steady Anchor persona');

Tone

Defines communication style.

builder.tone('Warm and supportive');

Few-shots

Provides example inputs/outputs. Can accept an array of strings which will be automatically formatted as "Example 1:", "Example 2:", etc.

// Using array (auto-formatted)
builder.fewShots([
  'Input: Hello\nOutput: Hi there!',
  'Input: How are you?\nOutput: I am doing well, thank you!'
]);
// Results in: "Example 1:\nInput: Hello\nOutput: Hi there!\n\nExample 2:\n..."

// Using string (manual formatting)
builder.fewShots('Example 1: ...');

// Using function that returns array
builder.fewShots((params) => {
  return ['Example one', 'Example two'];
});

Guardrails

Defines safety/behavior boundaries. Can accept an array of strings which will be automatically prefixed with "-".

// Using array (auto-prefixed)
builder.guardrails([
  'Do not provide medical advice',
  'Do not share personal information',
  'Always verify facts'
]);
// Results in: "- Do not provide medical advice\n- Do not share personal information\n- Always verify facts"

// Using string (manual formatting)
builder.guardrails('Do not provide medical advice');

// Using function that returns array
builder.guardrails((params) => {
  return ['Rule one', 'Rule two'];
});

Constraints

Defines limitations/rules. Can accept an array of strings which will be automatically prefixed with "-".

// Using array (auto-prefixed)
builder.constraints([
  'Maximum 500 words',
  'Response time under 2 minutes',
  'Use simple language'
]);
// Results in: "- Maximum 500 words\n- Response time under 2 minutes\n- Use simple language"

// Using string (manual formatting)
builder.constraints('Maximum 500 words');

// Using function that returns array
builder.constraints((params) => {
  return ['Constraint one', 'Constraint two'];
});

Tasks

Defines list of tasks to perform. Can accept an array of strings which will be automatically prefixed with "1.", "2.", etc.

// Using array (auto-prefixed)
builder.tasks([
  'Analyze the input',
  'Generate response',
  'Validate output'
]);
// Results in: "1. Analyze the input\n2. Generate response\n3. Validate output"

// Using string (manual formatting)
builder.tasks('1. Analyze the input\n2. Generate response');

// Using function that returns array
builder.tasks((params) => {
  return ['Task one', 'Task two'];
});

Steps

Defines sequential steps to follow. Can accept an array of strings which will be automatically prefixed with "Step 1:", "Step 2:", etc.

// Using array (auto-prefixed)
builder.steps([
  'Understand the requirements',
  'Break down into tasks',
  'Execute each task'
]);
// Results in: "Step 1: Understand the requirements\nStep 2: Break down into tasks\nStep 3: Execute each task"

// Using string (manual formatting)
builder.steps('Step 1: ...\nStep 2: ...');

// Using function that returns array
builder.steps((params) => {
  return ['Step one', 'Step two'];
});

Parameter Substitution

Use ${paramName} syntax in template strings:

builder.role('Hello ${name}, you are ${age} years old');
const result = builder.build({ name: 'John', age: 25 });
// Result: "Hello John, you are 25 years old"

Missing Parameters: If a parameter is missing, null, or undefined, the placeholder is kept as-is (allows for optional parameters):

builder.role('Hello ${name}');
const result = builder.build({}); // No params provided
// Result: "Hello ${name}" (placeholder preserved)

Non-string Values: Non-string values are automatically converted to strings:

builder.role('Age: ${age}, Active: ${active}');
const result = builder.build({ age: 25, active: true });
// Result: "Age: 25, Active: true"

You can also use functions for dynamic content:

builder.role((params) => {
  return `Hello ${params.name}, you are ${params.age} years old`;
});

Conditional Logic

Include/exclude components based on runtime conditions:

import { createCondition, RoleComponent, GoalComponent } from 'promptfmt';

builder.goal('Goal A', {
  condition: createCondition(
    (params) => params.age > 18,
    new GoalComponent('Adult Goal'),
    new GoalComponent('Child Goal')
  )
});

// If age > 18, includes "Adult Goal", otherwise includes "Child Goal"
const result = builder.build({ age: 25 });

Multiple Components in Conditions: You can include multiple components in the then or else clauses:

builder.role('Base role', {
  condition: createCondition(
    (params) => params.userType === 'premium',
    [
      new RoleComponent('Premium role'),
      new ContextComponent('Premium context')
    ],
    new RoleComponent('Standard role')
  )
});

Conditional Logic Without Else: If no else clause is provided and the condition is false, the component is excluded:

builder.goal('Optional goal', {
  condition: createCondition(
    (params) => params.includeGoal === true,
    new GoalComponent('Optional goal')
    // No else clause - component excluded if condition is false
  )
});

Component Ordering

Control the order of components:

builder.goal('Goal', { order: 2 });
builder.role('Role', { order: 1 });
builder.input('Input', { order: 3 });

// Components will appear in order: Role, Goal, Input

Ordering Rules:

  • Components with lower order numbers appear first
  • Components without an explicit order come after ordered components, maintaining their insertion order
  • If no order is specified, components appear in the order they were added

Custom Labels

Add custom labels to components:

builder.role('You are a helper', { label: 'System Role' });

Advanced Usage

Adding Custom Components

All component classes are available for direct instantiation:

import { 
  RoleComponent, 
  GoalComponent, 
  InputComponent, 
  OutputComponent,
  ContextComponent,
  PersonaComponent,
  ToneComponent,
  FewShotsComponent,
  GuardrailsComponent,
  ConstraintsComponent,
  TasksComponent,
  StepsComponent,
  BaseComponent
} from 'promptfmt';

const customComponent = new RoleComponent('Custom role');
builder.addComponent(customComponent);

BaseComponent

The BaseComponent class provides a base for all components and includes a clone method:

import { BaseComponent, ComponentType } from 'promptfmt';

const component = new BaseComponent(ComponentType.ROLE, 'You are a helper');
const cloned = component.clone({ label: 'Custom Label' });
// Creates a copy with updated properties

Multiple Components

builder.addComponents([
  new RoleComponent('Role 1'),
  new GoalComponent('Goal 1'),
]);

Clearing Components

builder.clear();

Empty Components

Empty components (with no content after parameter substitution) are automatically skipped during rendering.

API Reference

PromptBuilder

Main class for building prompts.

Methods

  • role(content, options?) - Add role component
  • goal(content, options?) - Add goal component
  • input(content, options?) - Add input component
  • output(content, options?) - Add output component
  • context(content, options?) - Add context component
  • persona(content, options?) - Add persona component
  • tone(content, options?) - Add tone component
  • fewShots(content, options?) - Add few-shots component
  • guardrails(content, options?) - Add guardrails component
  • constraints(content, options?) - Add constraints component
  • tasks(content, options?) - Add tasks component
  • steps(content, options?) - Add steps component
  • addComponent(component) - Add custom component
  • addComponents(components) - Add multiple components
  • getComponents() - Get all components
  • clear() - Clear all components
  • build(params?) - Build final prompt string. params is optional (defaults to {}). Missing parameters in template strings will keep their placeholders.

Utilities

Parameter Substitution

import { 
  substitute, 
  resolveContent,
  extractParameters, 
  validateParameters 
} from 'promptfmt';

// Substitute parameters in a template string
substitute('Hello ${name}', { name: 'John' }); // "Hello John"

// Resolve content (handles strings, template strings, or functions)
resolveContent('Hello ${name}', { name: 'John' }); // "Hello John"
resolveContent((params) => `Hello ${params.name}`, { name: 'John' }); // "Hello John"

// Extract parameter names from a template
extractParameters('Hello ${name}, age ${age}'); // ["name", "age"]

// Validate parameters (returns missing parameter names)
validateParameters('Hello ${name}', { name: 'John' }); // [] (no missing params)
validateParameters('Hello ${name}', {}); // ["name"] (missing params)

// Validate with strict mode (throws error if params missing)
validateParameters('Hello ${name}', {}, true); 
// Throws: Error("Missing required parameters: name")

Conditional Logic

import { 
  createCondition, 
  evaluateCondition,
  filterComponentsByCondition 
} from 'promptfmt';
import { RoleComponent, PromptComponent } from 'promptfmt';

// Create a condition
const condition = createCondition(
  (params) => params.age > 18,
  new RoleComponent('Adult'),
  new RoleComponent('Child')
);

// Evaluate a condition and get components to include
const components = evaluateCondition(condition, { age: 25 });
// Returns: [RoleComponent('Adult')]

// Filter an array of components based on their conditions
const allComponents: PromptComponent[] = [/* ... */];
const activeComponents = filterComponentsByCondition(allComponents, { age: 25 });
// Returns only components that pass their conditions

Prompt Rendering

import { 
  renderComponent, 
  renderComponents,
  RenderOptions 
} from 'promptfmt';
import { RoleComponent } from 'promptfmt';

// Render a single component
const component = new RoleComponent('You are a helper');
const rendered = renderComponent(component, {});
// Returns: "Role\nYou are a helper"

// Render multiple components
const components = [
  new RoleComponent('You are a helper'),
  new GoalComponent('Help users')
];
const prompt = renderComponents(components, {}, {
  separator: '\n\n', // Default: '\n\n'
  includeLabels: true, // Default: true
  skipEmpty: true // Default: true
});

// Custom render options
const options: RenderOptions = {
  separator: '\n---\n',
  includeLabels: false,
  labelFormatter: (component) => `[${component.type.toUpperCase()}]`,
  skipEmpty: false
};

Default Component Labels: When using renderComponent or renderComponents, default labels are automatically applied if no custom label is provided:

  • role → "Role"
  • goal → "Goal"
  • input → "Input"
  • output → "Output"
  • context → "Context"
  • persona → "Persona"
  • tone → "Tone"
  • few-shots → "Examples"
  • guardrails → "Guardrails"
  • constraints → "Constraints"
  • tasks → "Tasks"
  • steps → "Steps"

Note: PromptBuilder.build() does not use these default labels - it only includes labels if explicitly set via the label option.

Types

All TypeScript types are exported for use in your code:

import type { 
  PromptComponent,
  ComponentType,
  ComponentOptions,
  ParameterMap,
  ContentValue,
  TemplateString,
  Condition,
  ConditionFunction,
  RenderOptions
} from 'promptfmt';
  • PromptComponent - Base interface for all components
  • ComponentType - Enum of all component types (ROLE, GOAL, INPUT, etc.)
  • ComponentOptions - Options for adding components (condition, order, label)
  • ParameterMap - Type for parameter objects (Record<string, any>)
  • ContentValue - Content type: string | TemplateString | ((params: ParameterMap) => string)
  • TemplateString - Type alias for template strings
  • Condition - Conditional logic structure with if, then, and optional else
  • ConditionFunction - Function type: (params: ParameterMap) => boolean
  • RenderOptions - Options for rendering components (separator, includeLabels, labelFormatter, skipEmpty)

Examples

See the examples/ directory for comprehensive examples demonstrating:

  • Basic usage
  • Parameter substitution
  • Conditional logic
  • Component ordering
  • Custom labels
  • Dynamic content with functions
  • All component types
  • Complex real-world scenarios

Run examples with:

npm run build
npx ts-node -r tsconfig-paths/register examples/run-all.ts

License

MIT License - see LICENSE file for details.