Skip to content

Commit 4bed051

Browse files
Merge pull request #137 from barseghyanartur/dev
llms.txt
2 parents bb72863 + 5594cbe commit 4bed051

File tree

7 files changed

+332
-16
lines changed

7 files changed

+332
-16
lines changed

.github/workflows/gh-pages.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
name: Deploy llms.txt
2+
3+
permissions:
4+
contents: write # Required to push commits
5+
pages: write # If you’re deploying to GitHub Pages
6+
7+
on:
8+
push:
9+
branches: [ dev ]
10+
11+
jobs:
12+
deploy:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@v4
16+
- name: Set up Python
17+
uses: actions/setup-python@v5
18+
with: { python-version: '3.12' }
19+
- name: Install docs deps
20+
run: pip install -r examples/requirements/docs.txt
21+
- name: Build llms.txt
22+
# run: sphinx-build -n -D master_doc=llms -b text docs build/text llms.rst
23+
run: sphinx-build -n -b text docs build/text
24+
- name: Publish
25+
uses: peaceiris/actions-gh-pages@v4
26+
with:
27+
github_token: ${{ secrets.GITHUB_TOKEN }}
28+
publish_branch: gh-pages
29+
publish_dir: build/text
30+
allow_empty_commit: true

.readthedocs.yaml

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,11 @@
44
# Required
55
version: 2
66

7-
# Set the OS, Python version and other tools you might need
8-
build:
9-
os: ubuntu-22.04
10-
tools:
11-
python: "3.10"
12-
# You can also specify other tool versions:
13-
# nodejs: "20"
14-
# rust: "1.70"
15-
# golang: "1.20"
7+
# Optionally build your docs in additional formats such as PDF and ePub
8+
formats:
9+
- pdf
10+
- epub
11+
- htmlzip
1612

1713
# Build documentation in the "docs/" directory with Sphinx
1814
sphinx:
@@ -22,14 +18,26 @@ sphinx:
2218
# Fail on all warnings to avoid broken references
2319
# fail_on_warning: true
2420

25-
# Optionally build your docs in additional formats such as PDF and ePub
26-
formats:
27-
- pdf
28-
- epub
29-
3021
# Optional but recommended, declare the Python requirements required
3122
# to build your documentation
3223
# See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
3324
python:
3425
install:
3526
- requirements: examples/requirements/docs.txt
27+
28+
# Set the OS, Python version and other tools you might need
29+
build:
30+
os: ubuntu-22.04
31+
tools:
32+
python: "3.12"
33+
# You can also specify other tool versions:
34+
# nodejs: "20"
35+
# rust: "1.70"
36+
# golang: "1.20"
37+
jobs:
38+
build:
39+
# The default commands for generating the HTML and pdf formats will still run.
40+
htmlzip:
41+
- echo "Override default build command for htmlzip format"
42+
- mkdir -p $READTHEDOCS_OUTPUT/html/
43+
- sphinx-build -n -b text docs $READTHEDOCS_OUTPUT/html/

.secrets.baseline

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@
118118
"filename": "README.rst",
119119
"hashed_secret": "077d5a0e0f8bb517307a6e92a73b0a9aa959233c",
120120
"is_verified": true,
121-
"line_number": 589
121+
"line_number": 593
122122
}
123123
],
124124
"docs/_static/examples/recipes/sftp_storage_1.py": [
@@ -203,5 +203,5 @@
203203
}
204204
]
205205
},
206-
"generated_at": "2025-05-03T20:11:03Z"
206+
"generated_at": "2025-05-20T20:55:38Z"
207207
}

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ benchmark-test:
1010
pytest -vvrx --durations=0
1111

1212
build-docs:
13+
python scripts/generate_project_source_tree.py
14+
sphinx-build -n -b text docs builddocs
1315
sphinx-build -n -a -b html docs builddocs
1416
cd builddocs && zip -r ../builddocs.zip . -x ".*" && cd ..
1517

README.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,10 @@ faker-file
1919
:target: http://faker-file.readthedocs.io/en/latest/?badge=latest
2020
:alt: Documentation Status
2121

22+
.. image:: https://img.shields.io/badge/docs-llms.txt-blue
23+
:target: http://faker-file.readthedocs.io/en/latest/llms.txt
24+
:alt: llms.txt - documentation for LLMs
25+
2226
.. image:: https://img.shields.io/badge/license-MIT-blue.svg
2327
:target: https://github.com/barseghyanartur/faker-file/#License
2428
:alt: MIT

docs/llms.rst

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
.. include:: ../README.rst
2+
3+
----
4+
5+
.. include:: recipes.rst
6+
7+
----
8+
9+
.. include:: cheatsheet.rst
10+
11+
----
12+
13+
.. include:: creating_files.rst
14+
15+
----
16+
17+
.. include:: creating_images.rst
18+
19+
----
20+
21+
.. include:: creating_pdf.rst
22+
23+
----
24+
25+
.. include:: creating_docx.rst
26+
27+
----
28+
29+
.. include:: creating_odt.rst
30+
31+
----
32+
33+
.. include:: concepts.rst
34+
35+
----
36+
37+
.. include:: methodology.rst
38+
39+
----
40+
41+
.. include:: quick_start.rst
42+
43+
----
44+
45+
.. include:: recipes.rst
46+
47+
----
48+
49+
.. include:: cli.rst
50+
51+
----
52+
53+
.. include:: security.rst
54+
55+
----
56+
57+
.. include:: contributor_guidelines.rst
58+
59+
----
60+
61+
.. include:: changelog.rst
62+
63+
----
64+
65+
.. include:: package.rst
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
#!/usr/bin/env python3
2+
import argparse
3+
import fnmatch
4+
import os
5+
from pathlib import Path
6+
7+
8+
def build_tree(
9+
path: Path,
10+
max_depth: int,
11+
ignore_patterns: list,
12+
whitelist_dirs: list,
13+
include_all: bool,
14+
root: Path,
15+
prefix: str = "",
16+
) -> str:
17+
"""
18+
Recursively build an ASCII tree up to max_depth, applying whitelist and
19+
ignore rules.
20+
"""
21+
if max_depth < 0:
22+
return ""
23+
entries = sorted(
24+
path.iterdir(), key=lambda p: (p.is_file(), p.name.lower())
25+
)
26+
lines = []
27+
for i, entry in enumerate(entries):
28+
rel_path = entry.relative_to(root).as_posix()
29+
# Skip ignored patterns
30+
if any(fnmatch.fnmatch(rel_path, pat) for pat in ignore_patterns):
31+
continue
32+
# Enforce whitelist if not including all
33+
if not include_all and whitelist_dirs and not any(
34+
rel_path.startswith(w.rstrip("/")) for w in whitelist_dirs
35+
):
36+
continue
37+
connector = "└── " if i == len(entries) - 1 else "├── "
38+
lines.append(f"{prefix}{connector}{entry.name}")
39+
# Recurse into directories
40+
if entry.is_dir():
41+
extension = " " if i == len(entries) - 1 else "│ "
42+
subtree = build_tree(
43+
entry,
44+
max_depth - 1,
45+
ignore_patterns,
46+
whitelist_dirs,
47+
include_all,
48+
root,
49+
prefix + extension,
50+
)
51+
if subtree:
52+
lines += subtree.splitlines()
53+
return "\n".join(lines)
54+
55+
56+
def detect_language(path: Path) -> str:
57+
"""Map file suffix to Sphinx language."""
58+
mapping = {
59+
".py": "python",
60+
".js": "javascript",
61+
".java": "java",
62+
".md": "markdown",
63+
".yaml": "yaml",
64+
".yml": "yaml",
65+
".json": "json",
66+
".sh": "bash",
67+
".rst": "rst",
68+
}
69+
return mapping.get(path.suffix, "")
70+
71+
72+
def main():
73+
p = argparse.ArgumentParser(
74+
description="Auto-generate a .rst with tree + literalinclude blocks"
75+
)
76+
p.add_argument(
77+
"-p",
78+
"--project-root",
79+
type=Path,
80+
default=Path("."),
81+
help="Path to your project directory",
82+
)
83+
p.add_argument(
84+
"-d",
85+
"--depth",
86+
type=int,
87+
default=10,
88+
help="How many levels deep to print in the tree",
89+
)
90+
p.add_argument(
91+
"-o",
92+
"--output",
93+
type=Path,
94+
default=Path("docs/source_tree.rst"),
95+
help="Where to write the generated .rst",
96+
)
97+
p.add_argument(
98+
"-e",
99+
"--ext",
100+
nargs="+",
101+
default=[".py", ".md", ".js", ".rst"],
102+
help="Which file extensions to include via literalinclude",
103+
)
104+
p.add_argument(
105+
"-i",
106+
"--ignore",
107+
nargs="+",
108+
default=[
109+
"__pycache__",
110+
"*.pyc",
111+
"*.py,cover",
112+
".ipynb_checkpoints",
113+
"*.ipynb",
114+
"media",
115+
"static",
116+
"*.sqlite3",
117+
],
118+
help="Ignore files or dirs matching these glob patterns (relative to "
119+
"project root)",
120+
)
121+
p.add_argument(
122+
"-w",
123+
"--whitelist",
124+
nargs="+",
125+
default=["src", "docs", "examples", "scripts"],
126+
help="Directories (relative to project root) to include "
127+
"unless --include-all is given",
128+
)
129+
p.add_argument(
130+
"--include-all",
131+
action="store_true",
132+
help="Include all files regardless of whitelist",
133+
)
134+
args = p.parse_args()
135+
136+
root = args.project_root.resolve()
137+
ignore_patterns = args.ignore
138+
whitelist_dirs = args.whitelist
139+
include_all = args.include_all
140+
output = args.output.resolve()
141+
output_dir = output.parent.resolve()
142+
143+
# Header + tree
144+
header = f"""Project source-tree
145+
===================
146+
147+
Below is the layout of our project (to {args.depth} levels), followed by
148+
the contents of each key file.
149+
150+
.. code-block:: bash
151+
:caption: Project directory layout
152+
153+
{root.name}/
154+
"""
155+
tree = build_tree(
156+
root,
157+
args.depth,
158+
ignore_patterns,
159+
whitelist_dirs,
160+
include_all,
161+
root,
162+
prefix=" ",
163+
)
164+
out = [header, tree, ""]
165+
166+
# Walk and collect files
167+
for filepath in sorted(root.rglob("*")):
168+
if not filepath.is_file() or filepath.suffix not in args.ext:
169+
continue
170+
rel_path = filepath.relative_to(root).as_posix()
171+
# Skip ignored
172+
if any(fnmatch.fnmatch(rel_path, pat) for pat in ignore_patterns):
173+
continue
174+
# Enforce whitelist
175+
if (
176+
not include_all
177+
and whitelist_dirs
178+
and not any(
179+
rel_path.startswith(w.rstrip("/")) for w in whitelist_dirs)
180+
):
181+
continue
182+
183+
# Compute include path relative to output_dir
184+
include_path = os.path.relpath(filepath, output_dir).replace(
185+
os.sep, "/"
186+
)
187+
title = rel_path
188+
underline = "-" * len(title)
189+
lang = detect_language(filepath)
190+
out += [
191+
title,
192+
underline,
193+
"",
194+
f".. literalinclude:: {include_path}",
195+
f" :language: {lang}" if lang else "",
196+
f" :caption: {rel_path}",
197+
# " :linenos:",
198+
"",
199+
]
200+
201+
# Write output
202+
args.output.write_text("\n".join(line for line in out if line is not None))
203+
print(f"Wrote {args.output}")
204+
205+
206+
if __name__ == "__main__":
207+
main()

0 commit comments

Comments
 (0)