quarto-dev / quarto-dev/quarto-cli

`llms-txt`: links and images inside `.llms.md` stay relative while `llms.txt` uses absolute URLs

Open
#14,807 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug llms-txt
Dominant language
JavaScript
Stars
6k
Forks
458
Avg merge
1d 9h
Merged PRs (30d)
41

Description

Bug description

With llms-txt: true, every link and image target inside a .llms.md file stays relative to the page that contains it. The llms.txt index, produced by the same feature, writes absolute URLs as soon as site-url is set. The two outputs of one feature therefore disagree.

The consequence is that a client cannot follow a target found inside a .llms.md file unless it also kept the URL it fetched that file from. A model that is handed the raw text of a page, or a tool that concatenates several pages, loses that base and the targets become dead.

On the published site, https://quarto.org/llms.txt lists absolute entries such as https://quarto.org/docs/cli/publish.llms.md, while https://quarto.org/docs/extensions/lua-api.llms.md contains only relative targets, for example ../../docs/advanced/quarto-ast.llms.md#custom-nodes. Images behave the same way: https://quarto.org/docs/get-started/hello/rstudio.llms.md points at ../images/positron-logo.svg.

This is a separate concern from #14806, which covers table content lost to a [TABLE] placeholder in the same output.

Steps to reproduce

_quarto.yml:

project:
  type: website

website:
  title: "Repro"
  site-url: "https://example.com"
  llms-txt: true

index.qmd:

---
title: "Home"
---

See [the other page](sub/other.qmd).

![A logo](logo.png)

sub/other.qmd:

---
title: "Other"
---

Back to [home](../index.qmd).

Add any logo.png next to index.qmd, then:

quarto render
cat _site/llms.txt
cat _site/index.llms.md
cat _site/sub/other.llms.md
Actual behavior

_site/llms.txt uses the site URL:

# Repro

## Pages

- [Other](https://example.com/sub/other.llms.md)
- [Home](https://example.com/index.llms.md)

_site/index.llms.md does not:

# Home

See [the other page](sub/other.llms.md).

![](logo.png)

A logo

_site/sub/other.llms.md climbs out of its own directory:

# Other

Back to [home](../index.llms.md).
Expected behavior

Targets inside .llms.md resolve without extra knowledge of where the file came from, consistent with the index that lists those files. With site-url set, sub/other.llms.md would read https://example.com/index.llms.md, and logo.png would carry the site URL as well.

Root cause

The Link handler in the llms.lua filter rewrites the extension from .html to .llms.md, strips a leading ./, and returns the link. It never touches the rest of the target, so a relative path stays relative.

Images are affected for a simpler reason: the filter defines handlers for Header, Inline, Block, Link, CodeBlock, RawBlock, and Div, and none of them is Image. The generic Inline handler only cleans attributes on elements that carry an identifier, so an image target is never inspected at all.

The filter also has no way to know the site URL. convertHtmlToLlmsMarkdown() runs Pandoc with an input file, an output file, the filter, and --wrap=none, and passes no metadata.

The index takes the other path. updateLlmsTxt() reads the base URL from the project configuration and joins it with the path of each .llms.md file, which is why that file alone comes out absolute.

The value comes from websiteBaseurl(), the helper that resolves site-url. It is already available in the module that spawns Pandoc, so the information exists at the point where the pages are converted.

Your environment
  • IDE: Terminal
  • OS: macOS 26.6.1
Quarto check output
Quarto 99.9.9
[✓] Checking environment information...
      Quarto cache location: /Users/mcanouil/Library/Caches/quarto
[✓] Checking versions of quarto binary dependencies...
      Pandoc version 3.10.0: OK
      Dart Sass version 1.101.0: OK
      Deno version 2.7.14: OK
      Typst version 0.15.1: OK
[✓] Checking versions of quarto dependencies......OK
[✓] Checking Quarto installation......OK
      Version: 99.9.9
      commit: d4cb49f1e70fb34e4cdf38edbb2f938c3ce7cc21
      Path: /Users/mcanouil/Projects/quarto-dev/quarto-cli/package/dist/bin

[✓] Checking tools....................OK
      TinyTeX: v2026.07
      Chrome Headless Shell: (not installed)
      VeraPDF: (not installed)

[✓] Checking LaTeX....................OK
      Using: TinyTex
      Path: /Users/mcanouil/Library/TinyTeX/bin/universal-darwin
      Version: 2026

[✓] Checking Chrome Headless....................OK
      Using: Chrome from QUARTO_CHROMIUM
      Path: /Applications/Brave Browser.app/Contents/MacOS/Brave Browser

[✓] Checking basic markdown render....OK

(|) Checking R installation...........ℹ R version 4.6.1 (2026-06-24)
! Config '~/.Rprofile' was loaded!
[✓] Checking R installation...........OK
      Version: 4.6.1
      Path: /Library/Frameworks/R.framework/Versions/4.6/Resources
      LibPaths:
        - /Users/mcanouil/Projects/quarto-dev/quarto-playground/renv/library/macos/R-4.6/aarch64-apple-darwin23
        - /Users/mcanouil/Library/Caches/org.R-project.R/R/renv/sandbox/macos/R-4.6/aarch64-apple-darwin23/46003b10
      knitr: 1.51
      rmarkdown: 2.31

[✓] Checking Knitr engine render......OK

[✓] Checking Python 3 installation....OK
      Version: 3.9.6
      Path: /Library/Developer/CommandLineTools/usr/bin/python3
      Jupyter: (None)

      Jupyter is not available in this Python installation.
      Install with python3 -m pip install jupyter

      There is an unactivated Python environment in .venv. Did you forget to activate it?

[✓] Checking Julia installation...

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with src/resources/filters/llms/llms.lua, then read convertHtmlToLlmsMarkdown() and updateLlmsTxt() in src/project/types/website/website-llms.ts, along with website-config.ts's websiteBaseurl() helper. Reproduce the issue with the provided Quarto example and inspect the generated files. Done means links and image targets in .llms.md resolve consistently with llms.txt when site-url is set.

Written by the indexing model from the issue text.

Assessment

Tech stack
lua, typescript
Domain
documentation
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.