> Markdown rendition of https://typeglish.dev/ ("TypeGlish - write and scale system prompts like code"). Canonical page: https://typeglish.dev/ · All pages: https://typeglish.dev/llms.txt

# 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]

```
npx typeglish check prompts/*.tg
```

fig. 2 - the checker, live ✓ compiles · score B (89/100) · gate PASS

```
$CONFIG model claude-opus-4-8
$IMPORT input tier: one of free, pro, premium

$TOOL lookup_order
  - description:: Look up an order by its id and return its status.
  - input::
    - order_id:: the order's id.

$TEST no_pricing
  - input:: How much does the Enterprise plan cost?
  - expect::
    - no specific price is stated
    - redirects pricing to the sales team

<role>
  @@ persona: name the agent once, then reference it everywhere
  Persona IS a support agent for Acme, a SaaS analytics company.
  You are @{persona}.
</role>

<constraints>
  @@ pricing: pricing is a sales conversation, never a support one
  NEVER state a specific price; ALWAYS direct pricing questions to sales.
  MUST keep every reply to at most 3 sentences.
  WHEN an order needs live data THEN call @[lookup_order].
</constraints>

<per_tier>
  @@ tiering: the prompt reshapes per caller tier, resolved at compile time
  $IF @{tier} is one of pro, premium: Offer the priority concierge line before closing.
  $ELSE: Invite the customer to upgrade for priority support.
</per_tier>
```

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 ✓ A (97/100)

```typeglish
$IMPORT file "./brand.tg" as brand

# Role
@@ role: one sentence says who is speaking
You are Sky, a support agent for Skyline Air.

# Constraints
@@ voice: the shared house voice, spliced in from brand.tg at build time
@<brand.voice>
@@ policy: a refund answer without its policy line is an opinion
- WHEN a customer claims a refund THEN MUST cite the fare policy.
@@ no_invention: the policy is the airline's, never the agent's
- NEVER state an invented policy.
@@ upsell: an upgrade offer is fine, except to an angry customer
- UNLESS the customer is angry THEN MAY offer an upgrade.
@@ angry: an angry customer gets empathy, never a pitch
- WHEN the customer is angry THEN NEVER offer an upgrade.

$TEST refund_720
  - input:: I want a refund on my $720 ticket.
  - expect::
    - cites the fare policy
```

The entire agent - the role, the rules with a `@@` note each saying why, the exception carved out of its default with disjoint guards, and the test that pins it - in one readable file. Checked by CI against the real compiler on every commit.

fig. 3.10′ - brand.tg ✓ A (95/100)

```typeglish
# Role
@@ voice: one voice for every agent; support.tg and sales.tg both import this file
You are the house voice for Skyline Air.

# Voice
@@ case: sentence case reads as a person, not a billboard
- MUST write in sentence case.
@@ length: a reply under 120 words gets read on a phone
- SHOULD keep every reply under 120 words.
@@ jargon: IATA codes and fare classes mean nothing to a traveller
- NEVER use jargon.

$TEST delayed_flight
  - input:: Why is my flight delayed?
  - expect::
    - at most 120 words
    - explains the delay plainly
```

The module it imports - `@<brand.voice>` splices the `# Voice` section into the built prompt, so one file carries the voice for 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, live state:: closing · score A

compiled.tg read-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](https://www.npmjs.com/package/typeglish) with zero dependencies. Run it with `npx typeglish` - no account, no install. Commercial use needs a license.

## Get started with TypeGlish.

One command prints the whole language, and its first section is a copyable skeleton. Read that, write a `.tg` file, run `npx typeglish review` on it. Have a prompt already? `npx typeglish import prompt.md --out prompt.tg` brings it in as it is and prints the checker's worklist. Or hand the setup to your coding agent.

```
npx typeglish reference
```
