14 · Day-to-day
What's the editor, debugging, and error experience?
A real language server: glyph lsp speaks the Language
Server Protocol over stdio, so any editor lights up: highlighting,
diagnostics, hover types, go-to-definition, completion, format-on-save. A VS Code extension
ships ready-made. Errors are held to an Elm-quality bar: a stable code, the
fix, and the reason.
Why it matters
Editing a language your editor doesn't understand feels like a step backward, and early on that was a fair complaint. It isn't anymore: your editor gives you the modern niceties, and the errors are written to teach, not just to reject.
Is it a real LSP, or just a VS Code plugin?
A real one. The compiler ships a language server you launch as glyph lsp,
which speaks the Language Server Protocol over stdio (the standard JSON-RPC
transport every LSP client understands). The bundled VS Code extension is a thin client
around it, but nothing is VS-Code-specific: point any LSP-capable editor,
Neovim, Helix, Zed, Emacs (eglot), at glyph lsp and it behaves
the same. It's the same binary you already installed (npx @glyphlang/glyph lsp),
so there's no separate server to fetch.
The server implements what an editor needs: live diagnostics with
the stable codes below, hover types, go-to-definition
(including across modules), completion, document and workspace symbols for outline
and symbol search, workspace-wide find-references, rename, and formatting in the
canonical glyph fmt layout for format-on-save. Rename is complete: renaming a module-level declaration edits
its definition, every reference, and each importing module's import binding
across the workspace, and it checks the new name (a real identifier, not a keyword) before
touching anything. Two limits worth knowing: the cross-file index is rebuilt on each request
rather than cached (fine for a normal project, worth speeding up later), and a file that
doesn't currently parse is skipped, so a rename won't reach references inside a file that's
itself broken.
See it
Every diagnostic carries a stable code, a one-line fix, and often the design reason. glyph --explain prints the long form for any of them:
[E0203] the `?` operator propagates error type `HttpError`, but this function returns `Result<_, string>` ╰─ Help: map the error first, e.g. `.map_err(...)`, so its `E` matches the function's.
We tightened the messages exactly where agents and newcomers stumble most: writing
if now points you at match instead of a generic
“unexpected token,” and a wrong argument count, a non-exhaustive
match, or a mistyped variant each get their own coded, fixable error.
Type errors that only the TypeScript back-end can catch used to point at the generated
.ts. Now they're mapped back onto your .glyph
(same ariadne caret, same stable code), so a tsc error reads like
a native Glyph one.
Debugging: you debug the emitted TypeScript with the tools you already use,
like Node's inspector, browser devtools, or your IDE's JS/TS debugger. Every build ships a
standard v3 source map beside each .ts (with the Glyph source
embedded), so a debugger or a bundler that chains maps steps back into your
.glyph line numbers. One thing it doesn't cover yet: glyph run's
own crash stack still prints .ts lines, because tsx doesn't chain
the map through its transform. Remapping that stack is next.
Asking “does this compile?” without running it:
glyph check path.glyph type-checks one file, or a whole directory, and
exits. It writes nothing into your tree, which
glyph run could not promise and glyph build would not do at all
(it refused anything that wasn't a directory). It runs the same pipeline a build runs,
including tsc --strict over the emitted TypeScript, into a temp directory it
deletes on the way out. --no-tsc stops after the Glyph stages when you want the
fast answer; --json gives an agent the same diagnostic shape
glyph build --json does. A file is checked in the context of its own
directory, so a broken sibling still fails the check. Your @example and
@doc @run tests run here too, the same gate glyph build
uses, so a check cannot report a clean tree that a build would fail;
--no-test skips them when you want an answer that executes nothing.
The transcript doesn't lie to you either. A failing build used to open
with “no diagnostics” and then print tsc errors underneath it,
because the Glyph-stage summary was written before the TypeScript stage ran. The summary
now prints after every stage that can turn the build red, so a red build never shows a
green line. In the same pass, glyph run app.glyph --min -12.50 started
working: a leading hyphen used to make clap reject the argument before your
main ever saw it. Flags glyph owns still bind to glyph, so put
-- in front of a program flag that collides with one of them.
And for the agent in the loop: glyph build --json emits every
diagnostic as structured JSON: a stable code, severity, message, file, and 1-based
line/column range, plus the help and note, including tsc errors mapped
onto your Glyph source. An agent reads and acts on them directly instead of scraping
terminal text.
For interactive agents there's more than batch diagnostics: glyph mcp runs a
Model Context Protocol server over stdio (the same binary, the same
analysis the editor uses), exposing the language server's queries as MCP tools. An
agent can ask for a file's diagnostics, the type at a
position, where a name is defined (following imports), every
reference to a symbol across the whole project, or a
symbol search, without running a full build or reading every file. It's a
thin adapter over the same pure analysis layer, not a second implementation, so it can't
drift from what the compiler and editor see.
Not everything is a hard error. Glyph has a warning tier that flags dead
weight without failing the build: an unused import (E0106), an
unused let (E0107, exempting names led by _),
and unreachable code after a return/break/continue
(E0108). These keep a module honest: a dead import is exactly the kind of
greppability noise that makes a reader hunt for a dependency the code doesn't really have.
Warnings surface in the same rendered and --json output, marked by severity, and
still emit TypeScript.
Where it stands
Shipping today
A language server (glyph lsp over stdio, any editor; VS Code extension bundled) with highlighting, diagnostics, hover, go-to-def, completion, symbols, workspace-wide find-references and rename, and format-on-save; a glyph mcp Model Context Protocol server with eleven tools, the editor's queries plus glyph_symbol, glyph_impact, glyph_assignable, glyph_dependencies and glyph_exports, each with a glyph query verb printing the same JSON for an agent with no MCP client; stable error codes, glyph --explain, tsc back-end errors mapped onto .glyph source, a warning tier (unused import/binding, unreachable code), and --json structured diagnostics. glyph check [path] type-checks a single file or a tree without running it or writing output. glyph fmt --check gates formatting in CI: it writes nothing and exits non-zero when a file is not already in the one canonical layout.
On the way
Caching the cross-file index instead of rebuilding it per request (a speed win on large trees), and remapping `glyph run`'s own crash stack, since `tsx` does not chain the emitted map through its transform.glyph.