Skip to content

Repository files navigation

Meturgaman

A study companion for Jewish texts. Ask it what the tradition says about a subject and it finds the sources, fetches them with their editions and licences, and teaches from what it fetched: how the argument is built, who disagrees, and what turns on the disagreement. Underneath the teaching sits a careful instrument: retrieval that names its edition every time, romanization under eight published standards, comparison of editions, recorded cantillation, and the Jewish calendar.

A meturgaman is the person who stood beside the reader and rendered the text for the congregation. This does that job, in both senses: it renders the letters, and it renders the meaning, without ever supplying a text from memory.

What an answer looks like

Asked "is there Jewish wisdom about limited liability?", the agent built on this tool answered from fetched sources: the default of expansive liability, the apotiki formula of Shulchan Arukh Choshen Mishpat 117:1 quoted from the Lemberg 1898 printing, the Nehardean iska from Bava Metzia 104b:14, the debtor protections of Deuteronomy 24 and Mishneh Torah Creditor and Debtor 1:7, and the modern dispute over whether halakhah recognizes the corporate veil, reported as a dispute. An independent checker then fetched every one of those citations and confirmed all six.

That is the standard the project holds itself to, and it is measured rather than claimed: six real questions were put to the agent, every answer was saved verbatim, and every citation-bearing claim was handed to a separate verification agent told to fetch it and try to refute it. The transcripts, the hostile verification reports, and an honest account of what still goes wrong (counts and reference anchors remain the weak spot) are in notes/agent-evaluation.md.

Getting started

git clone https://github.com/Oranburg/meturgaman.git
cd meturgaman
python3 -m venv .venv && .venv/bin/pip install -e ".[dev]"
.venv/bin/python -m tools.fetch_sources    # the source documents, verified by hash
.venv/bin/python -m pytest -m "not network"

Python 3.11 or later. The core has no dependencies at all: both services are keyless JSON over HTTPS, and urllib does that.

meturgaman topics charity                     # find a subject's slug
meturgaman sources tzedakah --text            # what the tradition says about it
meturgaman text "Berakhot 2a"                 # a passage, in every edition
meturgaman chain "Mishnah Bava Metzia 5:11"   # what the tradition built on it
meturgaman romanize "כָּל־הָאָרֶץ"              # kol-ha-’arets
meturgaman audio "Genesis 1:1"                # chanted, by a person

Modern Israeli law

The same instrument, pointed at the statute book. In the classical library the risk is a Hebrew text supplied from memory. In Israeli legislation the Hebrew is usually the easy part and the English is the trap: an unattributed copy of a statute on the open web is complete, fluent, right about the section numbering, and not the authorized translation, and nothing on its face says so.

meturgaman law statutes                    # the registry
meturgaman law tiers                       # the authority ladder
meturgaman law sources remedies-1970       # where an English text can be had
meturgaman law hebrew remedies-1970        # consolidated Hebrew, with its revision id
meturgaman law parse delivery.txt --json   # split a delivered English text into sections
meturgaman law align --hebrew numbers.txt --english delivery.txt
meturgaman law reconcile --witness lsi=authorized:a.txt --witness web=unattributed:b.txt

It does not translate. It locates, fetches, parses, aligns and reconciles, and every artifact carries the tier of what it came from. The tiers, best first, are enacted (the English is law, or authentic treaty text), authorized (Laws of the State of Israel, the Ministry of Justice's own translation, authorized and not binding), government, commercial, scholarly, unattributed, and assistant. Only the first two print as the law. A translation a model produced is tier assistant, says so wherever it appears, and never stands as the text.

Three facts the registry knows so that nobody has to remember them. L.S.I. runs to volume 45 and stops around 1989, so a later statute has no authorized English at all and the registry says so rather than sending anyone to a volume that does not exist. L.S.I. prints the law as enacted, so setting it beside a consolidated Hebrew text can print two different laws and call one a translation of the other. And a section's marginal heading is typeset in its own column, so every flattening of the columns puts it somewhere a naive read will misfile it; the parser reports it as a candidate and refuses to attach it.

Pairing is on the section number and never on position. align exits non-zero on any section without a counterpart, because a bilingual page built over a gap prints one column beside the wrong other column and shows nothing.

Walking the tradition

A halakhic question runs Torah, Mishnah, Gemara, the Rishonim, the codes, then responsa. Sefaria's link graph knows the actual path for any passage, and three commands walk it:

$ meturgaman chain "Mishnah Bava Metzia 5:11"
Mishnah Bava Metzia 5:11: what the tradition built on this passage

Tanakh  (5)
    Exodus       Exodus 22:24
    Leviticus    Leviticus 25:36  and 2 more
    Deuteronomy  Deuteronomy 23:20
Tosefta  (2)
Talmud  (3)
    Bava Metzia  Bava Metzia 75a:11-75b:4
Commentary  (43)
Quoting Commentary  (6)
Halakhah  (1)

The work lists are trimmed here; the command prints every work and its refs.

meturgaman links gives the same graph filtered and flat (--category Commentary, --refs-only for piping), and meturgaman related summarizes everything Sefaria attaches to a passage, topics included. Reading the chain down from a Gemara shows where its law lands in the Shulchan Arukh; reading up from a code shows where its ruling began. Every ref in the output can be fetched with meturgaman text.

meturgaman sugya maps a Talmud reference to the passage boundary it belongs to, which regularly crosses the printed page. meturgaman word fetches dictionary entries, with Jastrow's citations back into the corpus.

The one idea this is built around

Every romanization table lives in schemes/, as markdown, one file per standard. No table is written in Python anywhere in this repository.

That is not a stylistic preference. A table written in code is a table nobody proofreads: it gets edited to make a test pass, it drifts from the standard it claims to implement, and the drift is invisible because checking it means reading code. This project shipped a set of spirant characters that appear nowhere in the SBL Handbook, and they survived a rewrite because a second file had quietly copied them. They were caught by someone opening the PDF.

So the tables are documents. Each one carries its citation, its grid, and a section recording every place it departs from what its source prints and why. A test re-extracts each source and checks every character against it. When the output looks wrong, you read one page.

The eight schemes

Scheme Source Use it when
sbl-general SBL Handbook 2nd ed. §5.1.2 general scholarly writing. The default.
sbl-academic SBL Handbook 2nd ed. §5.1.1 the romanization must be reversible
ala-lc ALA-LC, Library of Congress the result must match a catalogue record
bgn-pcgn BGN/PCGN 2018 Agreement Israeli place names, or anything official
encyclopaedia-judaica-general EJ 2nd ed. vol. 1 p. 197 matching how a Jewish Studies reader expects a word to look
encyclopaedia-judaica-scientific EJ 2nd ed. vol. 1 p. 197 comparative Semitics. Check its output
yivo YIVO Institute Yiddish, and the closest published thing to Ashkenazi Hebrew
ala-lc-yiddish ALA-LC, Yiddish column Yiddish for a catalogue record
meturgaman schemes                # all eight with their sources
meturgaman schemes --name yivo    # one in full, table and reasoning

About Ashkenazi

There is no published romanization table for Ashkenazi Hebrew. Not one that could not be found: one that does not exist. ArtScroll and Feldheim have house practices and publish no tables, and the Library of Congress's Hebraica Cataloging manual mentions Ashkenaz only as the surname of a lexicographer.

What exists is Yiddish. YIVO's treatment of the loshn-koydesh layer, the words of Hebrew and Aramaic origin, is Ashkenazi Hebrew pronunciation: תּ is t and bare ת is s, which is what gives Shabbos rather than Shabbat. That is real, published, and in schemes/yivo.md.

What the tool will not do is rewrite your Ashkenazi spelling into Sephardi. It refuses and shows its evidence:

$ meturgaman romanize "Shabbos and halachah and Sukkos and mitzvos"
refused: This text is written in Ashkenazi register (ashkenazi 14, sephardi 0),
and sbl-general would rewrite it as Sephardi. That is a change of the author's
usage rather than a correction.

Flags, and why they matter more than the output

Hebrew orthography is genuinely ambiguous in four places, and each of them has one mark doing two jobs: a dagesh is lene or forte, a sheva is vocal or silent, a qamats is long or short, a vav is a consonant or half a vowel.

The engine decides these in a pass of their own, in the open. Where the rules cannot settle a question, it raises a flag rather than picking a default and staying quiet. Output goes to stdout, flags to stderr, and under --json the flags travel inside the document so a pipeline cannot lose them.

$ meturgaman romanize "קָנְיָא"
qaneya
  [qamats-may-be-short] (קָנְיָא) read long (a), which is the commoner reading
  of this shape. A few words take a short qamats (o) here and are listed in
  rules/qamats-qatan.md; check the word if it matters
  [sheva-after-qamats] (קָנְיָא) read as vocal, which is the commoner reading
  after a long qamats. Some words take a silent sheva here and the spelling
  does not say which; check if it matters

The Library of Congress reaches the same conclusion from the cataloguing side: its manual sends cataloguers to a dictionary "primarily to distinguish schwa naʻ from schwa nah, a matter which has significant impact on romanization." Code that answers from orthography alone should say when it is unsure.

Every source carries its edition and its licence

$ meturgaman text "Genesis 1:1" --version all
Genesis 1:1
8 editions, 5 independent witnesses
providers: he.wikisource.org, tanach.us, jps.org, sefaria.org, chabad.org

Five witnesses from eight editions, because three of them came from the same digitization. An edition's provider is derived from its own stated source rather than from the fact that Sefaria served it, so two rows that came from one place do not count as two. meturgaman compare then reports where editions actually differ, on the consonantal skeleton, with vocalization recorded as apparatus rather than as disagreement.

Checking a draft

$ meturgaman verify chapter.md
resolved    'Genesis 1:1' -> Genesis 1:1
found       בראשית ברא אלהים את השמים ואת...  in Genesis 1:1
NOT FOUND   בראשית ברא משה את הארץ ואת...  checked against Genesis 1:2

verify finds every citation in a manuscript with Sefaria's own reference finder, validates each one, and checks every Hebrew quotation of three words or more against the fetched text of the passages cited in its paragraph. Matching runs on the consonantal skeleton, so pointing and cantillation differences do not defeat a genuine quotation while a reworded one still fails. A miss arrives as a diagnosis: the closest passage, the run of words that does match, and the first word where the draft parts from the edition:

            matches Genesis 1:1 through 'בראשית ברא', then the draft has
            'משה' where the edition has 'אֱלֹהִ֑ים'

Exit code 0 means everything resolved and every quotation was found; "not found" is a flag to check, never proof of fabrication.

Counting from data

$ meturgaman anchors "Hasagot HaRaavad on Mishneh Torah, Repentance"
Hasagot HaRa'avad on Mishneh Torah, Repentance
    10 chapters, 12 populated anchors, 13 segments
    2:5          1 segment
    3:7          2 segments
    ...

A sentence like "Ravad glosses this book in nine places" is a census, and a census written from memory is how careful answers go wrong. anchors prints every populated anchor of a work with its segment count, straight from the service's shape record, so counting means reading rather than remembering.

Daily learning

meturgaman daf                        # today's daf yomi, fetched
meturgaman daf --cycle "Daily Mishnah"
meturgaman study "Bava Metzia 75b:11" --sugya --tier block
meturgaman study "Genesis 1" --tier file --output ~/notes/

daf fetches any learning cycle's reading directly. study --sugya expands a Talmud reference to its mapped passage before rendering, and --output writes the file, named from the normalized reference when given a directory.

Two further study options:

  • --paired appends companion passages: rules/pairings.md names the pairs (Mishneh Torah with the Guide, Shulchan Arukh and the Tur with the Beit Yosef, each with its reason), and the passage-level connections come from Sefaria's link graph at run time. Where the graph records no companion passage, the file says so instead of guessing; the graph is genuinely sparse there.
  • --vocalize points unvocalized Hebrew with Dicta's model, run locally (pip install 'meturgaman[dicta]'), and stamps the output as a model's reading rather than an edition's. Editions that carry their own pointing are left alone.

For scripts and other agents

Every command that talks to a service takes --json and --no-cache, limits are bounded, refusals exit non-zero with a reason on stderr, and meturgaman clear-cache empties the response cache. The engine romanizes about 60,000 words per second and the CLI starts in about 50 milliseconds, so driving it one call at a time from an agent costs nothing worth engineering around.

meturgaman text "Genesis 1:1" --json | jq '.editions[0].segments[0].text'
meturgaman links "Bava Metzia 75b:2" --refs-only | while read ref; do ...

For MCP clients such as Claude Desktop and Claude Code, an optional server wraps the same library:

pip install 'meturgaman[mcp]'
meturgaman-mcp

Thirteen tools over stdio, structured results, flags inside the payload. The SDK stays behind the extra so the core keeps its zero dependencies.

Hearing it, and the calendar

Recorded cantillation. Sefaria carries PocketTorah's recordings: a person chanting, with the trope, CC-BY-SA, timestamped to the verse. Torah only. Synthetic speech through the macOS Carmit voice covers the rest, and the tool always says which one you are getting.

meturgaman audio "Genesis 1:1"           # the recording
meturgaman audio "Berakhot 2a" --synth   # nobody has recorded the Talmud
meturgaman day --date 2026-08-08 --register a
meturgaman calendars                     # daf yomi and the learning cycles
meturgaman leyning --triennial
meturgaman yahrzeit 2020-03-15
meturgaman zmanim --zip 20902 --elevation 150

--register a keeps holiday names in Ashkenazi register; Hebcal publishes twenty-two locales and six of them are Ashkenazi.

What it reads

Sefaria, for texts, editions, links, topics, search, dictionaries, commentaries, sugya boundaries and the learning calendar. No key, no registration.

Hebcal, for the calendar, Torah readings, halachic times and yahrzeits.

Dicta, optionally and locally, for adding vowel points. Their models are on HuggingFace under a permissive licence and run on your own machine. They publish no API contract, so this project does not call one.

Both APIs' contracts are committed under docs/api/, so the code is written against a recorded spec rather than against anyone's memory of one: Sefaria's OpenAPI document with all sixty of its paths, Sefaria's index of the prose about them, and Hebcal's OpenAPI document, which sources/hebcal.py reads at import time to validate its own parameter names. Provenance and hashes are in docs/api/README.md, and python -m tools.fetch_contracts verifies every one of them against the bytes the services serve.

The agent, and the skill, on five surfaces

The same capability, packaged for wherever Claude happens to be running. All of them are written around teaching from fetched sources, and all of them carry the four disciplines that came out of the evaluation: no census without an enumeration, no dressing a reading in the tool's authority, references copied exactly as fetched, and search snippets treated as leads rather than sources.

Where What to use
Claude Code, workstation agents/meturgaman.md and skills/meturgaman/SKILL.md, with the tool on the path
Claude Code on the web .claude/agents/meturgaman.md, which installs from the checkout and needs nothing else
claude.ai claude-ai/: build the zip, upload it, allow the sandbox three hosts. claude-ai/INSTALL.md
Claude Cowork the skill, plus claude-ai/agents/cowork-instructions.md as folder instructions
Claude Desktop desktop-skill/, the complete package the claude.ai build is an overlay on

Both packaged builds come out of python tools/build_skill.py, which vendors the library whole, standard library only and no install step, because a sandbox may have no package index in reach. They also carry both API contracts and a script that queries them, so a question about a parameter is answered from what the service publishes rather than from recollection, and answering it costs a few hundred tokens rather than the megabyte the spec occupies.

The evidence for what the agent can and cannot do is in notes/agent-evaluation.md.

Sources and licences

The code and documentation here are MIT. The rest is not ours to license:

  • The texts carry their own licences per edition. The tool reports each one rather than assuming.
  • Hebcal is CC-BY-4.0 and the tool prints its attribution.
  • PocketTorah is CC-BY-SA and the tool prints its attribution.
  • The romanization standards in sources/ are not committed. Two of the six are copyrighted commercial publications, so sources/ is gitignored and only provenance lives in the repository. sources/manifest.md records every URL and SHA-256, and python -m tools.fetch_sources retrieves and verifies them.

What is committed is the extracted table data in schemes/, because a table of correspondences between two writing systems is a set of facts.

Not yet

The tanach.us reader, translation beyond retrieval of published translations, and a web interface. Sefaria also runs hosted MCP servers at https://mcp.sefaria.org/sse and https://developers.sefaria.org/mcp; they need a paid account, which is why this tool does not depend on them.

About

Fetch Hebrew, Aramaic, and Yiddish primary sources, romanize them under any of eight published standards, and hear them read aloud. Every table copied from a published source, none invented.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages