tg and lat.md: what is the same, what differs
tg reads the same lat.md/ folders as lat.md and agrees with its check. It adds a graph, typed links from code and a few developer conveniences. lat.md is stronger in places too, and this page says where.
tg is tested against. Check its documentation for anything newer.The short version
- Same: the folder format, section ids, wiki links,
@lat:comments, the leading-paragraph rule,require-code-mention, and the verdicts ofcheck. - Added by tg: typed, signed edges from code (
@tg:), an openCypher query over docs and code, search that needs no model, export and import, and an Obsidian plugin that reads the folder in place. - Better in lat.md: symbol parsing uses full grammars, more agent templates ship, and it has had far more real-world use.
Side by side
| lat.md 0.12 | tg 0.5 | |
|---|---|---|
| Folder format and ids | lat.md/, file#Heading#Sub, short ids | The same |
check verdicts | Reference behavior | Same findings on the 3 projects it is tested on |
| Commands | locate, section, refs, check, expand, search, reindex, gen, init, hook, mcp, config | locate, section, refs, check, expand, search, reindex, gen, init, hook, mcp, plus cypher, edges, export, import |
| Code annotations | @lat: plain links | @lat: plus @tg: typed, signed edges with properties |
| Querying | Locate by name, refs, search | All of those, plus openCypher over sections and code |
| Code symbols | Tree-sitter grammars: TS, TSX, JS, JSX, Python, Rust, Go, C | Regex finders: TS/JS (also .mts .cts .mjs .cjs), Python, Go, Rust, C |
| Search | Semantic: a local offline model or a hosted API key | Lexical by default, no model; optional Ollama or OpenAI-compatible embeddings |
| Agents | Claude Code, Cursor, plus Pi and OpenCode templates | Claude Code, Cursor, AGENTS.md |
| MCP tools | 6 (locate, section, search, expand, check, refs) | 8 (the same plus cypher and edges) |
| Obsidian | Folder opens as plain Markdown | Plugin resolves lat ids on click, shows diagnostics, draws code nodes in the graph |
| Move content between formats | No equivalent | export (lossy, with a report) and import |
| Install | npm package with tree-sitter grammars and a database client as dependencies | One bundled file, no runtime dependencies |
check on this repository | about 0.38 s | about 0.17 s |
The timing is one machine, warm caches, this repository's own lat.md/. Treat it as an order of magnitude, not a benchmark.
What tg adds
Typed edges from code
A lat.md reference says that this function relates to that section. A @tg: edge says how:
// @tg: implements:: [[auth#Token expiry]] {since: 2}
// @tg: -contradicts:: [[design#Stateless sessions]]
Types, a negative sign and properties give you something to filter and count. Plain @lat: comments keep working, and a bare @tg: [[x]] is the same thing.
A query language over docs and code
Because sections and code symbols are graph nodes, questions that need several greps become one query:
tg cypher "MATCH (c:CodeSymbol)-[:implements]->(s:Section)
RETURN s.title, count(c) AS functions
ORDER BY functions DESC"
Which design sections have no implementation, which are implemented by many functions, which docs nothing links to. The engine is the one inside the Obsidian plugin, so the same queries run in both places.
Search with no setup
lat.md can search semantically, with a local model or an API key. tg search starts one step earlier: a lexical index with no model to download and no key to set, which is enough for a docs folder of a few hundred sections and safe to run inside a prompt hook. Embeddings are an option, not a requirement, and a provider outage degrades to lexical results rather than an error. The LAT_LLM_KEY variables are accepted as aliases.
Obsidian and interchange
If your notes live in Obsidian, the plugin makes a lat.md/ folder in your vault navigable and checkable in place. tg export turns a vault into a lat.md folder that both tools accept. Typed edges and properties have no lat.md equivalent, so export flattens them and tells you what it dropped; it does not claim a lossless round trip.
Safer setup
tg init prints a diff and writes nothing until you pass --write. Moving from lat.md is explicit: --migrate replaces the lat block, hooks and MCP entry, and leaves everything else in your files byte-for-byte as it was.
Where lat.md is better
- Symbol accuracy. lat.md parses source with real grammars.
tg's regex finders cover common declarations, but unusual syntax, macros and generated code can slip past them. They say "cannot tell" rather than guess, yet a grammar is still more precise. - Agent coverage. lat.md ships templates for Pi and OpenCode as well.
tgcovers Claude Code, Cursor andAGENTS.md. - Maturity. It is the original, and the format is defined by it.
tgfollows the format, and when the two disagree, lat.md is right by definition unless the difference is listed. - Semantic search out of the box. If you want embeddings without running or configuring anything else, lat.md's local model option is simpler than pointing
tgat an embedding server.
The deliberate differences
tg accepts .mts, .cts, .mjs and .cjs as source links, which lat.md reports as unresolved. That is the only intentional difference in check verdicts, and the parity test normalizes it. Everything else the test compares has to match: on the upstream lat.md project snapshot, which has 231 findings, both tools report the same set.
Which should you use
- Stay with lat.md if you want the reference tool, need Pi or OpenCode, or rely on grammar-accurate symbol links across many languages.
- Use tg if you want typed links from code, queries across docs and code, a no-setup search for hooks, or you also keep notes in Obsidian.
- Use both. They read the same folder, so trying
tgon an existing project costs nothing:tg checkshould agree withlat check.
Switching in four commands
npm install -g @typedgraph/cli tg check # should match lat check tg init # dry run: see the diff tg init --write --migrate # swap the lat block, hooks and MCP entry
Your lat.md/ folder and @lat: comments stay as they are. If something differs from lat.md that is not on the list above, that is a bug, and the issue tracker is the place for it.