sphinx-source-tree
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.
Prerequisites
Python 3.10+
Installation
uv pip install sphinx-source-tree
Usage
Quick start
Run in your project root:
sphinx-source-tree
This 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 --stdout
CLI reference
sphinx-source-tree [OPTIONS]
-p, --project-root PATHProject directory. Default: current directory.
-d, --depth NMaximum tree depth. Default:
10.-o, --output PATHOutput
.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-allInclude everything regardless of whitelist. Default: on.
-t, --title TEXTRST section title. Default:
Project source-tree.--linenos / --no-linenosAttach
:linenos:toliteralincludedirectives. Default: off.--stdoutWrite to stdout instead of the output file.
-V, --versionShow version and exit.
Configuration via pyproject.toml
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"}
Key names use hyphens (include-all) to follow TOML/PEP 621
convention; they are normalised internally.
Multiple output files
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 = false
The 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.
Per-file inclusion options
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.
Python API
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
Documentation is available on Read the Docs.
Tests
Run the tests:
pytest -vvv
Writing documentation
Keep 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
**************************
License
MIT
Support
For security issues contact me at the e-mail given in the Author section.
For overall issues, go to GitHub.
Project documentation
Contents: