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

@reallyartificial/grain

v0.3.2

Published

Grain — a structured data format for describing AI agents.

Readme

Grain

Define AI agents as data, not prompts.

Most agent frameworks treat personality and behavior as strings you paste into a system prompt. Grain treats them as a structured, immutable data type with real operations: build, merge, diff, query, serialize.

You define an agent's personality as numbers. Grain turns them into behavioral directives using 5-level graduated semantic anchoring. The same agent definition produces different prompts per channel.

grain.reallyartificial.org | GitHub | PyPI (grain-sdk)

Install

npm install @reallyartificial/grain

30-second version

import { Grain } from "@reallyartificial/grain"

const agent = Grain.create("support-bot", { name: "Alex", description: "Customer support agent" })
  .setPersonality("warmth", 0.9)      // 0-1 float, maps to real behavioral directives
  .setPersonality("formality", 0.3)
  .setPersonality("confidence", 0.8)
  .addBoundary({ description: "Never share internal pricing data", category: "data", enforcement: "hard", onViolation: "refuse" })

// Natural language system prompt, ready for any LLM
agent.toPrompt()

// Same agent, tuned for Slack
agent.toPrompt("slack")

// Clean YAML, stripped of metadata, usable directly as a prompt
agent.toString()

What the personality numbers actually do

setPersonality("warmth", 0.9) doesn't just store 0.9. It maps to a specific behavioral directive:

| Value | Directive | |-------|-----------| | 0.0-0.2 | "Be direct and clinical. Focus purely on facts and outcomes, not feelings." | | 0.2-0.4 | "Be polite but task-focused." | | 0.4-0.6 | "Be friendly. Show basic courtesy." | | 0.6-0.8 | "Be warm and empathetic. Acknowledge feelings and show genuine care." | | 0.8-1.0 | "Lead with empathy. Mirror the user's emotional state, use inclusive language." |

This works across 8 dimensions: formality, warmth, humor, assertiveness, verbosity, confidence, concreteness, urgency. Each has 5 graduated levels. The directives are grounded in the SAC framework for trait decomposition.

Immutable by design

Every operation returns a new Grain. The original never changes.

const base = Grain.create("bot")
const friendly = base.setPersonality("warmth", 0.9)

base.personality.warmth     // 0.5 (default, unchanged)
friendly.personality.warmth // 0.9

Merge and diff agents

const a = Grain.create("bot-a").setPersonality("warmth", 0.3)
const b = Grain.create("bot-b").setPersonality("warmth", 0.9)

const merged = a.merge(b)   // b wins on conflicts
const changes = a.diff(b)   // { "voice.personality.warmth": { before: 0.3, after: 0.9 } }

Works with any LLM

import { Grain } from "@reallyartificial/grain"
import OpenAI from "openai"

const agent = Grain.load("./support.agent.yaml")
const client = new OpenAI()

const response = await client.chat.completions.create({
  model: "gpt-4o",
  messages: [
    { role: "system", content: agent.toPrompt() },
    { role: "user", content: "I need help with my order" }
  ]
})

Swap OpenAI for Anthropic, Gemini, Ollama, or anything that takes a system prompt. Grain produces strings, not vendor lock-in.

Load from YAML

specVersion: "1.0"
id: support-bot
version: 1.0.0
meta:
  name: Alex
  description: Customer support agent

That's a valid Grain file. Four required fields. Everything else has sensible defaults.

const agent = Grain.load("./support.agent.yaml")

CLI

npx @reallyartificial/grain validate agent.yaml
npx @reallyartificial/grain generate agent.yaml --channel slack
npx @reallyartificial/grain info agent.yaml

API

Constructors: Grain.create(id, opts?) | Grain.from(yamlOrJson) | Grain.load(filePath) | Grain.of(spec)

Mutations (return new Grain): addRule / removeRule / addBoundary / removeBoundary / addTool / removeTool / addSkill / removeSkill / addExpertise / removeExpertise / setPersonality / set / get / merge

Output: toString(channel?) clean YAML | toPrompt(channel?) natural language | toYAML() full YAML | toJSON() full JSON | validate() | diff(other)

Presets: Presets.personality.professional | .friendly | .expert | .creative | .executor

Full API docs at grain.reallyartificial.org

License

MIT