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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@yaos-git/prompt-opm

v126.0.3

Published

A local-first Object Prompt Mapper and testing framework

Readme

Node Version TypeScript Version Zod

Uses Vitest Uses Biome


Table of Contents

Getting Started

Prompt Files

Usage

API


Overview

prompt-opm compiles .prompt.md files into type-safe TypeScript modules with Zod validation. Write prompts in Markdown with YAML frontmatter, and the compiler generates importable objects with validated input/output schemas, model configs, and a ready-to-use prompt() function.


Key Features

  • Compile-Time Safety: Generates Zod schemas and TypeScript types from your prompt definitions.
  • Git-Centric: Your .prompts/ directory lives in your repository. No external database.
  • Modular Snippets: Compose prompts from reusable fragments with {{ @snippet }} syntax.
  • Incremental Builds: Content-hash manifest skips unchanged files. Only dirty files recompile.
  • Auto-Versioning: The CLI detects changes and bumps versions automatically (patch for body changes, minor for schema changes).
  • Lightweight: Two runtime dependencies (yaml, chokidar). Zod is a peer dependency.

Quick Start

# Install
npm install -D @yaos-git/prompt-opm
npm install zod

# Scaffold project
npx prompt-opm init

# Compile prompts to TypeScript
npx prompt-opm generate

Prompt File Format

Create .prompt.md files in your .prompts/ directory:

---
model: "gemini-1.5-pro"
version: "1.0.0"
config:
  temperature: 0.7
  maxTokens: 1024
inputs:
  name: string
  traits: string[]
outputs:
  bio: string
---
{{ @persona_expert }}
Write a bio for {{ name }} with these traits: {{ traits }}.

Supported Types

| Frontmatter syntax | Generated Zod schema | |-------------------------|----------------------------------| | string | z.string() | | number | z.number() | | boolean | z.boolean() | | string[] | z.array(z.string()) | | number[] | z.array(z.number()) | | enum(a, b, c) | z.enum(["a", "b", "c"]) |

Config Fields

Known optional fields (extras allowed):

  • temperature: number
  • topK: number
  • topP: number
  • maxTokens: number

Snippets

Compose prompts from reusable fragments.

Root snippet (resolves from .prompts/ root):

{{ @persona_expert }}

Relative snippet (resolves from current file's directory):

{{ @.local_helper }}

Snippets are resolved recursively. Circular dependencies are detected and reported. Input variables from snippets are merged with the root prompt's inputs (same name + same type = ok, different type = error).

Snippets cannot declare outputs — only the root prompt defines the response schema.

Snippet-Only Files

Mark a prompt as snippet: true to prevent it from being compiled into its own module:

---
model: "gemini-1.5-pro"
snippet: true
---
You are an expert copywriter.

Snippet-only files are still available for {{ @name }} references but won't appear in the generated output or barrel index.ts.


CLI Commands

prompt-opm init        # Scaffold .prompts/ directory and config
prompt-opm generate    # Compile .prompt.md files to TypeScript
prompt-opm watch       # Watch for changes and regenerate
prompt-opm validate    # Check for errors without emitting files
prompt-opm diff        # Preview what would change

Generated Output

Each .prompt.md compiles to a .ts file:

import { z } from "zod";

export const model = "gemini-1.5-pro" as const;
export const configs = { temperature: 0.7, maxTokens: 1024 } as const;
export const meta = { version: "1.0.0", ... } as const;

export const inputSchema = z.object({
  name: z.string(),
  traits: z.array(z.string()),
});
export type InputType = z.infer<typeof inputSchema>;

export const outputSchema = z.object({ bio: z.string() });
export type OutputType = z.infer<typeof outputSchema>;

export const template = `...`;  // Snippets resolved, variables intact
export const prompt = (inputs: InputType): string => { ... };

Usage in Application Code

import * as bioPrompt from "./generated/prompts/generateBio";

// 1. Validate input
const variables = bioPrompt.inputSchema.parse({
  name: "Alice",
  traits: ["witty", "coder"],
});

// 2. Build the prompt string
const promptText = bioPrompt.prompt(variables);

// 3. Call your LLM
const llmResult = await myLLMClient({
  model: bioPrompt.model,
  prompt: promptText,
  ...bioPrompt.configs,
});

// 4. Validate output
const data = bioPrompt.outputSchema.parse(llmResult);

A barrel index.ts is generated for convenient imports:

import { generateBio, translateEmail } from "@prompts";

Versioning

Versions are declared in frontmatter and auto-bumped by prompt-opm generate:

  • Patch bump (1.0.0 -> 1.0.1): Body or config changed, but inputs/outputs schema is the same.
  • Minor bump (1.0.0 -> 1.1.0): Inputs or outputs schema changed (potential breaking change for consumers).

The CLI writes the bumped version back into the source .prompt.md file, so the change appears in your git diff.


Configuration

.prompt-opm.config.json:

{
  "source": "./.prompts",
  "output": "./src/generated/prompts",
  "manifest": "./.prompt-opm"
}

| Field | Required | Default | Description | |---|---|---|---| | source | yes | — | Directory containing .prompt.md files | | output | yes | — | Directory for generated .ts files | | manifest | no | same as output | Directory for the .prompt-opm.manifest.json cache file |


Library API

Use prompt-opm programmatically by importing from @yaos-git/prompt-opm:

import { generate, parsePromptFile, analyze } from "@yaos-git/prompt-opm";

Core Pipeline

| Function | Signature | Description | |----------|-----------|-------------| | generate | (config: OpmConfig) => GenerateResult | Run the full compilation pipeline | | analyze | (config: OpmConfig) => AnalyzeResult | Analyze prompts without emitting files | | parsePromptFile | (content: string, filePath: string) => PromptFile | Parse a single .prompt.md file | | scanPromptFiles | (sourceDir: string) => string[] | Recursively find all .prompt.md files | | resolveSnippets | (file: PromptFile, sourceRoot: string) => ResolvedPrompt | Resolve {{ @snippet }} references |

Schema & Emitter

| Function | Signature | Description | |----------|-----------|-------------| | mapTypeToZod | (type: SchemaValue) => string | Convert a type string to Zod code | | mapSchemaToZodObjectString | (schema: Record<string, SchemaValue>) => string | Convert a full schema to z.object(...) string | | generateFileContent | (input: EmitInput) => string | Generate a TypeScript module string | | generateBarrelContent | (moduleNames: string[]) => string | Generate barrel index.ts content |

Versioning & Hashing

| Function | Signature | Description | |----------|-----------|-------------| | bumpVersion | (version: string, type: BumpType) => string | Bump a semver string | | determineVersionBump | (prev: ManifestEntry, ...) => BumpType \| null | Decide if a version bump is needed | | hashContent | (content: string) => string | SHA256 hash of file content | | hashInputsOutputs | (inputs?, outputs?) => string | Deterministic hash of schema fields |

Manifest

| Function | Signature | Description | |----------|-----------|-------------| | loadManifest | (dir: string) => ManifestData | Load .prompt-opm.manifest.json | | saveManifest | (dir: string, data: ManifestData) => void | Save manifest to disk |

Types

All types are exported for library consumers:

import type {
  OpmConfig,
  PromptFile,
  FrontMatter,
  Config,
  SchemaValue,
  ManifestEntry,
  ManifestData,
  DiagnosticError,
  PromptAnalysis,
  AnalyzeResult,
  GenerateResult,
  ResolvedPrompt,
  EmitInput,
  BumpType,
  DependencyGraph,
  DependencyNode,
} from "@yaos-git/prompt-opm";

Style Guide

Conventions for contributing to this project. All rules are enforced by code review; Biome handles formatting and lint.

Exports

  • Named exports only — no export default. Every module uses export function, export const, or export type.
  • import type — always use import type for type-only imports.
  • .js extensions — all relative imports use explicit .js extensions (ESM requirement).

File Structure

src/
├── cli/              # CLI entry point and command handlers
│   └── commands/     # One file per CLI command
├── core/             # Core pipeline modules (PascalCase directories)
│   └── MyModule/
│       ├── index.ts
│       └── index.test.ts
├── manifest/         # Manifest storage and hashing
├── types/            # Shared type definitions (PascalCase directories)
│   └── MyType/
│       ├── index.ts
│       └── MyType.test-d.ts

Types

  • Use type for all type definitions.
  • Shared/exported types live in src/types/TypeName/index.ts with a co-located TypeName.test-d.ts.
  • All type imports use the barrel src/types/index.ts — never import from subdirectories directly.
  • No duplicate type definitions — import from the canonical source.

Constants & Patterns

  • Shared regex patterns live in src/core/patterns.ts.
  • No magic numbers — extract to named constants at the top of the file or in a .consts.ts file.
  • No duplicated logic — extract shared functions to a common module.

Testing

  • Every module has a co-located test file.
  • Core modules: index.test.ts inside the module directory.
  • CLI commands: commandName.test.ts in src/cli/commands/.
  • Types: TypeName.test-d.ts (type-level tests using expectTypeOf/assertType).

License

ISC