Write and scale system prompts like code.

TypeGlish is typed English: a language for system prompts and agents. The compiler catches the contradictions, vague rules, and bloat that make an agent drift and cost more, so a prompt becomes something you can improve, trust, and run lean, not just a wish.1

fig. 2 - the checker, live✓ compiles · score B (89/100) · gate PASS
1$CONFIG model claude-opus-4-8
2$IMPORT input tier: one of free, pro, premium
3
4$TOOL lookup_order
5 - description:: Look up an order by its id and return its status.
6 - input::
7 - order_id:: the order's id.
8
9$TEST no_pricing
10 - input:: How much does the Enterprise plan cost?
11 - expect::
12 - no specific price is stated
13 - redirects pricing to the sales team
14
15<role>
16 @@ persona: name the agent once, then reference it everywhere
17 Persona IS a support agent for Acme, a SaaS analytics company.
18 You are @{persona}.
19</role>
20
21<constraints>
22 @@ pricing: pricing is a sales conversation, never a support one
23 NEVER state a specific price; ALWAYS direct pricing questions to sales.
24 MUST keep every reply to at most 3 sentences.
25 WHEN an order needs live data THEN call @[lookup_order].
26</constraints>
27
28<per_tier>
29 @@ tiering: the prompt reshapes per caller tier, resolved at compile time
30 $IF @{tier} is one of pro, premium: Offer the priority concierge line before closing.
31 $ELSE: Invite the customer to upgrade for priority support.
32</per_tier>
B
acme-support.tg · the checker runs on every keystroke✓ 0 contradictions · $TEST no_pricing 1/1 · △ 1 advisory

The first language built for writing
reliable system prompts.

TypeGlish turns raw prompts into structured, logical statements that can be checked for logical errors, redundancy, forbidden symbols, and prompting best practices.

Agent-written prompts follow your rules - or don’t compile.

When an agent writes or edits a prompt, the compiler holds it to the rules you set. Off-brand, contradictory, or rule-breaking lines never land.

fig. 3.1 - an agent, unsupervised✗ crumbles
# written by an agent, unreviewed
You are a world-class assistant!!
Always be helpful AND thorough 🎉
(your house rules: nowhere in sight)
An agent writes what works on average. Your rules aren’t in the loop - quality is whatever it guessed.
fig. 3.1′ - an agent, held to your rules✓ holds
+ agent adds: ALWAYS quote a price
✗ E212 breaks your rule at L23 · rejected
  fix:: route pricing to sales
agent: patched · recompiled ✓ 0 exceptions
Every generated line must compile against the rules you set. The agent iterates until it complies - quality is enforced, not requested.

Rules that can never contradict each other.

Every hard rule is checked against all the others. If two can collide, the prompt doesn’t compile - you find out on save, not in front of a user.

fig. 3.2 - a raw prompt✗ crumbles
You are a helpful bot.
Always answer every question.
Never answer anything off-topic.
Be concise but also thorough.
“Always answer” contradicts “never answer off-topic.” Nothing catches it - until it fails in front of a user.
fig. 3.2′ - the same intent, typed✓ holds
Bot IS helpful and concise
Bot MUST answer IF on-topic
Bot NEVER answer IF off-topic
Tone IS warm, brief
The checker resolves the overlap and proves the rules consistent - before runtime, on every save.

Best practice is the syntax, not tribal knowledge.

You declare what you want; the compiler writes the structure that works - ordering, sections, emphasis. No folklore, no lucky comments.

fig. 3.3 - prompt folklore✗ crumbles
IMPORTANT!!! Always stay in role.
(repeating this 3× so it sticks)
Take a deep breath. Think step
by step.  # v14_final_FINAL
Folklore: emphasis hacks, repetition, lucky comments. Nobody knows which parts still matter.
fig. 3.3′ - intent, declared✓ holds
Persona IS a claims adjuster
Persona MUST verify {claim.id} first
Reasoning IS step-by-step
Declare the intent once. The compiler emits the proven structure - ordering, sections, emphasis - and every default is configurable.

The compiler enforces the prompting rules you set.

Your guidelines become compiler config. Write them once, and every prompt - hand-written or generated - is checked against them on every build.

fig. 3.4 - rules in a doc✗ crumbles
# prompt-guidelines.docx
“Keep prompts short. No emojis.
Every agent cites its sources.”
(read once, enforced never)
The team’s rules live in a doc. Enforcement is a code review - when someone remembers.
fig. 3.4′ - rules in the compiler✓ holds
# glish.tgc - your compiler, your rules
RULE forbid:: emojis, superlatives
RULE require:: one $TEST per agent
RULE bound:: prompts AT MOST 40 lines
The same guidelines as config. Every build checks every prompt against them - yours and the machine’s.

One prompt, perfect on every model.

Models read prompts differently. Declare per-model rules once, and each build reshapes the same source for its target - no forks, no compromise.

fig. 3.5 - the fork farm✗ crumbles
prompts/agent-gpt5.txt
prompts/agent-claude.txt
  ← three weeks stale
prompts/agent-llama.txt
  ← who wrote this?
A fork per model. Every fix made three times - or, realistically, once.
fig. 3.5′ - one source, every model✓ holds
$TARGET claude-opus-4-8:: xml sections
$TARGET gpt-5:: markdown, terse

$ tg build --all  ✓ 3 models · same rules
Per-model rules in config. Each build reshapes sections, order, and emphasis for its target - behavior stays identical.

Every line’s contribution, measured.

Every line shows which tests it covers and how often it fires. Dead weight looks like dead weight - and is safe to cut.

fig. 3.6 - the write-only prompt✗ crumbles
# 2,400 lines · 14 authors · 3 years
Never discuss competitor pricing (2023?)
Always end with a haiku.  ← why
(deleting anything = roulette)
Lines accrete forever, because nobody can prove what’s safe to delete.
fig. 3.6′ - the map✓ holds
Persona MUST cite {policy}
  · covers 3 tests · fires 214×/day
Persona NEVER quotes prices
  · fires 12×/day
Tone IS warm
  · no coverage ⚠ add a test
The map ties every line to the tests it covers and the turns it fires on. Dead weight looks like dead weight.

Ship the rules the moment needs - not all 212.

Each turn ships only the rules the moment needs. Edge cases ride along only when the edge shows up - smaller prompts, lower cost.

fig. 3.7 - the flat prompt✗ crumbles
"hi" → 212 rules attached:
refund edge cases, the GDPR
clause, the haiku rule… every
turn, every user, every day.
Flat prompts bill you for every edge case on every turn - the greeting pays for the lawsuit clause.
fig. 3.7′ - compiled per turn✓ holds
IF state IS greeting
THEN include: Persona, Tone
IF claim IS open
THEN include: Refunds, Policy

── this turn: 41 of 212 lines
The runtime compiles the prompt for the current state. Edge cases ride along only when the edge shows up.

Spend intelligence on the problem, not on parsing.

Clear rules leave nothing to interpret, so a smaller model handles them. Same behavior, a fraction of the cost.

fig. 3.8 - vibes as policy✗ crumbles
Use your best judgment about
when a refund feels appropriate,
all things considered. Read
the room.  → frontier model, $$$
Vague rules rent a frontier model just to interpret them - an intelligence tax on reading comprehension.
fig. 3.8′ - policy as policy✓ holds
IF order.age < 30 days
AND item IS unused
THEN refund IS approved
OTHERWISE route TO human

→ runs on the small model
When nothing is left to guess, a smaller model stops guessing. Same behavior, a fraction of the cost.

Your words, your symbols - enforced everywhere.

Your words, symbols, and spelling are checked before anything ships. Every agent, on every model, sounds like your brand.

fig. 3.9 - brand drift✗ crumbles
"Sign in" here, "log in" there,
"LOGIN!" on the sales agent, an
emoji storm on support. Five
agents, five brands.
Every agent invents its own house style, one reply at a time.
fig. 3.9′ - the house dictionary✓ holds
$DICTIONARY "./brand.dict"
Lexicon MUST use "sign in"
Lexicon NEVER says "log in"
Symbols ARE one of: - · ↗
spellcheck · US English · 0 issues
Dictionaries, allowed symbols, and spellcheck run at compile time - the brand survives every agent and every model.

The whole agent, holding.

The whole agent in one readable file - rules, branches, and the tests that guard them - plus the brand module it shares with every other agent.

fig. 3.10 - support.tg
# support.tg - a whole agent, one file
$IMPORT BrandVoice FROM "./brand.tg"

Persona IS Sky, an airline support agent
Persona MUST follow BrandVoice
Persona MUST cite {policy} on every claim
Persona NEVER invents policy

IF customer IS angry THEN
  Tone IS empathetic
  Persona NEVER upsells
OTHERWISE Persona MAY offer {upgrade}

$TEST "refund a $720 ticket"
  EXPECT Persona refuses AND cites {policy}
The entire agent - the rules, the branch, and the test that guards them - in one readable file.
fig. 3.10′ - brand.tg
# brand.tg - one voice, every agent
Tone ARE one of: warm, direct, playful
BrandVoice IS the house style

BrandVoice MUST write in sentence case
BrandVoice SHOULD keep replies under 120 words
BrandVoice NEVER uses jargon

$TEST "explain a delayed flight"
  EXPECT reply IS warm AND plain
The module it imports - versioned, diffable, shared across every agent you run.2
How TypeGlish works

TypeGlish converts raw English into
objects with assigned meaning.

The TypeGlish compiler runs in real-time as prompts are written - turning raw english into logical statements that are checked for validity against your rules.

Sentences compile to an object map, not vibes. Once You.name holds “John”, a later Your name is Sarah. is a provable collision - caught by a lookup, not a model.

Your name IS John.
  1. Your name - the name property on the You object
  2. IS - the copula - binds
  3. John - the value - held, checkable
Your name is Sarah.
  1. Your name is Sarah - collides with “John” at L1 - error

Every action parses to verb + object + tool binding against a curated verb catalog. A bare imperative is an obligation - and each record is a fact the checker can hold against the rest.

Go to checkout, then use @[lookup_order].
  1. Go to - a verb from the catalog: navigate
  2. use - another action - same record shape
  3. @[lookup_order] - the tool it binds
Send the receipt. Suppress the receipt.
  1. Suppress - antonym of send - the actions collide

Different wording, one canonical claim: verb synonyms merge, nouns fold by lemma (people ⇄ person), negations normalize. Restatements become comparable - the precondition for every check downstream.

Use @[lookup_order]. Don't print prices.
  1. Use - folds to call
  2. Don't - reads as DO NOT
  3. print - folds to output
Call @[lookup_order]. Utilize @[lookup_order].
  1. Utilize @[lookup_order] - folds to the same claim - duplicate, warns

One line, one claim: compound rules split into atomic statements, and every statement must terminate - an unterminated line is a compile error with a one-click fix. Atoms are what the solver checks.

Cite sources and stay brief.
  1. Cite sources - claim one
  2. and - splits the compound
  3. . - the terminator - required
Cite sources
  1. Cite sources - no terminator - error, one-click fix

Two strengths, two fates. must and never are contracts - proved before you ship. should and may are preferences - graded, not gated. No more guessing which “always” meant it.

Sky must cite @{policy} and should stay brief.
  1. must - hard - goes to the prover
  2. @{policy} - a runtime slot
  3. should - soft - scored, never sworn
Sky should probably try to stay brief.
  1. should probably try to - hedging - warns, suggests one modal

all / some / no / most / only map onto the square of opposition - the 2,000-year-old logic of quantity. “All claims cite policy” and “some claims skip policy” are now provably incompatible.

All claims cite policy. No reply quotes a price.
  1. All - universal - every claim
  2. No - universal negative
  3. a price - the set it ranges over
Some claims skip policy.
  1. Some claims skip policy - collides with “All claims cite policy” - proved

Numbers in prose become arithmetic: bounds, action counts, set sizes - even supply vs demand (“there is only one tool” vs “call five tools”). The solver decides them instantly.

Keep every reply to at most 3 sentences.
  1. every - all replies
  2. at most 3 - a bound: n ≤ 3
  3. sentences - the counted unit
Write at least 5 sentences.
  1. at least 5 - n ≥ 5 - impossible beside n ≤ 3

Every rule resolves to its subject. “You”, a declared alias, a declared role - one subject, one rule-set, checked together. And “the user” is never you.

You are Claude. Claude must cite sources.
  1. You are Claude - an alias - Claude = you
  2. Claude - so this rule binds to you
The assistant must sign every reply.
  1. The assistant - undeclared - stays scoped, warns

“The” promises a unique referent. With two tools declared, “the tool” is ambiguous - an error naming both candidates. English’s most common word, checked.

Call @[lookup_order] when data is stale.
  1. @[lookup_order] - named - exactly one referent, resolves
Call the tool when data is stale.
  1. the tool - definite - promises exactly one referent

Guards parse in both spellings - IF … THEN and the comma form English writes. Each rule knows when it fires, so two rules that never fire together can never collide.

If the user is angry, never upsell.
  1. If - opens a guard (UNLESS negates)
  2. the user is angry - the condition
  3. never upsell - the rule it gates
If the user is angry, always upsell.
  1. always upsell - collides with “never upsell” under the same guard

Deterministic branches resolve at compile: (source, state) → prompt. The model never reads the branch that didn’t apply - and membership is checked against the declared set.

$IF @{tier} is one of pro, premium: offer concierge.
  1. $IF - a compile-time branch
  2. @{tier} - a typed input decides it
  3. offer concierge - only ships when true
$IF @{tier} is enterprise:
  1. enterprise - not in the declared set - error

Three sigils, three meanings - every name resolves at compile time to a section, a tool, or a typed slot. A dangling reference stops the build. No silent “see the doc above.”

Follow @<policies>, call @[lookup_order] for @{tier}.
  1. @<policies> - inlines a section
  2. @[lookup_order] - points at a tool
  3. @{tier} - reads a typed input
Escalate via @[open_ticket].
  1. @[open_ticket] - no such tool - the build stops

A closed set makes everything downstream provable: a branch on enterprise is an error, and a switch over the set is checked exhaustive. Membership is set math, not vibes.

Tier IS one of free, pro, premium.
  1. IS - binds the type
  2. one of - a closed set
  3. free, pro, premium - the only legal values
Tier IS gold.
  1. gold - not a member of the set - error

Sections compile to the XML tags models read best; duplicate or vague names are errors. Annotations ride beside the rules and compile away - documentation that costs zero tokens.

<constraints> holds the rules. @@ pricing: says why.
  1. <constraints> - structure the model reads
  2. @@ pricing: - docs for humans - stripped
<misc> holds everything else.
  1. <misc> - vague section name - warns; duplicates error

The compiler knows what it can’t check: vague and hedging phrasing gets an advisory nudge toward a measurable bound. Best practice is a lint rule, not tribal knowledge.

Be concise; keep replies to at most 3 sentences.
  1. concise - unmeasurable - warns
  2. at most 3 sentences - provable - passes

Instructions are plain text because fancy marks hide meaning from the checker: conceals the bound “at most 3” proves; the curly apostrophe conceals the negation. One-click ASCII fixes - human language never flags.

Keep replies ≤ 3 sentences; don’t quote prices.
  1. ≤ 3 - a symbol hiding a bound
  2. don’t - a curly quote hiding a negation

Every line is scored for what it adds over the rest. Saying it three ways warns - and a reducer proposes the minimal prompt that keeps the behavior.

Stay brief. Keep replies short. Be concise.
  1. Stay brief - the claim
  2. Keep replies short - overlap 0.91
  3. Be concise - overlap 0.88

This is why the breakdown matters: objects, actions, bounds, guards, and subjects all land in one solver, so every hard rule is proved against every other - in milliseconds, on every keystroke, no LLM in the loop.

Sky must offer @{upgrade}.
  1. must offer @{upgrade} - P1 - a hard promise
Sky never upsells.
  1. never upsells - P2 - its negation, in disguise

Serve optimized prompts with dynamic
runtime compilation.

Better agents by subtraction: the runtime resolves the business logic - routing, tiers, tool gating - before the model reads a word. The model handles the conversation; the compiled rules handle everything that must never be wrong.

fig. 5 - one session, livestate:: closing · score A
compiled.tgread-only · rebuilt every turn
<role>
You are Sky, a support agent for Acme.
</role>
<pricing>
NEVER state a specific price;
ALWAYS route pricing to sales.
</pricing>
<tools>
@[lookup_order]: look up an order by id.
</tools>
<refunds>
Refunds require an open claim;
route to @[open_claim].
</refunds>
<verify>
Confirm the account email before refunds.
</verify>
<de_escalation>
Acknowledge, own the delay,
give a concrete next step.
</de_escalation>
<account>
Address changes need inbox confirmation.
</account>
<products>
Integrations: Slack, Teams, Zapier.
</products>
<per_tier>
Offer the priority concierge line.
</per_tier>
this turn: 21 of 212 lines
branches, gates, and tiers are resolved by the runtime - never by the model.
Hi - quick question about my account.
Happy to help - what do you need?
How much does the Enterprise plan cost?
Pricing is handled by our sales team - I’ll connect you now. Anything else?
Where’s my order #4412?
It shipped yesterday and arrives Tuesday. Anything else I can check?
Actually - I’d like a refund on that order.
Refunds need a quick identity check - can you confirm the email on the account?
Sure - it’s jordan@acme-corp.com.
Verified, thanks. Claim #310 is open for order #4412 - refunds take 3–5 days once approved.
3–5 days? This is taking way too long. Honestly, pretty frustrated.
You’re right - that’s on us. I’ve flagged your claim for review today; you’ll hear back before 5pm.
OK, thank you. Can I also change my delivery address for next time?
Done - future orders ship to the new address once you confirm it from your inbox.
Got it. One more thing - do you integrate with Slack?
We do - Slack, Teams, and Zapier are built in on every plan.
Perfect, that’s everything. Thanks!
Anytime! And since you’re on Premium, you always have our priority concierge line.

Fewer errors. Business logic leaves the prompt and runs as code - the model can’t misread what it never has to read.

Sharper tool calls. Two tools in context instead of fifty: selection accuracy up, tokens down.

Rules that hold live. Every MUST and NEVER is enforced on each draft - the same rules the checker proved.

One prompt per caller. Each request compiles to its own state and tier, from one source file.

1

A wish, unlike a prompt, cannot be version-controlled.

2

Yes - a monorepo of personalities. It's better than it sounds.

Questions, answered.

Why does my AI agent give different answers to the same prompt?

Most of the time the prompt is the culprit, not the model: it's underspecified or quietly self-contradictory, so the model has room to resolve it differently on each run. TypeGlish type-checks the prompt and surfaces those contradictions, undefined references, and vague or unmeasurable rules as errors before you ship - then you lock the behavior you want with inline $TEST cases.

Model sampling still applies, so pair a robust prompt with a low temperature for the most consistent output.

How do I make my prompts more reliable and robust?

Treat them like code. Write the prompt as a TypeGlish .tg file, run npx typeglish check prompts/*.tg to catch provable defects, npx typeglish score prompt.tg --min B to measure quality against prompting best practices, and add $TEST blocks so a future edit can't silently break what already works.

How do I get an AI agent to consistently follow instructions?

When a model ignores an instruction, the usual cause is a conflicting or buried rule - for example "always be brief" sitting next to a rule that forces long output. TypeGlish proves when two rules contradict and flags vague or unmeasurable language, so every instruction is consistent and checkable before the model ever sees it.

Can I test and version-control my prompts like code?

Yes - that's the whole idea. .tg files are plain text you keep in git, $TEST blocks are inline evals that run against the prompt, and npx typeglish check (add --json) returns machine-readable results with CI exit codes, so a broken prompt fails the build like any other broken code.

What is TypeGlish, and what does "typed English" mean?

TypeGlish is a language and compiler for prompts. You write in near-plain English; it parses your prompt into a typed structure and checks it the way a type system and linter check code - proving contradictions, catching undefined references, and scoring quality - then compiles it down to the prompt the model actually reads. English on the surface, types underneath.

Does TypeGlish make an LLM deterministic?

No, and it doesn't claim to. It makes the prompt robust - removing the contradictions, ambiguity, and unmeasurable rules that make a model behave inconsistently, and letting you catch regressions with tests. For the most consistent behavior, combine a checked TypeGlish prompt with a low temperature.

How do I install and use TypeGlish?

There's nothing to install - it runs on npx with zero dependencies. Use npx typeglish check prompts/*.tg to catch provable errors, npx typeglish score prompt.tg --min B to measure prompt quality, and npx typeglish build prompts/*.tg to compile the final prompt. Add --json to any command for machine-readable output.

Does it work with Claude, GPT, and other models?

Yes. TypeGlish checks and compiles the prompt itself, so the result is a plain prompt that works with any model - Claude, GPT, Gemini, open models, anything. It's model-agnostic by design.

How is TypeGlish different from a prompt template or a framework like LangChain?

Templates and frameworks help you assemble and send prompts, but none of them check whether the prompt is internally consistent. TypeGlish is a type system and linter for the prompt's meaning - it proves contradictions, flags vague rules, and scores quality before a single token is sent. It's complementary: keep your framework and check the prompt with TypeGlish.

Can my AI agent use TypeGlish on its own?

Yes, and it's encouraged. Run it as an MCP server with npx -y typeglish mcp to expose check, build, score, and explain tools to any agent host, or install the Claude Code plugin with /plugin install typeglish@typeglish so the check-score-build loop runs automatically while the agent works.

Is TypeGlish free to use?

Free for noncommercial use under the PolyForm Noncommercial 1.0.0 license, published on npm with zero dependencies. Run it with npx typeglish - no account, no install. Commercial use needs a license.

Get started with TypeGlish.

One line to install. Everything else is in the file.