typeglish
v0.11.0
Published
The TypeGlish language + compiler — turn English into logically consistent, checkable programs for LLMs.
Maintainers
Readme
TypeGlish
Turning English into logically consistent, controlled, maintainable, executable programs for LLMs.
A controlled language with the rigor of a programming language — operators, declarations, types, and a static consistency checker — that compiles English to clean, XML-tagged prompts. Think the TypeScript of English: you still write English, but a grammar, a type system, and a solver have your back.
The long-term goal: make English safe for agents to work with — a static analyzer for the instructions, policies, and knowledge agents consume, run at corpus scale to catch contradictions the way a compiler does for code. This repo is the rules and the compiler for TypeGlish, plus a playground to build and work on the language. See ROADMAP.md for where it's headed.
Why
A prompt is a program written in English — but English has no compiler. Nothing tells you when two instructions contradict each other, when a constraint is impossible to satisfy, or when a reference is undefined. TypeGlish adds that missing layer.
$REQUIRE variable customer_name: string
# Role
You are a customer support agent for Acme. The customer's name is @{customer_name}.
# Constraints
- NEVER state a specific price; ALWAYS direct pricing questions to sales.
- MUST keep every response to at most 3 sentences.
- IF you are unsure THEN say so AND offer to escalate.- Operators — modals (
MUST/NEVER/SHOULD…), control flow (IF … THEN … OTHERWISE), connectives (AND/OR/NOT), and the copula (IS/ARE/AM). Case-insensitive. - Statement terminators — every statement ends with
.!?(a lead-in may end with:); unterminated is a compile error, one-click fixable — the semicolon rule, one level up. The terminator never leaks into a bound value:Tone IS warm.bindswarm. - Layout is syntax — the indent unit is 2 spaces and indentation mirrors section nesting (a nested tag one unit deeper, its content one deeper than that); one space between things. Alignment columns, trailing whitespace, and tabs are compile errors with one-click fixes — "LLM spacing" doesn't compile. Blank lines, by contrast, are free: the compiler packs the emitted prompt — consecutive prose statements join onto one line, a run of blank lines collapses to a single break, and structure (lists, headings, tags, fences) keeps its own line. Fences and literal zones stay verbatim, and cosmetic indent is stripped at compile, so the model reads clean packed text either way.
- A strict character set — the instruction plane is plain ASCII text: em-dashes, curly quotes,
arrows, bullets, math symbols, emoji, and invisible characters are compile errors with
one-click ASCII fixes. Not aesthetics: each mark hides meaning from the checker (a curly
apostrophe conceals a possession contradiction;
≤ 3hides the boundat most 3proves). Human language never flags — accents, scripts, currency stay legal — and symbols keep their literal homes (<examples>,<"…">zones, fences, quotes). - Declarations —
Persona IS a support agentbinds a reusable object AND renders (binding and instruction are the same statement);@{Persona}expands the value elsewhere (case-insensitive). - Adjective axes — every adjective lives on an opposition axis (WordNet-seeded pairs + weighted
spectra:
cold … cool … warm … hot), soTone IS casual.vsSpeak formally.is a proven contradiction wherever the claim sits — copula, adverb (speak fast), be-imperative, or a bare fragment (Formal tone only.). Claims land on their owner (youby default — a prompt is instructions), graded by the frequency square (Sometimes …× 2 stays legal). Vocabulary is declarable:$CONFIG adjective ornatedefines a word onto an axis (the pair rule as schema law), and an undefined adjective in a claim seat is an error by default — dialable per project. A declaration whose pointer also expands puts the value in front of the model twice — the checker flags it (prompt/duplicated-declaration). - Adverbs, classified — degree words are transparent where they should be and ONLY there:
Speak very formally.still contradictsSpeak casually., whileNever be too casual.stays compatible withBe casual.(banning the excess is not banning the quality). Canon verbs carry their manner (Reply formally.collides withRespond casually.— one action, both sides), and every adverb family — degree, manner, delay, rate, sequence — answers one classification, with frequency/focus derived from the keyword layer, never duplicated. Degree words DO THE MATH: a claim is an interval on its axis,veryshifts it toward the pole,slightlybounds it — soslightly warmvsvery warmis a proven contradiction, and one truth per axis holds:The tool is very fast.thenThe tool is fast.flags the looser value as dead weight. - Types —
Animals ARE one of alligator, orangutandeclares a closed, mutually-exclusive set. - String literals —
"…"escapes to literal prose: no operators parsed inside, quotes stripped on compile,@{refs}still expand. - Sections & structure — group with
#headings or<xml_tags>(same-line and inline content welcome);<$name>strips control-plane scaffolding,<?name>strips just the wrapper tag,<"name">keeps its body verbatim (a literal zone for reference tables and syntax samples — quotes are the one escape, here at section scale), and<examples>is the literal zone by name. Structure is enforced: content must live in a section,#headings must sit inside XML sections once you use any, and section names must be distinct and meaningful — duplicate/near-duplicate names error, vague ones warn. - Definition lists —
term:: definitionrows for glossaries and cheat-sheets: position-insensitive to author (agents never break alignment), parse-literal (samples inside never open zones or bind) yet content-checked — one statement per line holds inside a body (the editor wraps; a hard-wrapped continuation is a compile error), terminators and the character set apply — and the compiler owns the emitted layout (nested sub-bullets, fenced samples) so the model reads clean markdown regardless of authoring; duplicate terms flagged. - Statement annotations & references —
@@ a notedocuments the statement below it (a docstring that compiles away and is inert to every analysis tier);@@ name: …also names that statement. References are typed sigils, each validated at compile time:@{variable}reads host state,@<section>inlines a section,@[tool]references a tool — inline it emits the checked bare name, alone on a line it surfacesname — description(a dangling reference is an error, and an exact tool name in bare prose is too: point it or quote it). - A consistency checker — propositional + SMT (Z3). Catches
MUST escalatevsNEVER escalate, and numeric contradictions likeat most 3 sentencesvsat least 5 sentencesthat propositional logic provably can't decide. Its quantifier layer gives the English of quantity its logic: the square of opposition (all/some/no/most/only), set sizes (there is only one toolvsthere are many tools; nouns fold by lemma, sopeople⇄person), grammatical number as a claim (one dogcounts exactly one — counting things is exact, floors are explicit (at least,more than,over); a bare plural own claims 2 or more, soYou have one dog.vsYou have dogs.is a contradiction;a dogstays existential — an instance, never a count — and a modifier never blocks the read:great dogsstill counts dogs, withgreatlanding as a quality claim on the same slotYour dogs are great.claims), action counts (call five toolsvscall six tools— one count per directive slot), supply vs demand (there is only one toolvscall five tools), definite reference (the toolwith two tools declared), and transitive syllogism chains. Claims key by what they are about (noun + restrictor tail); frame, subject, and owner are refinement fields that must agree where present but never exempt a claim from checking. Directives default to the ADDRESSEE — a bare rule,You must…, a declared alias (You are Claude.→Claude must…), and a declared role (You are a support agent.→the support agent must…) are one subject's rules, while an undeclaredthe assistantstays scoped (no guessing; opt nouns in via glish.tgc$CONFIG addressee one of …) andthe useris never you. Guards parse in both spellings —IF … THEN …and the comma form English writes (If the user is angry, never quote prices.);Don'treads asDO NOT. - A predicate system — actions parse into
verb + theme + roles + tool bindingagainst a curated catalog, so the checker also proves verb-level facts: synonyms merge (output/print), antonyms conflict (MUST outputvsMUST suppressflips the badge), bare imperatives are obligations, andfire book_appointment≡call book_appointment. - A semantic auditor (advisory) — per-line residual value and overlap, scored at the atom level by a local NLI model with an LLM-judge escalation (every verdict distilled as training data for an offline replacement). Heavy duplication warns; proposed cuts are behaviorally verified; a greedy reducer consolidates toward a minimal, orthogonal prompt.
- The TG score — one proof-backed number per prompt (
typeglish score, CI-gateable with--min B). Proven errors gate to F; graded facets measure enforceability (the checker's own visibility), hardness (how binding the language is — force words on a spectrum,must/never/allat the poles,may/sometimesin the soft middle; 1.0 renders pure bold), consistency, structure, annotation coverage (every statement should carry a@@note saying what it's for), style, security, and density; every deducted point cites a finding + fix (the ledger is the to-do list). Calibrated against an authored good/bad corpus with a tested separation margin. $TEST— the file carries its own eval suite — cases whose- input::/- expect::bullets live beside the rules they test, or in an auto-discovered sibling<name>.test.tg, and compile away entirely. Deterministic asserts (contains,matches,at most N sentences) are code-checked; prose expectations are judge-scored. Run them withtypeglish test(--dryvalidates the suite + reports rule coverage fully offline; live runs needANTHROPIC_API_KEY); feeds coverage (prompt/untested-rule), the profile, promptfoo emit, portability, and the optimizer.$EXAMPLE— the file carries its own few-shot — one exchange per block (- input::/- good::, optional- bad::), the same bullet record. Each- good::compiles into the prompt as a labelled few-shot exchange;- bad::is held out — never shown to the model, so it can't be imitated. A malformed block is astructure/bad-exampleerror.- The profile — per-statement attribution: what a line says (IR, predicates),
what duplicates it (residual), which cases exercise it, and the measured ablation Δ per case
(
npm run profiles -- --behavioral, budget-confirmed; repo dev tool). Those fold into a per-statement verdict — potency (Δ) crossed with redundancy (residual) and coverage → keep / cut (implied or dead) / test, with attribution and honest degradation when a tier hasn't run.npm run annotate(repo) writes it into the margin as//@machine comments — compile-stripped, human comments untouched. Shared statement hovers use snapshot-qualified occurrence IDs; optional evidence is bound to the full source/configuration, and measured behavior also needs runner, judge, bindings, imports and rubric provenance. - Portability, measured —
npm run portability(repo dev tool) runs the suite across a model panel (promptfoo owns the matrix; TypeGlish owns zero dependencies) and reports cross-model agreement WITH per-model pass rates beside it — consistency ≠ correctness is encoded, not footnoted. - A dependency map — the playground's Map view renders the prompt as the graph it already is:
the depended-upon symbols (inputs with their domains, tools, variables, tests) on one side, the
section skeleton with a reference chip on every wired line on the other, and hover to light up an
edge — where a symbol is used, or what a line depends on. Broken edges (a
<closing>that resolves to nothing, an unused import) are marked in place, from the same resolution engine as the squiggles, so the map can never disagree with the checker. Deterministic and free. - A fix list, ranked by score impact — the playground's Fixes panel is the score ledger made actionable: every deduction below 100, ordered by the points it costs, pinned to its line, with a one-click fix where one exists. Deterministic and free — it updates as you type, and it never spends a model call. This is "highlight what needs changing," computed, not judged.
- A GEPA optimizer with the compiler in the loop —
npm run optimize(repo dev tool; a deliberate CLI opt-in — the editor highlights, the human changes) runs reflective prompt evolution (per-instance Pareto, minibatch acceptance) where provably-broken mutants are rejected free before any eval spend, mutants that touch their own$TESTblocks are rejected as fitness hacking, and the accept rule breaks ties toward fewer tokens — the bloat spiral of naive optimization loops structurally loses. Every model call (evals AND reflections) is charged against the budget, so the pre-run quote is a hard spend ceiling; the result lands as a new.optimized.tg, never overwriting the seed. - Conditionals, resolved — deterministic
IFchains / guarded sections make a prompt a template:(source, state) → prompt. Two stages, one seam (prepareTemplate): render is state-free and total — an unresolved chain compiles to conditional English (If voice is true: … / Otherwise: …), never a leaked sigil; resolve is state-dependent and partial — it collapses only the branches the bag can decide. The playground's values panel exposes it: set an input and the Compiled view resolves that chain to the one branch the model gets; leave it unset and it stays conditional. - A compiler —
text → parse → IR → check → emit:@{references}expand, comments /@@notes /$-commands /$TESTblocks strip,$EXAMPLEblocks emit their good few-shot (bad held out), and structure is preserved as authored —# headingsstay markdown,<tags>stay tags (the two are never interconverted; they carry different signals). - Agent files — a single
.tgfile can be a whole agent:$CONFIGsets the model and settings,$TOOL/$SERVICEdefine tools as YAML-shaped code with HTTP bindings, andcompile()emits the prompt plus an agent bundle (settings + JSON-Schema tool definitions).npm run tg:run agent.tgruns it live — tool-use loop, bindings, and{env.*}secrets included.
The full language reference is in SPEC.md.
Quickstart
The package ships runnable examples in examples/ — real agents, checked in CI so they can never rot.
Five commands from install to a deployable prompt:
npm i -D typeglish
npx typeglish check node_modules/typeglish/examples/support-agent.tg
npx typeglish check node_modules/typeglish/examples/support-agent-draft.tg
npx typeglish score node_modules/typeglish/examples/support-agent.tg --min B
npx typeglish build node_modules/typeglish/examples/support-agent.tg
npx typeglish test node_modules/typeglish/examples/support-agent.tg --dry
npx typeglish --explain prompt/hedgingThe two support agents are the same agent twice: the draft is hedged prose ("Try to avoid…",
"kind of short") and the linter lights it up; the final commits to modals and scores an A. build
writes the deployable artifact plus a hash manifest; --explain tells you what any finding means and
how to fix it. Then write your own agent.tg and run the same loop.
The repo
src/core/ the language + compiler (pure TS — runs in Node, browser, and tests alike)
typeglish-grammar.ts the grammar (Peggy / PEG)
parser.ts, ast.ts parse a clause → statement AST
ir.ts lower the AST → logical-form IR (directives, conditions, numeric bounds)
layers/ ONE FILE PER SEMANTIC LAYER — assignment, possession, definiteness,
quantity, frequency, deontic, order, redundancy, references, statements,
style, spelling; the registry (layers/index.ts) welds the roster
world.ts the world model: the object graph + aliases + addressee → buildWorld()
diagnostics.ts the orchestrator: build the world once → run every layer's checks
prompt.ts render + PromptDoc (what the model reads; the ablation API)
compile.ts render → semantic XML + the agent bundle
tool.ts $TOOL / $SERVICE definitions (tool schemas + HTTP bindings)
agent.ts the runner: the tool-use loop + HTTP bindings (Node-side, like z3)
solver/z3.ts the SMT consistency checker (deontic + numeric)
predicate.ts, verbs.ts the predicate system: action parser + the curated verb catalog
semantic/ the advisory tier: atoms, NLI engine, judge + distillation, reducer
src/lib/ the published surface — the `typeglish` bin (check / build / score / test / resolve /
reference / mcp / claude-hook; cli-surface.ts is the table --help derives from),
build.ts (artifact + hash manifest + the scaffolding failsafe), deploy-gate.ts,
claude-hook.ts, test-run.ts (the $TEST runner), project.ts (glish.tgc discovery),
`typeglish/monaco`, `typeglish/spell`, `typeglish/vf` (the platform boundary:
vf-read.ts + voiceflow-write.ts, severed from the compiler — ~24 kB, not 2.36 MB)
src/mcp/ the MCP head — `typeglish mcp`: one stateless tool per served verb (check, build,
score, review, explain, statements, prepare-rewrite, brief-rewrite, review-rewrite, reference — MCP_TOOLS in cli-surface.ts), results
byte-shaped like the CLI --json (the LSP pattern, one transport over)
lsp/ the stdio LSP head (editor language services)
plugin/ the Claude Code plugin — skill + PostToolUse check hook + PreToolUse deploy gate
(served by .claude-plugin/marketplace.json at the repo root)
docs/agents/ TYPEGLISH.md — the GENERATED agent language reference (AGENTS.md-style consumers)
examples/ runnable sample agents — shipped in the package, compiler-locked in CI (examples.test.ts)
src/web/ the playground — a Monaco editor that consumes `src/lib` (it's the first consumer).
ONE app, TWO surfaces (hosted.ts): localhost = the dev cockpit (Copilot, Test runs,
the dogfooded copilot.tg seed); any build = the PUBLIC playground (curated seeds,
no server features). brand/ is the Sandcastle design system vendored from
typeglish.dev (npm run sync:brand)
server/ the Node side (LLM calls + the server-side Z3 logic check) as Vite middlewareChanging the language? Every operator, type, character rule, and diagnostic code lives in exactly one
single-source-of-truth module — see the SSOT registry in CLAUDE.md (invariant #1). Edit
the SSOT, run npm run gen:syntax, and let the drift-lock tests tell you what else to update; a hardcoded copy
anywhere else fails CI.
Install
Zero-setup — the CLI has no required runtime dependencies (the Z3 proof tier is an optional
dependency npm fetches by default; --omit=optional skips it), so npx just works:
npx typeglish check prompts/ [--json] # exit 1 on any provable error (a directory checks
# every .tg inside, fileset manifest order first)
npx typeglish check prompt.tg --fix # apply the declared-safe fixes in place first (whitespace,
# the character set, terminators), then check the result
npx typeglish review prompt.tg # one run, one report: findings, the rules table, the score,
# and the fixes ranked by projected gain — in the order you act
npx typeglish build prompts/ # everything that passes → .typeglish/dist/*.txt
# artifacts + a hash manifest; refusals named
npx typeglish fmt prompts/ --check # mechanical formatting gate (in place without --check)
npx typeglish score prompt.tg --min B # the TG score; exit 1 below the floor (the CI gate)
npx typeglish resolve prompt.tg --vars '{"tier":"premium"}'
npx typeglish --explain logic/contradiction # what any diagnostic code (or score facet) means
npx typeglish --explain enforceability prompt.tg # …and, with a file, where it applies: the lines, the rules table
npx typeglish mcp # the same tools over MCP stdio (for shell-less agent hosts)
npx typeglish lsp # the language server over stdio (VS Code / Neovim / Zed / any LSP editor)build is the deploy contract: the artifact — compile(source).xml — is what a prompt-taking system
(a system prompt, an assistant API, Voiceflow) should receive, and the manifest proves any payload
byte-for-byte ("this exact prompt came out of a passing compile"). A file with a blocking error
refuses to build; nothing failing can enter the manifest.
In a project, npm i -D typeglish gives you the full checker: z3-solver (the SMT proof tier —
contradictions, unsatisfiable bounds, exhaustiveness) is an optionalDependency, so npm installs it
by default and degrades gracefully where it can't. Spell-check stays opt-in:
npm i -D typeglish # full tier out of the box (z3 auto-installs)
npm i -D typeglish --omit=optional # lean install — skip the 33MB z3 WASM
npm i -D nspell dictionary-en dictionary-en-gb # + spell-check (the typeglish/spell module)Without z3-solver, check runs the synchronous tier (still the full language/consistency/structure
check) and says so on stderr — a degraded checker is never silent. Requires Node ≥ 20.19.
Embed in your product (Monaco)
Any product with a Monaco editor gets the full TypeGlish experience — syntax highlighting, the object-record hover (each card ends in its relations map — painted, line-linked edges), completion, live consistency squiggles, object-reference highlighting, and list/operator authoring — in a couple of calls:
import * as monaco from 'monaco-editor';
import { setupTypeGlish, attachTypeGlish } from 'typeglish/monaco';
import 'typeglish/monaco.css';
setupTypeGlish(monaco); // once per monaco instance
const editor = monaco.editor.create(el, { language: 'typeglish', theme: 'typeglish-dark' });
attachTypeGlish(monaco, editor); // per editor → squiggles, highlights, authoringNeed just the compiler (no editor)? The core runs anywhere — Node or browser:
import { compile } from 'typeglish';
const { diagnostics, consistent, xml } = compile(source);Every rule lives in the framework-agnostic core; typeglish/monaco only wires it to Monaco's
APIs. The playground in src/web/ is the first consumer of exactly this surface.
Live playground sync with your coding agent
Choose Connect CLI in the playground, copy its agent prompt, and give that prompt to your coding agent on the same computer. The prompt asks the agent to do two things:
npm install --global typeglish@latest
typeglish sync --connect <details> --agent "<agent name>"The connection details bind the CLI to this tab's origin, loopback port, and secret. The agent keeps the sync command running and the playground connects automatically. There is no pairing link to paste back, no folder to select, and no request to move the agent into another project. The current playground folder supplies its prompts to an isolated session workspace created by the CLI; no existing local folder is searched or imported. Keep the tab and command running. Waiting survives a tab reload.
Automatic connection details and sessions do not expire with time. Copying the prompt refreshes its secret and port; copy it again for the agent if its port is occupied. Cancel connection stops waiting. Neither action stops an unrelated local process. Treat the prompt's connection details as private. An explicit retry with the same details recovers a lost pairing response without creating a second session.
For manual setup, npx typeglish sync ./prompts --origin https://typeglish.dev still prints a
five-minute pairing link. Expand Use a pairing link instead in the dialog to paste it. Use the
exact playground origin (including a development port when applicable).
Your coding agent works on ordinary local .tg / .tgc files; the playground picks up edits,
additions, renames and deletions. Browser text edits save back to the same files. No account,
model credentials, remote relay or embedded agent is involved.
The agent waits for instructions after connecting. Subsequent edits use the session workspace path
printed by the CLI. That workspace is stored in the OS temporary directory and retained on exit for
recovery; export work you want to keep permanently. Explicit folder sync remains available with
typeglish sync <folder>. File-management actions in the playground are disabled while paired;
make those changes through your agent.
The bridge binds only to 127.0.0.1, accepts one paired session and the specified browser origin,
and skips symlinks, hidden directories, node_modules, dist, and vendor. It supports up to
512 files, 512 KiB per file, 8 MiB total, and 20 folder levels. Manual pairing links expire after five
minutes; disconnecting revokes the session and original connection details. Automatic pairing
has no time limit; for an expired manual pairing link, restart the command.
Concurrent browser/disk edits produce a conflict with both versions visible. Choose which version
to keep. Browser writes check the last disk revision and retain before/after recovery copies under
.typeglish-sync in the folder; these copies may contain sensitive prompt text, are never synced,
and are excluded from Git by a local ignore file. Filesystem writes by other processes do not share a lock, so
revision checks cannot rule out a change in the final check/rename interval. Prefer one active
editor per file. On a network interruption, the same open bridge reconnects automatically.
The connection and unsent drafts survive reload in that tab's session storage. The pre-connection
playground draft has a separate recovery slot: disconnect, then choose Restore previous
playground draft. Closing the tab ends that recovery lifetime; save or download unsent work first.
Hosted pages access the loopback bridge directly and may require local-network permission; browser
policies can block this. If connection fails, allow that permission or use a local playground with
an exact matching --origin. No prompt content is uploaded to the hosted site by this bridge.
For an unreleased source checkout, substitute node --import tsx src/lib/cli.ts for
npx typeglish (or node dist/cli.js after npm run build:lib).
Portable authoring tools
The playground, language server and package share the same authoring operations:
import { analyzePrompt, annotatePrompt, cleanPrompt, refinePrompt, selectPromptContent, compressPrompt, consolidatePrompt, refreshAnnotations, applyPromptEdit } from 'typeglish';
const analysis = analyzePrompt(source); // diagnostics, explanations, grammatical words, classification coverage, score, cache
const proposal = annotatePrompt(source); // add missing @@ notes; preserve existing notes
const annotated = applyPromptEdit(source, proposal); // refuses a stale source snapshot
const cleaned = cleanPrompt(annotated); // formatting + declared-safe repairs
const refined = refinePrompt(annotated); // supported statement refinements; authored notes stay intact
const selection = selectPromptContent(refined.text); // independent editorial selection + source-span decisions
const compressed = compressPrompt(refined.text); // selection + shorter realization in one plan
const conservative = compressPrompt(refined.text, { policy: 'conservative' }); // no editorial content selection
const consolidated = consolidatePrompt(compressed.text); // remove covered policies; inspect surviving-witness receipts
const refreshed = refreshAnnotations(annotated); // explicitly replace repairable existing notesStatement optimization uses an explicit concise-instruction policy: it can reduce selected rhetorical
emphasis, shorten assigned-goal framing and consolidate adjacent repeated standing guidance. These
edits retain supported actions and scope, but intentionally relax some effort/quality maximization;
they do not promise identical model behavior. contentSelection and selectionRounds receipts identify those choices; each round carries its own source snapshot.
Unknown constructions and authored annotations remain intact. Neither operation uses an LLM.
Consolidation is independently callable and runs between compression and organization in the import
pipeline. It preserves unique content and removes only supported covered policy sentences; repeated
events, unresolved references, timing differences and authored notes remain protected. Receipts name
an earlier surviving witness for every omission. The CLI and explicit LSP
source.consolidate.typeglish action share the same core edit plan and original source guards.
explainStatements(source) also exposes the existing parser result and span-carrying compiler facts.
These explanations are deterministic readings, not proof of arbitrary English equivalence. Existing
notes can be current, stale, or unverified; missing notes do not warn. Annotation coverage in the TG
score still measures presence, not accuracy. The default compile behavior is unchanged; opt in with
annotationChecks: true, or use analyzePrompt. coach: false removes annotation advisories.
analyzeWords(source, { glishConfig }) exposes the shared grammatical records independently:
original token spans, lexical candidates, contextual class, lemma and evidence. Hover, highlighting
and word profiles use these records too. classificationCoverage(source) reports resolved,
ambiguous and unknown tokens separately; it measures classification coverage, not semantic
understanding or classification accuracy. Unknown vocabulary is never silently labeled a noun.
The editor APIs accept the same WordAnalysisOptions: semanticTokens(source, options),
cardAt(source, line, column, options), hoverAt(source, line, column, alerts, options) and
wordObjects(source, tokens, options). Monaco's attachTypeGlish accepts a live analysisOptions
getter; call refreshAnalysisContext() after project configuration changes without a text edit.
analyzeStatements(source, options?) provides the central statement metadata used by statement
hovers and agent tools. Each record includes exact source, occurrence ID, annotation, section/scope,
parsed clauses, grammatical words, facts, metrics, diagnostics and explicit relationships.
inspectStatement(analysis, idOrZeroBasedPhysicalLine) returns one record with related statements
and entities. Unknown or stale IDs return null.
import { analyzeStatements, inspectStatement } from 'typeglish';
const statements = analyzeStatements(source, { glishConfig });
const first = statements.statements[0];
const context = first ? inspectStatement(statements, first.id) : null;The CLI typeglish statements prompt.tg --json, MCP typeglish_statements, and LSP request
typeglish/statementAnalysis expose this same read-only contract. CLI/MCP line selectors are
one-based physical lines; core/LSP coordinates are zero-based. The analysis is document-scoped,
does not expand imports, and invokes neither a model nor Z3. Semantic readings remain explicitly
partial; absent, stale and unavailable evaluation results are distinct. attachStatementEvidence
can add results bound to their original statementSnapshot, without re-running core analysis.
Oversized documents retain source and statement inventory while enrichment is marked unavailable.
For agent-authored rewrites, prepareRewrite(source, options?) produces compact context bound to
that source and configuration. The agent supplies candidate wording, then
reviewRewrite(source, candidate, { baseline: preparation.snapshot, mappings? }, options?)
checks the binding, reports source and context changes, and compares diagnostics and estimated
rendered tokens. Optional mapping groups contain sourceIds, candidateIds and a reason;
prepare the candidate with the same options to obtain its occurrence IDs. Mappings declare
provenance and do not prove preserved meaning. Unsupported edits remain unverified. Neither
operation writes files, calls a model, applies the candidate or runs the authoring pipeline.
npx typeglish annotate prompt.tg # print proposed source
npx typeglish annotate prompt.tg --refresh # explicitly refresh existing notes
npx typeglish cleanup prompt.tg --check # read-only drift check
npx typeglish cleanup prompt.tg --fix # apply mechanical cleanup
npx typeglish refine prompt.tg # preview supported statement refinements
npx typeglish refine prompt.tg --fix # apply the refinement plan
npx typeglish compress prompt.tg --json # preview statement rewrites and word savings
npx typeglish compress prompt.tg --fix # apply the compression plan
npx typeglish consolidate prompt.tg --json # preview covered-policy removals and retained witnesses
npx typeglish consolidate prompt.tg --fix # apply the consolidation plan
npx typeglish analyze prompt.tg --json # document report, synchronous tier
npx typeglish statements prompt.tg --json # shared metadata for all statement occurrences
npx typeglish statements prompt.tg --line 4 --json # statement + related context at physical line 4
npx typeglish prepare-rewrite prompt.tg --json > baseline.json # save context for your agent
npx typeglish review-rewrite prompt.tg --candidate candidate.tg --baseline baseline.json --jsonThe corresponding MCP tools are typeglish_prepare_rewrite and typeglish_review_rewrite.
Preparation returns {file?, preparation}; review returns {file?, review}. Review accepts the
baseline snapshot, candidate source, optional candidateSnapshot, and optional mappings.
CLI --baseline accepts a saved preparation envelope, core preparation or bare snapshot;
--mapping accepts a JSON array. Invalid requests and stale bindings exit 2; a valid review with
changes exits 0. MCP marks invalid reviews with isError while retaining the structured report.
All authoring commands leave files unchanged unless --fix is supplied. The report describes one
document; compose imports first for a composed prompt. Token counts are estimates and cacheability
is potential prefix reuse, not measured provider cache hits. It does not run Z3 or an LLM.
The LSP offers explicit source actions for adding annotations, refreshing annotations, cleanup, refinement and statement compression, plus annotation warnings with individual Refresh annotation quick fixes. Source actions require versioned-edit support; older clients retain legacy quick fixes. The playground supplies controls, worker transport, draft storage and undo.
Develop
npm install
npm run dev # the playground at http://localhost:5273 (VITE_HOSTED=1 previews the public surface)
npm run sync:brand # re-vendor the Sandcastle design tokens, components + logo from typeglish.dev
npm run typecheck
npm test # ~2,300 unit tests cover the language, checker, score, and semantic kernel
npm run check -- prompt.tg [--json] # compiler errors + warnings, agent-friendly (exit 1 on errors)
npm run score -- prompt.tg [--full] [--min B] # the TG score + ledger (exit 1 below --min: the CI gate)
npm run tg:test -- prompt.tg [--dry] [--min 0.7] # run the suite (inline + sibling .test.tg): per-case pass/fail
npm run fmt -- prompt.tg [--write] # join wrapped fragments, split compound rules
npm run profiles -- prompt.tg [--behavioral --yes] # per-statement attribution (static free; Δ costs, confirmed)
npm run annotate -- prompt.tg [--write|--clean] # write the profile into //@ margin comments
npm run emit -- promptfoo prompt.tg # the file's $TEST suite as a promptfoo config
npm run portability -- prompt.tg --models a,b # cross-model agreement (then --results out.json)
npm run optimize -- prompt.tg --budget 24 [--estimate] [--json] # GEPA; --estimate quotes cost (free), --json = agent-readable result
npm run semantic -- prompt.tg # per-line residual/overlap audit (add --reduce, --actions)
npm run semantic:bench # regenerate the golden-pair benchmark reportThe compiler core has no browser dependencies, so the grammar, the IR, and the solver behave identically in Node, the browser, and Vitest.
Agents in the loop
The compiler talks back to agents at the moment their work SETTLES — the turn boundary — never
mid-typing (half-written text is legitimately broken). The mental model everywhere is TypeScript for
prompts: the .tg is source in git, the deployed prompt is a build artifact, and the gate between
them is mechanical.
In Claude Code, install the plugin and the whole loop runs itself:
/plugin marketplace add Bradenream/TypeGlish /plugin install typeglish@typeglish- A skill teaches the workflow — author
.tg→ check → score → build → deploy the artifact verbatim — with the generated language reference as its deep material. - A PostToolUse hook re-checks every settled
.tgedit and feeds blocking diagnostics straight back to the model as fix-now feedback (advisories ride along as context; clean files stay silent). - A PreToolUse deploy gate hashes outgoing prompt payloads (Voiceflow MCP tools by default;
configurable via
.typeglish/deploy-gate.json) against.typeglish/build-manifest.json: a prompt no passing build produced is denied with the fix in the message, and a verified one still goes through the normal permission flow — the gate never auto-approves. Escape hatches:TYPEGLISH_DEPLOY_GATE=off|ask.
- A skill teaches the workflow — author
In the playground, the Copilot gets a turn-end compiler report: when its edits land, the merged diagnostics are diffed against the turn-start snapshot and reported — as a ⚙ row in the transcript and as compiler truth in the model's history. A turn that introduced errors triggers one automatic fix round (toggleable, never recursive).
Any other agent with a shell (Cursor, a CI bot, …) gets the same truth from
npx typeglish check|score|build --json— data on stdout, diagnostics on stderr, exit codes as gates,typeglish --explain <code>for what any code means. Wire the same loop into any hook system.Shell-less hosts (claude.ai, any MCP client) get the compiler through ten MCP tools —
npx -y typeglish mcp— check / build / score / review / statements / brief-rewrite / prepare-rewrite / review-rewrite / explain / reference, with results byte-identical to the CLI's--json. A host with no filesystem passessourceinline;buildreturns the artifact + its sha256 without persisting.Teaching the language:
typeglish referenceprints the generated agent reference (docs/agents/TYPEGLISH.md is the same document, committed for AGENTS.md-style consumers), so an agent that has never seen TypeGlish can learn it in one read.
License
TypeGlish is licensed under the PolyForm Noncommercial License 1.0.0: free for personal, research, education, and other noncommercial use. Any commercial use requires a commercial license from Voiceflow, Inc. Versions through 0.7.2 were published under Apache-2.0 and remain available under that license. Third-party attributions (Open English WordNet CC-BY-4.0, Princeton WordNet, and others) are listed in NOTICE.
