Ship entire project source code and directory tree with your Sphinx documentation.
Generate a reStructuredText (.rst) file that contains:
- An ASCII directory tree of your project.
- A
literalincludedirective for every source file you select.
The result is a single .rst document ready to be included in a Sphinx
documentation build, specifically for the llms.txt, providing full
project context for LLMs.
Python 3.10+
uv pip install sphinx-source-treeRun in your project root:
sphinx-source-treeThis writes docs/source_tree.rst with the full tree and
literalinclude blocks for .js, .json, .md, .py, .rst,
.toml, .yaml and .yml files.
Print to stdout instead:
sphinx-source-tree --stdoutsphinx-source-tree [OPTIONS]-p, --project-root PATH- Project directory. Default: current directory.
-d, --depth N- Maximum tree depth. Default:
10. -o, --output PATH- Output
.rstfile. Default:docs/source_tree.rst. -e, --extensions EXT [EXT ...]- File suffixes to include via
literalinclude. Default:.js .json .md .py .rst .toml .yaml .yml. -i, --ignore PAT [PAT ...]- Glob patterns to ignore (matched against both the relative path and the bare file name).
-w, --whitelist DIR [DIR ...]- Restrict output to these directories. Ignored when
--include-allis active. --include-all / --no-include-all- Include everything regardless of whitelist. Default: on.
-t, --title TEXT- RST section title. Default:
Project source-tree. --linenos / --no-linenos- Attach
:linenos:toliteralincludedirectives. Default: off. --order PATH [PATH ...]- Explicit ordering for the
literalincludelisting. The files listed here appear first, in the given sequence; all remaining collected files follow in their default sorted order. Has no effect on the ASCII directory tree. --stdout- Write to stdout instead of the output file.
-V, --version- Show version and exit.
All CLI options (except --stdout and --version) can be set under
[tool.sphinx-source-tree] in your project's pyproject.toml.
CLI arguments always take precedence.
Single-file example:
[tool.sphinx-source-tree]
depth = 4
output = "docs/source_tree.rst"
extensions = [".py", ".rst", ".toml"]
ignore = ["__pycache__", "*.pyc", ".git", "*.egg-info"]
whitelist = ["src", "docs"]
include-all = false
title = "Source listing"
linenos = true
extra-languages = {".vue" = "vue", ".svelte" = "svelte"}
order = ["README.rst", "pyproject.toml", "src/app.py"]Key names use hyphens (include-all) to follow TOML/PEP 621
convention; they are normalised internally.
You can generate several .rst files in one run by adding
[[tool.sphinx-source-tree.files]] entries. Top-level settings act as
shared defaults; each entry can override any of them.
[tool.sphinx-source-tree]
# Shared defaults — applied to every file unless overridden
depth = 10
ignore = ["__pycache__", "*.pyc", ".git", "*.egg-info"]
linenos = false
[[tool.sphinx-source-tree.files]]
output = "docs/source_tree.rst"
title = "Full project source"
# inherits depth, ignore, linenos from the section above
[[tool.sphinx-source-tree.files]]
output = "docs/api_tree.rst"
title = "API source"
extensions = [".py"]
whitelist = ["src"]
include-all = false
depth = 5 # overrides the shared default
[[tool.sphinx-source-tree.files]]
output = "docs/docs_tree.rst"
title = "Documentation files"
extensions = [".rst", ".md"]
whitelist = ["docs"]
include-all = falseThe merge priority is: built-in defaults < top-level ``[tool.sphinx-source-tree]`` < per-file ``[[…files]]`` entry < CLI arguments.
When no [[files]] entries are present the tool behaves exactly as
before, so existing configurations are fully backward compatible.
By default, literalinclude blocks are emitted in alphabetical order.
The order option lets you pin specific files to the top of the
listing while leaving all other files in their default sorted order.
Note
order affects only the literalinclude source-code listing.
The ASCII directory tree is always rendered in its natural sorted
order and is unaffected by this setting.
Via pyproject.toml (top-level)
The top-level order list is shared by all output files (or inherited
by [[files]] entries that do not set their own):
[tool.sphinx-source-tree]
order = [
"README.rst",
"pyproject.toml",
"src/app.py",
]Via [[tool.sphinx-source-tree.files]]
Each [[files]] entry can define its own order, which overrides
the top-level value for that output file only:
[tool.sphinx-source-tree]
ignore = ["__pycache__", "*.pyc"]
[[tool.sphinx-source-tree.files]]
output = "docs/source_tree.rst"
title = "Full project source"
# no order — uses default alphabetical listing
[[tool.sphinx-source-tree.files]]
output = "docs/llm_tree.rst"
title = "Source for LLMs"
order = ["src/core.py", "src/models.py", "src/utils.py"]Via the CLI
sphinx-source-tree --order src/core.py src/models.py src/utils.pyVia the Python API
Pass the order keyword argument to generate():
from pathlib import Path
from sphinx_source_tree import generate
rst = generate(
project_root=Path("."),
output=Path("docs/source_tree_ordered.rst"),
extensions=[".py", ".rst"],
order=[
"README.rst",
"src/app.py",
],
)
Path("docs/source_tree_ordered.rst").write_text(rst)Files listed in order that do not match any collected file (because
they are excluded by extension or ignore rules, or simply do not exist)
emit a warning to stderr and are silently skipped.
You can restrict how much of each file is shown by attaching Sphinx literalinclude range options to individual files. The following options are supported:
:lines:— explicit line numbers or ranges (e.g.1-20, 30):start-at:— include from the first line that contains the marker:start-after:— include from the line after the marker:end-before:— include up to, but not including, the marker line:end-at:— include up to and including the marker line
Via pyproject.toml (flat)
Add a [tool.sphinx-source-tree.file-options] table whose keys are
file paths relative to the project root. This mapping is used by all
output files that do not select a named profile:
[tool.sphinx-source-tree.file-options]
"src/app.py" = {"end-before" = "# *** Tests ***"}
"src/utils.py" = {"start-after" = "# -- public API --"}
"src/models.py" = {"lines" = "1-60"}This produces literalinclude blocks such as:
.. literalinclude:: ../src/app.py
:language: python
:caption: src/app.py
:end-before: # *** Tests ***Option keys may be written with either hyphens (end-before) or
underscores (end_before); both are accepted and normalised to the
hyphenated form that Sphinx expects. Unknown option keys emit a warning
to stderr and are ignored.
Via pyproject.toml (named profiles)
When you need different inclusion rules for different output files —
for example a full source tree and a compact one for LLMs — define
named profiles under [tool.sphinx-source-tree.file-options-profiles]
and select one per [[files]] entry with file-options-profile:
[tool.sphinx-source-tree]
ignore = ["__pycache__", "*.pyc", ".git"]
# "full" profile — no restrictions (empty table = include everything)
[tool.sphinx-source-tree.file-options-profiles.full]
# "compact" profile — trim each file at its test boundary
[tool.sphinx-source-tree.file-options-profiles.compact]
"src/app.py" = {"end-before" = "# ********** Tests **********"}
"src/models.py" = {"end-before" = "# ********** Tests **********"}
"src/utils.py" = {"lines" = "1-60"}
[[tool.sphinx-source-tree.files]]
output = "docs/source_tree_full.rst"
title = "Full project source"
file-options-profile = "full"
[[tool.sphinx-source-tree.files]]
output = "docs/source_tree.rst"
title = "Compact source for LLMs"
file-options-profile = "compact"Resolution order:
- If
file-options-profilenames a key infile-options-profiles, that profile's mapping is used. - If the name is not found, a warning is printed to stderr and the tool
falls back to the top-level
file-optionstable. - If no profile is specified, the top-level
file-optionstable is used directly (fully backward compatible).
Via the Python API
Pass the file_options keyword argument to generate() with the
already-resolved mapping for that output file. Profile selection happens
in _generate_from_cfg; when calling generate() directly simply
pass whichever dict applies:
from pathlib import Path
from sphinx_source_tree import generate
compact_options = {
"src/app.py": {"end-before": "# *** Tests ***"},
"src/utils.py": {"start-after": "# -- public API --"},
}
rst = generate(
project_root=Path("."),
output=Path("docs/source_tree2.rst"),
file_options=compact_options,
)Absolute paths are also accepted as keys and are resolved relative to
project_root automatically.
You can also call the generator from Python:
from pathlib import Path
from sphinx_source_tree import generate
rst = generate(
project_root=Path("."),
output=Path("docs/source_tree1.rst"),
depth=5,
extensions=[".py", ".rst"],
ignore=["__pycache__", "*.pyc"],
title="My project source",
)
Path("docs/source_tree.rst").write_text(rst)generate() returns the RST content as a string and never writes to
disk, so you can post-process or redirect as needed.
Lower-level helpers are also importable:
build_tree()-- ASCII tree string.collect_files()-- list ofPathobjects to include.detect_language()-- suffix-to-Sphinx-language mapping.load_config()-- read[tool.sphinx-source-tree]frompyproject.toml.
- Documentation is available on Read the Docs.
Run the tests:
pytest -vvvKeep the following hierarchy.
=====
title
=====
header
======
sub-header
----------
sub-sub-header
~~~~~~~~~~~~~~
sub-sub-sub-header
^^^^^^^^^^^^^^^^^^
sub-sub-sub-sub-header
++++++++++++++++++++++
sub-sub-sub-sub-sub-header
**************************
MIT
For security issues contact me at the e-mail given in the Author section.
For overall issues, go to GitHub.
Artur Barseghyan <artur.barseghyan@gmail.com>