Case study · dlexaBack to the portfolio
Go · CLI · alpha

dlexa

Normative Spanish, resolved from the terminal.

A Go CLI that consults the Diccionario panhispánico de dudas and the RAE editorial surfaces through explicit routes. You choose the surface — discovery, a direct entry, or a known slug — and the tool never silently reroutes you to a different one.

5
command surfaces
18
internal packages
1
direct dependency
75
test files
dlexa · dpd search
$ dlexa dpd search guion# [dlexa:search] guion*Fuente: Diccionario panhispánico de dudas | Caché: HIT* --- ## Resultado semántico para "guion" - total_candidatos: 1- siguiente_paso: `dlexa dpd guion` ### 1. guion1- snippet: guion1- clasificación: dpd-entry- fuente: Diccionario panhispánico de dudas- ver_contenido: `dlexa dpd guion`
The complete output of `dlexa dpd search guion`, reproduced whole — nothing elided, nothing rewrapped. `Caché: HIT` means the run was served from the local cache. Note what the last line is: not an answer, but the exact command that produces one. The tool prints Spanish to every reader, and its stdout is never translated on this page.
The surface

Five routes, and you always name the one you took

A free-text query could be dispatched by guesswork, and most lookup tools do exactly that. This one refuses: discovery, direct entry and slug articles are separate commands, so the answer you get always corresponds to the surface you asked for.

  1. 01

    search

    Discovery

    Federated semantic discovery

    dlexa search <consulta>

    Queries the general RAE search surface and the DPD entry index together, then returns candidates with the exact follow-up command for each one. This is where a question in plain language belongs.

    Copy this literally
    $ dlexa search solo o sólo
  2. 02

    dpd

    Direct lookup

    Direct entry in the Diccionario panhispánico de dudas

    dlexa dpd <termino>

    Exact-match consultation for a DPD entry you already know. It also carries its own discovery path, `dlexa dpd search <consulta>`, which stays scoped to the DPD index instead of federating.

    Copy this literally
    $ dlexa dpd tilde
  3. 03

    espanol-al-dia

    Article by slug

    RAE editorial article by slug

    dlexa espanol-al-dia <slug>

    Opens a complementary guidance article when discovery already identified its route. The slug is the one that appears in the public URL, passed verbatim.

    Copy this literally
    $ dlexa espanol-al-dia un-solo-nombre-para-cada-letra
  4. 04

    duda-linguistica

    Article by slug

    Answered linguistic doubt by slug

    dlexa duda-linguistica <slug>

    The RAE surface for individual doubts already answered editorially, kept as its own command so a slug from one collection is never resolved against another.

    Copy this literally
    $ dlexa duda-linguistica cuando-se-escriben-con-tilde-los-adverbios-en-mente
  5. 05

    noticia

    Article by slug

    Announcements and FAQ-shaped resources

    dlexa noticia <slug>

    Reaches the RAE news surface, which also hosts FAQ-compatible pages. Discovery can hand you one of these routes directly when the best answer is not a dictionary entry at all.

    Copy this literally
    $ dlexa noticia preguntas-frecuentes-tilde-en-las-mayusculas

The rule that holds them apart

When a direct lookup fails to resolve, the tool does not quietly run the search flow behind your back. It prints a structured miss and hands you the search command to run yourself. The route stays yours.

dlexa · dpd tilde
$ dlexa dpd tilde# [dlexa:dpd] tilde*Fuente: Diccionario panhispánico de dudas | Caché: HIT* --- # tilde ## tilde1 Diccionario panhispánico de dudas 2.ª edición … líneas 14 a 339 elididas: el cuerpo de tilde1 y tilde2 … Source: Real Academia Española y Asociación de Academias de la Lengua EspañolaDictionary: Diccionario panhispánico de dudasEdition: 2.ª ediciónURL: https://www.rae.es/dpd/tildeConsulted: 05/08/2026
A direct lookup. The real run prints 344 lines, so lines 14 to 339 are removed as whole lines and the cut is marked where it happens — nothing was rewrapped or reworded. The closing block is the tool’s own attribution: source, dictionary, edition, canonical URL and consultation date, emitted on every entry.
Discovery

It answers with a command, not with a guess

The discovery surface federates two providers and returns candidates. What each candidate carries is the useful part: the exact next command, and whether that command can be run right now or is only a pointer to somewhere the CLI does not reach yet.

The two providers it federates

  • dpd--source dpd

    The DPD entry index, queried through its own `/srv/keys` discovery endpoint. It runs first in the federated default.

  • search--source search

    The general RAE search surface, which reaches editorial articles, answered doubts and FAQ-shaped pages beyond the dictionary itself.

The same query is not sent everywhere

A federated run plans per provider instead of forwarding one compact token bag to both. For a multi-token query such as `coma que explicativa`, the DPD provider first tries the leading lexical token — `coma` — and only retries the full query if that returns nothing. General search keeps the original query. It makes the gateway tolerant of the loose queries an agent tends to issue, without pretending the CLI ever sees the conversation behind them.

What a candidate hands you

  • ver_contenidoRunnable now

    The next step opens real content right now. Copy the command and run it: a `dpd` entry, an `espanol-al-dia` article, a `duda-linguistica` answer or a FAQ-compatible `noticia`.

  • seguir_buscandoDeferred

    The result is real but no CLI command maps to it yet, so the tool says so instead of inventing a route. In JSON output the same state is the candidate’s `deferred` field, which automation should read before acting.

dlexa · search
$ dlexa search "solo o sólo"# [dlexa:search] solo o sólo*Fuente: búsqueda general RAE | Caché: HIT* --- ## Resultado semántico para "solo o sólo" - total_candidatos: 7- siguiente_paso: `dlexa dpd hacer` ### 1. ⊗ solo (o solamente) hacer que + infinitivo- snippet: ⊗ solo (o solamente) hacer que + infinitivo- clasificación: dpd-entry- fuente: Diccionario panhispánico de dudas- ver_contenido: `dlexa dpd hacer` ### 2. solo- snippet: solo- clasificación: dpd-entry- fuente: Diccionario panhispánico de dudas- ver_contenido: `dlexa dpd tilde` … candidatos 3 a 6 elididos: tres artículos y una duda lingüística … ### 7. Clara Janés- snippet: … «La soledad no es vivir solo » . El Mundo , 3 de octubre de 2015. … de sombra , por el que obtuvo el Premio Ciudad de Melilla, o Los secretos del bosque , Premio Jaime Gil … del sueño (1989), El hombre de Adén (1991) o Espejos de agua (1997). Suyos son, también, otros …- clasificación: unknown- fuente: RAE- url: https://www.rae.es/academico/clara-janes- seguir_buscando: `dlexa search solo o sólo`
Candidates 3 to 6 are removed as whole lines and the cut is marked; nothing else was touched. Candidate 2 is worth reading twice — asking about `solo` resolves to `dlexa dpd tilde`, because that is where the DPD actually settles the question. Candidate 7 is the deferred case: a real RAE result with a URL but no command that opens it, so the tool offers `seguir_buscando` instead of pretending. The `…` inside its snippet belongs to the source.
Always a next step

A lookup that finds nothing still tells you where to look

Both dead ends print the same shape — a level, what was aimed at, the command that would move you forward, and the action to take — so you are never left holding an error string and a decision. A reader and a script get the same thing.

Nivel 1 · Syntax

The command itself does not exist

What triggers it

Syntax failures stay in the fallback system rather than taking over the help body. The response names the correct syntax and points at `--help`, which the project treats as part of the CLI contract for humans and language models alike.

dlexa · root
$ dlexa frobnicate# [dlexa:root] Nivel 1 · Syntax unknown command "frobnicate" for "dlexa" - objetivo: `dlexa`- sintaxis correcta: `dlexa <comando> [argumentos]`- acción: Usá `--help` para ver sintaxis válida y ejemplos copiables.
Complete and unedited.
Nivel 2 · Not Found

The command exists, the entry does not

What triggers it

This is where the no-rerouting rule becomes visible. The tool preserves a native DPD related-entry suggestion when the source offers one; otherwise it prints the explicit `dlexa search <consulta>` next step and stops. It never runs the search flow on your behalf.

dlexa · dpd
$ dlexa dpd zzzznoexiste# [dlexa:dpd] Nivel 2 · Not Found No se encontró contenido en este módulo. - objetivo: `zzzznoexiste`- siguiente comando: `dlexa search zzzznoexiste`- acción: Probá con `dlexa search <consulta>` para descubrir la ruta correcta.
Complete and unedited.

Measured, not assumed

Both were run unpiped to read the binary’s own status, and both returned exit code 0. The structured output is the signal to branch on: automation reads the printed shape, or the `deferred` field in JSON, rather than `$?`. Worth knowing before wiring it into a script.

Inside

Four stages, and no stage knows about the next one

Both the lookup modules and the discovery providers run the same explicit pipeline. Each stage lives in its own package, so acquiring a document, understanding it, and deciding how it should look are three problems that never leak into each other.

  1. Fetch

    01
    internal/fetch

    Upstream acquisition adapters. The only layer that knows the RAE is on the other side of a network.

  2. Parse

    02
    internal/parse

    Raw document parsers. They produce structured records — or an explicit miss, which is a result and not an exception.

  3. Normalize

    03
    internal/normalize

    Maps whatever the source shape happened to be onto the shared model, so nothing downstream ever branches on which provider answered.

  4. Render

    04
    internal/render

    Markdown or JSON, chosen by `--format`. Presentation stays outside module logic, which is why the same lookup serves a reader and a script without a second code path.

Cache-aside, and switchable per request

`internal/cache` sits beside the pipeline as explicit infrastructure: filesystem-backed with an in-memory fallback, never a distributed backend. Every response reports its own state — the `Caché: HIT` and `Caché: MISS` in the captures above are the tool telling you whether it went out to the network. `--no-cache` skips both reads and writes for a single request.

The shared vocabulary every package speaks

  • LookupRequest
  • LookupResult
  • SearchRequest
  • SearchResult
  • SearchCandidate
  • LookupMiss
  • HelpEnvelope
  • FallbackEnvelope
  • Entry
  • SourceDescriptor
  • SourceResult
  • Warning
  • Problem
Scope

Built to answer one kind of question properly

The tool accepts free text, so it says plainly what it is aiming at. A narrow instrument that names its edge is more useful than a broad one that quietly guesses past it — and knowing exactly what you are asking is what makes the answer worth pasting into your work.

  • Normative doubts, resolved precisely

    Spelling, pronunciation, morphology, syntax and usage — the questions the DPD exists to settle. That focus is what lets a single command return an answer you can act on instead of a page you still have to read.

  • Answers keep the DPD’s own nuance

    DPD recommendations turn on current usage, register, formal norm, geography and communicative context, and the tool passes that through rather than flattening it into a verdict. You get guidance you can weigh, with its conditions attached.

  • A term with no entry returns a route, not an invention

    When the live source has nothing for a term, the answer is a structured Nivel 2 with the exact `dlexa search` command that would find it. Nothing is fabricated to fill the gap, which is why an answer that does arrive can be trusted on sight.

  • Nothing leaves your machine

    The cache is the filesystem with an in-memory fallback, and there is no distributed backend behind it. Your queries stay where you ran them, and a warm machine answers from disk.

  • Complex tables keep their structure

    Simple DPD tables render as Markdown pipe tables. Tables built on merged or multi-level cells render as HTML inside the Markdown payload, because that is the only way their meaning survives the trip — a pipe table would have to discard the very structure the entry uses to make its point.

Install

One line, and a single dependency behind it

Go 1.22 or newer is the only prerequisite. The binary carries exactly one direct dependency — Cobra, for the command tree — and everything else, from fetching to caching to rendering, is in the eighteen internal packages.

$ go install github.com/Disble/dlexa/cmd/dlexa@latest
Open the repository
  • github.com/Disble/dlexa

    The source, the README that documents every surface, and the CONTRIBUTING guide with the pinned lint toolchain.

  • rae.es/dpd

    The Diccionario panhispánico de dudas itself. Every entry the CLI returns carries its canonical URL back to here, along with the edition and the consultation date.