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

@metalbolicx/blueprint

v0.2.0

Published

Transactional code generator with Hygen-compatible templates

Readme

Blueprint

A fast, transactional template generator — a modern replacement for Hygen.

Generate code, config files, or any text from templates with atomic commits, declarative prompts, and no runtime dependencies. Language-agnostic — the same engine drives TypeScript, Go, Python, Rust, SQL, YAML, or any text output.

npx blueprint generate react-component --name User --path src/components

Installation

npm install -g blueprint

For non-installed use:

npx blueprint <command>

Onboarding

Scaffold a runnable hello-world template in your project:

blueprint init
blueprint generate hello-world myfirst

Produces hello-myfirst.md in the current directory.

For global installation (templates stored in ~/.config/blueprint/):

blueprint init --global
blueprint generate hello-world myfirst

Both commands are idempotent — re-running is safe and produces no changes.

Quick start

  1. Init — blueprint init scaffolds .blueprint.yaml and a hello-world example in _templates/hello-world/
  2. Author — create a manifest + template files:
_templates/
└── component/
    └── new/
        ├── manifest.yaml
        └── files/
            └── Component.tsx.ejs.t
# manifest.yaml
name: component
classification: component
prompts:
  - name: name
    type: input
    description: "Component name (PascalCase)"
    default: MyComponent
  - name: path
    type: input
    description: "Output path"
    default: src/components
# files/Component.tsx.ejs.t
---
to: <%= path %>/<%= name %>.tsx
---
import React from 'react'

interface <%= name %>Props {
  children?: React.ReactNode
}

export const <%= name %>: React.FC<<%= name %>Props> = ({ children }) => {
  return <div className="<%= h.kebabCase(name) %>"><%= children %></div>
}
  1. Generate — blueprint generate component --name Button --path src/ui

CLI usage

# Show help
blueprint --help

# Scaffold .blueprint.yaml
blueprint init

# Generate from a template classification
blueprint generate <classification> [options]

# Options:
#   --name <name>     Component name (PascalCase)
#   -n, --name <name> Short form
#   --force           Skip prompts, overwrite existing files
#   -f, --force       Short form
#   --output <dir>    Output directory
#   -o, --output <dir> Short form
#   --<key> <value>   Arbitrary attributes passed to templates
 
# Template registry management
# blueprint template copy <classification>      Copy a project generator into global registry
# blueprint template list                      List registry entries (name + source path)
# blueprint template remove <classification>   Remove a global template and its registry entry

Examples

# Basic generation with prompts
blueprint generate react-component

# With explicit name (skips name prompt)
blueprint generate react-component --name Button

# Force overwrite existing files
blueprint generate react-component --name Button --force

# Custom output directory
blueprint generate react-component --name Button --output src/ui

# Pass custom attributes
blueprint generate react-component --name Button --path src/components --framework react18

# Use short flags
blueprint generate react-component -n Button -f -o src/ui

Template format

manifest.yaml

Declares metadata, classification, and interactive prompts:

name: component
classification: component
prompts:
  - name: package
    type: input
    description: "Package or module name"
    default: main
  - name: framework
    type: select
    description: "Framework"
    options:
      - react
      - vue
      - svelte
  - name: includeTests
    type: confirm
    description: "Include test file?"
    default: true

Template files (*.ejs.t)

Frontmatter defines the operation; body is the template. Uses EJS syntax.

---
to: src/<%= name %>.tsx
inject: React.FC<<%= name %>Props>
---
import React from 'react'

interface <%= name %>Props {
  children?: React.ReactNode
}

export const <%= name %>: React.FC<<%= name %>Props> = ({ children }) => {
  return <div className="<%= h.kebabCase(name) %>"><%= children %></div>
}

Frontmatter directives

| Directive | Type | Description | |-----------|------|-------------| | to | string | Target file path | | inject | string | Regex pattern to match for replacement | | after | string | Regex — insert content after this pattern | | before | string | Regex — insert content before this pattern | | prepend | bool | Prepend content to existing file | | append | bool | Append content to existing file | | force | bool | Overwrite existing file |

sh is not supported (shell-free execution, plans 035/039); use the script: directive (requires shell.enabled: true) or a lifecycle hook instead.

Directive examples

to — Write to a specific path:

---
to: src/<%= name %>.tsx
---

inject — Replace content matching a regex:

---
inject: const \w+ = new
---
const newInstance = new Constructor()

after — Insert after a regex match:

---
after: class \w+
---
  // Added after class definition

before — Insert before a regex match:

---
before: export default
---
// Header comment

prepend — Add to the beginning of a file:

---
prepend: true
---
// This goes at the top

append — Add to the end of a file:

---
append: true
---
// This goes at the bottom

force — Overwrite without prompting:

---
to: src/<%= name %>.tsx
force: true
---

script — Run a configured script after render:

---
to: src/<%= name %>.tsx
script: setup
---

Context variables

Available inside every template via EJS:

| Variable | Description | |----------|-------------| | <%= name %> | Component name (lowercase) | | <%= Name %> | Component name (PascalCase) | | <%= names %> | Pluralized lowercase | | <%= Names %> | Pluralized PascalCase | | <%= path %> | User-provided path attribute | | <%= package %> | User-provided package attribute | | Any prompt answer | Available by its name |

FuncMaps (template helpers)

Available as h.* in templates:

| Function | Example | Result | |----------|---------|--------| | h.pascalCase(str) | <%= h.pascalCase("hello_world") %> | HelloWorld | | h.camelCase(str) | <%= h.camelCase("hello_world") %> | helloWorld | | h.kebabCase(str) | <%= h.kebabCase("HelloWorld") %> | hello-world | | h.snakeCase(str) | <%= h.snakeCase("HelloWorld") %> | hello_world | | h.upper(str) | <%= h.upper("hello") %> | HELLO | | h.lower(str) | <%= h.lower("HELLO") %> | hello | | h.trim(str) | <%= h.trim(" hello ") %> | hello | | h.title(str) | <%= h.title("hello world") %> | Hello World |

Hooks

Lifecycle hooks in .blueprint.yaml:

hooks:
  pre_generate: echo Starting generation...
  post_generate: prettier --write generated/
  timeout: 30s

Commands, including interpreters such as bash script.sh and node setup.mjs, run as binaries with whitespace-tokenized arguments. Shell syntax (quotes, &&, pipes, and $ expansion) is not interpreted. Use the structured args field when arguments must be passed literally to execFile.

Hooks run through execFile without shell interpretation. They retain their configured timeout and filtered environment; script paths are restricted to the project tree. When shell.tools is configured, the command binary must be in that allowlist; without a configured allowlist, hooks remain permitted but shell-free. Use a script path for shell syntax.

Shell tool and script execution is disabled unless shell.enabled: true. It defaults to false (strict fail-closed); without enabling it, these executions fail with Shell execution disabled. That error is the migration guide for existing configurations. Setting enabled: true requires tools to be defined. Allowlisted inline commands run shell-free via execFile either way.

shell:
  enabled: true
  tools:
    - name: formatter
      command: prettier
  scripts:
    - name: setup
      path: scripts/setup.sh
  env:
    NODE_ENV: development

| shell field | Description | |--------------|-------------| | enabled | Boolean gate; defaults to false. Must be true to enable tool and script execution. | | tools | Tool definitions with name, command, and optional args; required when enabled: true. | | scripts | Script definitions with name, path, and optional args. | | env | String-valued environment variables. |

Safety

  • Transactional: renders to temp staging dir, commits atomically — no partial writes
  • Rollback: on any failure (render error, shell error), staged files are cleaned up
  • Conflict resolution: bulk prompt — [y]es to all, [n]o to all, [s]elect individually, [a]bort

Local templates remain fully trusted. Registry templates are trusted code too: they run with your permissions when you generate with them, so review a generator at install time — install requires an explicit confirmation of a local source path (default NO). Installed templates receive a .blueprint-provenance marker; the marker is provenance information, not a security boundary, and the associated tag check is advisory. See ADR 0001 for the trust-model decision and its rationale.

Global template registry

  • Template registry entries are stored in ~/.config/blueprint/config.yaml under the registry array. Each entry records name, source (absolute path to the original project generator), and path (the installed ~/.config/blueprint/templates/<name>/ location).
  • blueprint template copy <classification> copies the entire source generator directory (manifest + actions) into the registry folder and persists the entry.
  • blueprint template list shows installed templates and their originating paths.
  • blueprint template remove <classification> deletes the registry directory and removes the associated config entry.
  • Discovery automatically appends registry paths after the project’s _templates/templates/generators stack, so local generators still win when a name conflicts.

Local release

Publish from a clean worktree to avoid shipping unintended files:

git stash                 # or git checkout -- .
pnpm build                # ReScript compile + rolldown bundle
pnpm res:test             # full test suite
npm pack --dry-run        # inspect the exact file list
npm publish --access public
git tag v<version>
git push --tags

Publishing

The package is published as @metalbolicx/blueprint (scoped).

Pre-publish checklist

git stash                 # or git checkout -- .
pnpm build                # ReScript compile + rolldown bundle
pnpm res:test             # full test suite — must be 675/675 green
npm pack --dry-run        # verify clean tarball (only LICENSE, README.md, dist/main.mjs, package.json)
npm publish --dry-run --access public

Run pnpm release:smoke for a single-command readiness check (build → pack → install → invoke → cleanup).

After publish

git tag v<version>
git push --tags

Note

The bin name stays "blueprint" even though the package is scoped — users run npx blueprint, not npx @metalbolicx/blueprint.

Docs

Full documentation at /docs, including architecture, API reference, and tutorials. Allowlisted inline shell commands are split on whitespace and run as the allowlisted binary plus argument tokens via execFile; no shell interpretation is performed. Quoting, interpolation, or other complex arguments must use a ToolCall with structured args.