Package Exports
- @reallyartificial/grain
Readme
@reallyartificial/grain
TypeScript SDK for Grain — a structured data format for describing AI agents.
Install
npm install @reallyartificial/grainUsage
import { Grain } from "@reallyartificial/grain"
// Load from file
const agent = Grain.load("./my-agent.agent.yaml")
// Generate system prompt
const prompt = agent.toPrompt()
// Channel-specific prompt
const slackPrompt = agent.toPrompt("slack")
// Clean YAML for LLM consumption (strips metadata)
const yaml = agent.toString()Create programmatically
import { Grain, Presets } from "@reallyartificial/grain"
const agent = Grain.create("my-bot", { name: "My Bot", description: "Does helpful things" })
.setPersonality("warmth", 0.8)
.setPersonality("confidence", 0.9)
.addRule({
id: "always-greet",
condition: { type: "always" },
action: { type: "respond-with-style", style: { warmth: 0.9 } },
})
.addBoundary({
description: "Never share internal data",
category: "data",
enforcement: "hard",
onViolation: "refuse",
})
.addTool({ id: "search", usage: "Search knowledge base" })
console.log(agent.toPrompt())Immutable mutations
Every method returns a new Grain — the original is never modified.
const base = Grain.create("my-bot")
const withRule = base.addRule({ id: "r1", condition: { type: "always" }, action: { type: "require-confirmation" } })
console.log(base.rules.length) // 0
console.log(withRule.rules.length) // 1Merge and diff
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 } }CLI
npx @reallyartificial/grain validate my-agent.agent.yaml
npx @reallyartificial/grain generate my-agent.agent.yaml --channel slack
npx @reallyartificial/grain info my-agent.agent.yamlMinimal Spec
specVersion: "1.0"
id: my-bot
version: 1.0.0
meta:
name: My Bot
description: Does helpful thingsAPI
Constructors
Grain.create(id, opts?)— Create with defaultsGrain.from(yamlOrJson)— Parse from stringGrain.load(filePath)— Load from fileGrain.of(spec)— Wrap a plain AgentSpec object
Mutations (return new Grain)
addRule(rule)/removeRule(id)/addRules(rules)addBoundary(b)/removeBoundary(desc)/addBoundaries(bs)addTool(t)/removeTool(name)/addTools(ts)addSkill(s)/removeSkill(name)/addSkills(ss)addExpertise(domain, proficiency)/removeExpertise(domain)setPersonality(dim, value)— validates 0-1 rangeset(path, value)/get(path)— generic deep accessmerge(other)— deep merge, other wins
Accessors
id,name,version— scalarspersonality,rules,boundaries,tools,skills,expertise— copiesdata— raw frozen AgentSpecisValid— boolean
Queries
hasRule(id),hasTool(name),hasSkill(name)
Output
toString(channel?)— Clean YAML for LLMs (strips metadata)toPrompt(channel?)— Natural language system prompttoYAML()— Full YAML with all metadatatoJSON()— Full JSONvalidate()— ReturnsValidationError[]diff(other)— Structural diff
Presets
Presets.personality.professionalPresets.personality.friendlyPresets.personality.expertPresets.personality.creativePresets.personality.executor
License
MIT