docpull 4.3.0__tar.gz → 4.4.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. {docpull-4.3.0 → docpull-4.4.0}/LICENSE +1 -1
  2. {docpull-4.3.0/src/docpull.egg-info → docpull-4.4.0}/PKG-INFO +102 -42
  3. {docpull-4.3.0 → docpull-4.4.0}/README.md +91 -33
  4. {docpull-4.3.0 → docpull-4.4.0}/pyproject.toml +28 -12
  5. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/__init__.py +2 -2
  6. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/benchmark.py +1 -1
  7. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/cli.py +13 -5
  8. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/conversion/extractor.py +0 -9
  9. docpull-4.4.0/src/docpull/conversion/filings.py +57 -0
  10. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/conversion/markdown.py +1 -7
  11. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/core/fetcher.py +1 -5
  12. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/discovery/link_extractors/enhanced.py +0 -3
  13. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/discovery/link_extractors/static.py +0 -1
  14. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/discovery/sitemap.py +0 -4
  15. docpull-4.4.0/src/docpull/evidence_pack.py +1050 -0
  16. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/http/client.py +57 -9
  17. docpull-4.4.0/src/docpull/mcp/__init__.py +10 -0
  18. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/mcp/server.py +12 -12
  19. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/mcp/sources.py +3 -3
  20. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/mcp/tools.py +8 -8
  21. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/metadata_extractor.py +0 -10
  22. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/models/config.py +10 -5
  23. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/models/document.py +24 -1
  24. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/models/profiles.py +23 -0
  25. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/parallel_workflows.py +2 -2
  26. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/pipeline/manifest.py +15 -0
  27. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/pipeline/steps/convert.py +9 -0
  28. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/pipeline/steps/metadata.py +0 -5
  29. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/pipeline/steps/save.py +2 -8
  30. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/pipeline/steps/save_json.py +0 -2
  31. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/pipeline/steps/save_okf.py +2 -2
  32. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/security/download_policy.py +4 -4
  33. {docpull-4.3.0 → docpull-4.4.0/src/docpull.egg-info}/PKG-INFO +102 -42
  34. {docpull-4.3.0 → docpull-4.4.0}/src/docpull.egg-info/SOURCES.txt +3 -0
  35. {docpull-4.3.0 → docpull-4.4.0}/src/docpull.egg-info/requires.txt +2 -2
  36. {docpull-4.3.0 → docpull-4.4.0}/tests/test_cache_conditional_get.py +0 -9
  37. {docpull-4.3.0 → docpull-4.4.0}/tests/test_ci_policy.py +1 -1
  38. {docpull-4.3.0 → docpull-4.4.0}/tests/test_cli.py +28 -1
  39. {docpull-4.3.0 → docpull-4.4.0}/tests/test_conversion.py +1 -1
  40. {docpull-4.3.0 → docpull-4.4.0}/tests/test_discovery.py +2 -2
  41. docpull-4.4.0/tests/test_evidence_pack.py +236 -0
  42. {docpull-4.3.0 → docpull-4.4.0}/tests/test_integration.py +19 -0
  43. {docpull-4.3.0 → docpull-4.4.0}/tests/test_mcp_tools.py +11 -2
  44. {docpull-4.3.0 → docpull-4.4.0}/tests/test_outputs_e2e.py +1 -1
  45. {docpull-4.3.0 → docpull-4.4.0}/tests/test_parallel_workflows.py +10 -0
  46. {docpull-4.3.0 → docpull-4.4.0}/tests/test_security_hardening.py +98 -3
  47. docpull-4.3.0/src/docpull/mcp/__init__.py +0 -9
  48. {docpull-4.3.0 → docpull-4.4.0}/setup.cfg +0 -0
  49. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/__main__.py +0 -0
  50. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/cache/__init__.py +0 -0
  51. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/cache/frontier.py +0 -0
  52. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/cache/manager.py +0 -0
  53. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/cache/streaming_dedup.py +0 -0
  54. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/conversion/__init__.py +0 -0
  55. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/conversion/chunking.py +0 -0
  56. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/conversion/protocols.py +0 -0
  57. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/conversion/special_cases.py +0 -0
  58. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/conversion/trafilatura_extractor.py +0 -0
  59. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/core/__init__.py +0 -0
  60. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/discovery/__init__.py +0 -0
  61. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/discovery/_fetch.py +0 -0
  62. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/discovery/composite.py +0 -0
  63. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/discovery/crawler.py +0 -0
  64. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/discovery/filters.py +0 -0
  65. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/discovery/link_extractors/__init__.py +0 -0
  66. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/discovery/link_extractors/protocols.py +0 -0
  67. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/discovery/protocols.py +0 -0
  68. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/doctor.py +0 -0
  69. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/fixtures/__init__.py +0 -0
  70. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/fixtures/parallel-search-extract.json +0 -0
  71. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/http/__init__.py +0 -0
  72. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/http/protocols.py +0 -0
  73. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/http/rate_limiter.py +0 -0
  74. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/judge.py +0 -0
  75. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/models/__init__.py +0 -0
  76. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/models/events.py +0 -0
  77. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/models/run.py +0 -0
  78. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/pack_tools.py +0 -0
  79. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/passk.py +0 -0
  80. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/pipeline/__init__.py +0 -0
  81. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/pipeline/base.py +0 -0
  82. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/pipeline/steps/__init__.py +0 -0
  83. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/pipeline/steps/chunk.py +0 -0
  84. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/pipeline/steps/dedup.py +0 -0
  85. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/pipeline/steps/fetch.py +0 -0
  86. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/pipeline/steps/save_ndjson.py +0 -0
  87. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/pipeline/steps/save_sqlite.py +0 -0
  88. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/pipeline/steps/validate.py +0 -0
  89. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/provider_cli.py +0 -0
  90. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/provider_keys.py +0 -0
  91. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/py.typed +0 -0
  92. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/scraper.py +0 -0
  93. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/security/__init__.py +0 -0
  94. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/security/robots.py +0 -0
  95. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/security/url_validator.py +0 -0
  96. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/source_scoring.py +0 -0
  97. {docpull-4.3.0 → docpull-4.4.0}/src/docpull/time_utils.py +0 -0
  98. {docpull-4.3.0 → docpull-4.4.0}/src/docpull.egg-info/dependency_links.txt +0 -0
  99. {docpull-4.3.0 → docpull-4.4.0}/src/docpull.egg-info/entry_points.txt +0 -0
  100. {docpull-4.3.0 → docpull-4.4.0}/src/docpull.egg-info/top_level.txt +0 -0
  101. {docpull-4.3.0 → docpull-4.4.0}/tests/test_benchmark.py +0 -0
  102. {docpull-4.3.0 → docpull-4.4.0}/tests/test_chunking.py +0 -0
  103. {docpull-4.3.0 → docpull-4.4.0}/tests/test_convert_step_new.py +0 -0
  104. {docpull-4.3.0 → docpull-4.4.0}/tests/test_document_record.py +0 -0
  105. {docpull-4.3.0 → docpull-4.4.0}/tests/test_frontier_resume.py +0 -0
  106. {docpull-4.3.0 → docpull-4.4.0}/tests/test_judge.py +0 -0
  107. {docpull-4.3.0 → docpull-4.4.0}/tests/test_link_extractors.py +0 -0
  108. {docpull-4.3.0 → docpull-4.4.0}/tests/test_mcp_server.py +0 -0
  109. {docpull-4.3.0 → docpull-4.4.0}/tests/test_naming.py +0 -0
  110. {docpull-4.3.0 → docpull-4.4.0}/tests/test_pack_tools.py +0 -0
  111. {docpull-4.3.0 → docpull-4.4.0}/tests/test_passk.py +0 -0
  112. {docpull-4.3.0 → docpull-4.4.0}/tests/test_pipeline.py +0 -0
  113. {docpull-4.3.0 → docpull-4.4.0}/tests/test_provider_cli.py +0 -0
  114. {docpull-4.3.0 → docpull-4.4.0}/tests/test_real_site_regressions.py +0 -0
  115. {docpull-4.3.0 → docpull-4.4.0}/tests/test_save_ndjson.py +0 -0
  116. {docpull-4.3.0 → docpull-4.4.0}/tests/test_save_sqlite.py +0 -0
  117. {docpull-4.3.0 → docpull-4.4.0}/tests/test_special_cases.py +0 -0
  118. {docpull-4.3.0 → docpull-4.4.0}/tests/test_time_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2025 Raintree Technology
3
+ Copyright (c) 2025-2026 Raintree Technology
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,26 +1,28 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: docpull
3
- Version: 4.3.0
4
- Summary: Pull documentation from the web and convert to clean markdown
3
+ Version: 4.4.0
4
+ Summary: Convert public web pages into clean Markdown for AI agents and RAG
5
5
  Author-email: Zachary Roth <support@raintree.technology>
6
6
  Maintainer-email: Raintree Technology <support@raintree.technology>
7
7
  License-Expression: MIT
8
- Project-URL: Homepage, https://github.com/raintree-technology/docpull
9
- Project-URL: Documentation, https://github.com/raintree-technology/docpull#readme
8
+ Project-URL: Homepage, https://docpull.raintree.technology
9
+ Project-URL: Documentation, https://docpull.raintree.technology
10
10
  Project-URL: Repository, https://github.com/raintree-technology/docpull
11
11
  Project-URL: Source Code, https://github.com/raintree-technology/docpull
12
12
  Project-URL: Bug Tracker, https://github.com/raintree-technology/docpull/issues
13
13
  Project-URL: Releases, https://github.com/raintree-technology/docpull/releases
14
- Keywords: python,markdown,documentation,web-scraping,developer-tools,claude,ai-training-data
14
+ Project-URL: MCP Plugin, https://github.com/raintree-technology/docpull/tree/main/plugin
15
+ Project-URL: Comparison Guide, https://github.com/raintree-technology/docpull/blob/main/docs/alternatives.md
16
+ Project-URL: Download Stats, https://pepy.tech/project/docpull
17
+ Project-URL: Changelog, https://github.com/raintree-technology/docpull/blob/main/docs/CHANGELOG.md
18
+ Keywords: python,markdown,documentation,web-extraction,web-scraping,web-crawler,crawler,developer-tools,cli,rag,llm,mcp,ai-agents,context-engineering,claude,ai-training-data
15
19
  Classifier: Development Status :: 5 - Production/Stable
16
20
  Classifier: Intended Audience :: Developers
17
21
  Classifier: Intended Audience :: Information Technology
18
22
  Classifier: Intended Audience :: Science/Research
19
23
  Classifier: Intended Audience :: Education
20
24
  Classifier: Environment :: Console
21
- Classifier: Topic :: Documentation
22
25
  Classifier: Topic :: Internet :: WWW/HTTP :: Indexing/Search
23
- Classifier: Topic :: Software Development :: Documentation
24
26
  Classifier: Topic :: Text Processing :: Markup :: HTML
25
27
  Classifier: Topic :: Text Processing :: Markup :: Markdown
26
28
  Classifier: Topic :: Utilities
@@ -50,7 +52,7 @@ Requires-Dist: pyyaml>=6.0
50
52
  Requires-Dist: pydantic>=2.0
51
53
  Requires-Dist: urllib3>=2.7.0
52
54
  Provides-Extra: proxy
53
- Requires-Dist: aiohttp-socks>=0.8.0; extra == "proxy"
55
+ Requires-Dist: aiohttp-socks>=0.11.0; extra == "proxy"
54
56
  Provides-Extra: normalize
55
57
  Requires-Dist: url-normalize>=1.4.0; extra == "normalize"
56
58
  Provides-Extra: trafilatura
@@ -69,7 +71,7 @@ Requires-Dist: raindrop-ai>=0.0.50; extra == "observability"
69
71
  Provides-Extra: llm
70
72
  Requires-Dist: tiktoken>=0.7.0; extra == "llm"
71
73
  Provides-Extra: all
72
- Requires-Dist: aiohttp-socks>=0.8.0; extra == "all"
74
+ Requires-Dist: aiohttp-socks>=0.11.0; extra == "all"
73
75
  Requires-Dist: url-normalize>=1.4.0; extra == "all"
74
76
  Requires-Dist: trafilatura>=1.12.0; extra == "all"
75
77
  Requires-Dist: tiktoken>=0.7.0; extra == "all"
@@ -96,7 +98,7 @@ Dynamic: license-file
96
98
 
97
99
  # docpull
98
100
 
99
- **Security-hardened, browser-free web scraper and crawler that turns server-rendered pages into clean, AI-ready Markdown — fast.**
101
+ **Public web to agent-ready Markdown. Fast, local, browser-free.**
100
102
 
101
103
  [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
102
104
  [![PyPI version](https://badge.fury.io/py/docpull.svg)](https://badge.fury.io/py/docpull)
@@ -110,37 +112,35 @@ Dynamic: license-file
110
112
  </a>
111
113
  </p>
112
114
 
113
- ## Download History
115
+ docpull is a Python CLI, SDK, and MCP server for pulling public static and
116
+ server-rendered web pages into clean Markdown, NDJSON, OKF bundles, SQLite, or
117
+ local archives. Web-source extraction is the core workflow; docs are one
118
+ high-value lane alongside blogs, API references, vendor pages, OpenAPI specs,
119
+ and other server-rendered web content. It uses async HTTP instead
120
+ of Playwright, preserves source metadata, and is built for agent-selected URLs
121
+ with SSRF, XXE, DNS-rebinding, and CRLF-injection protections enabled by
122
+ default.
114
123
 
115
- <a href="https://pepy.tech/project/docpull">
116
- <img alt="Cumulative PyPI download history chart for docpull" src="docs/cumulative-downloads-history.svg" />
117
- </a>
124
+ Use docpull when you need to:
118
125
 
119
- ## Star History
126
+ - Load public web pages, docs, API references, or blogs into an LLM context.
127
+ - Build inspectable source corpora for RAG/search pipelines.
128
+ - Give a coding agent a local MCP tool for fetching, caching, grepping, and
129
+ reading docs.
130
+ - Mirror public web content for offline work while keeping source attribution.
120
131
 
121
- <a href="https://star-history.com/#raintree-technology/docpull&Date">
122
- <picture>
123
- <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=raintree-technology/docpull&type=Date&theme=dark" />
124
- <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=raintree-technology/docpull&type=Date" />
125
- <img alt="Star history chart for raintree-technology/docpull" src="https://api.star-history.com/svg?repos=raintree-technology/docpull&type=Date" />
126
- </picture>
127
- </a>
132
+ docpull intentionally does not render JavaScript. For JS-only apps, browser
133
+ automation, paid extraction APIs, or general web crawling, see
134
+ [`docs/scraping-boundary.md`](docs/scraping-boundary.md) and
135
+ [`docs/alternatives.md`](docs/alternatives.md).
136
+
137
+ ## Try it in 60 seconds
128
138
 
129
- docpull is a web scraper for static and server-rendered sites, with
130
- documentation crawling as its sharpest default workflow. It uses async HTTP (not
131
- Playwright) to fetch pages, discover links, extract main content, and write clean
132
- Markdown with source-URL frontmatter in seconds, with a small install
133
- footprint. It won't render JavaScript, but for the large class of sites that
134
- don't need it (API references, vendor docs, Python/Go stdlib, blogs, OpenAPI
135
- specs, Next.js and Docusaurus builds), it is a fast, auditable,
136
- sandbox-friendly way to pipe web content into an LLM context, a RAG index, or an
137
- offline archive. SSRF, XXE, DNS-rebinding, and CRLF-injection protections are on
138
- by default — a necessity when an AI agent is choosing the URLs.
139
-
140
- docpull is intentionally not a general browser-automation scraper. See
141
- [`docs/scraping-boundary.md`](docs/scraping-boundary.md) for the exact product
142
- boundary and when to use Scrapy, Crawlee, hosted extraction APIs, or trafilatura
143
- directly.
139
+ ```bash
140
+ pip install docpull
141
+ docpull https://docs.python.org/3/library/asyncio.html --single
142
+ docpull https://docs.python.org/3/library/asyncio.html --profile llm --stream | jq .
143
+ ```
144
144
 
145
145
  ## Install
146
146
 
@@ -169,12 +169,33 @@ docpull https://docs.example.com/guide --single
169
169
  docpull https://docs.example.com --profile llm --stream | jq .
170
170
 
171
171
  # Open Knowledge Format bundle for agent/wiki interoperability
172
- docpull https://docs.example.com --format okf
172
+ docpull https://example.com --format okf -o ./site-okf
173
+
174
+ # SEC filing evidence pack with citation anchors
175
+ docpull evidence-pack filings.ndjson \
176
+ --profile sec-filing \
177
+ --rules docs/examples/vendor-dependency-rules.yml \
178
+ --sec-user-agent "YourOrg your-email@example.com" \
179
+ --output-dir packs/dla-vendors
173
180
 
174
181
  # Mirror scraped content for offline use
175
182
  docpull https://docs.example.com --profile mirror --cache
176
183
  ```
177
184
 
185
+ ## Project traction
186
+
187
+ <a href="https://pepy.tech/project/docpull">
188
+ <img alt="Cumulative PyPI download history chart for docpull" src="docs/cumulative-downloads-history.svg" />
189
+ </a>
190
+
191
+ <a href="https://star-history.com/#raintree-technology/docpull&Date">
192
+ <picture>
193
+ <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=raintree-technology/docpull&type=Date&theme=dark" />
194
+ <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=raintree-technology/docpull&type=Date" />
195
+ <img alt="Star history chart for raintree-technology/docpull" src="https://api.star-history.com/svg?repos=raintree-technology/docpull&type=Date" />
196
+ </picture>
197
+ </a>
198
+
178
199
  ## Framework-aware extraction
179
200
 
180
201
  docpull inspects each page before running the generic extractor and can pull
@@ -280,15 +301,37 @@ async def tool_call(url: str) -> str:
280
301
  docpull https://site.com --profile rag # Default. Dedup, rich metadata.
281
302
  docpull https://site.com --profile llm # NDJSON + chunks + metadata; JS-only pages skip unless --strict-js-required is passed.
282
303
  docpull https://site.com --profile okf # OKF bundle with generated index.md files.
304
+ docpull https://site.com --profile sec-filing # EDGAR-friendly NDJSON chunks, trafilatura, Inline XBRL cleanup.
283
305
  docpull https://site.com --profile mirror # Full archive, polite, cached, hierarchical paths.
284
306
  docpull https://site.com --profile quick # Sampling: 50 pages, depth 2.
285
307
  ```
286
308
 
309
+ ## SEC filing evidence packs
310
+
311
+ `docpull evidence-pack` turns an NDJSON list of filings into a source pack for
312
+ government/vendor-dependency research. Each input row needs a filing URL under
313
+ `primary_document_url` or `url`; filing fields such as `cik`,
314
+ `accession_number`, `form`, `filing_date`, and `issuer_name` are carried into
315
+ the emitted records.
316
+
317
+ Use `docs/examples/vendor-dependency-rules.yml` as a starting rule profile for
318
+ government customer, customer concentration, segment revenue, and related-party
319
+ signals. Rules can use literal strings or regex entries with optional
320
+ confidence values.
321
+
322
+ The pack writes `evidence.pack.json`, `documents.ndjson`, `evidence.ndjson`,
323
+ `diagnostics.ndjson`, `sources.md`, `corpus.manifest.json`,
324
+ `EVIDENCE_CONTEXT.md`, and `AGENT_CONTEXT.md`. Evidence hits include source URL,
325
+ chunk id, section heading when available, quote, surrounding context, source
326
+ document hash, confidence, and extraction method. Diagnostics flag no readable
327
+ content, high XBRL noise, degraded tables, missing evidence categories, and
328
+ source hash changes since the last run.
329
+
287
330
  ## Parallel context packs
288
331
 
289
332
  `docpull[parallel]` adds an optional source-discovery and research layer on top
290
333
  of Parallel web intelligence APIs. Use the core crawler when you already know
291
- the docs URL. Use Parallel packs when an agent needs current web sources found,
334
+ the target URL. Use Parallel packs when an agent needs current web sources found,
292
335
  extracted, researched, organized, and loaded as durable local context.
293
336
 
294
337
  Parallel handles live Search, Extract, Task, Entity Search, FindAll, TaskGroup,
@@ -511,7 +554,7 @@ Write:
511
554
  - `parallel_context_pack(...)` — build or dry-run a Parallel Search + Extract pack without shelling out.
512
555
  - `parallel_api_pack(source, kind?, output_dir?)` — build a pack from `llms.txt` or OpenAPI.
513
556
  - `add_source(name, url, description?, category?, max_pages?, force?)` — register a user alias (HTTPS-only, atomic write to `sources.yaml`).
514
- - `remove_source(name, delete_cache?)` — drop a user alias and (optionally) its cached docs.
557
+ - `remove_source(name, delete_cache?)` — drop a user alias and (optionally) its cached Markdown.
515
558
 
516
559
  All schema-backed tools return `structuredContent` validated against an
517
560
  `outputSchema` for clients that prefer typed output. `fetch_url` intentionally
@@ -564,7 +607,22 @@ NDJSON (one record per page or chunk):
564
607
  Open Knowledge Format:
565
608
 
566
609
  ```bash
567
- docpull https://docs.example.com --format okf
610
+ docpull https://example.com --format okf -o ./site-okf
611
+
612
+ # Equivalent profile form
613
+ docpull https://example.com --profile okf -o ./site-okf
614
+ ```
615
+
616
+ A generated OKF bundle has a normal Markdown tree:
617
+
618
+ ```text
619
+ site-okf/
620
+ index.md
621
+ corpus.manifest.json
622
+ _root.md
623
+ docs/
624
+ index.md
625
+ getting-started.md
568
626
  ```
569
627
 
570
628
  OKF output is an opt-in Markdown bundle. Scraped pages are written as concept
@@ -610,6 +668,8 @@ for hit in hits:
610
668
  When running with `--proxy`, DNS pinning is delegated to the proxy. Pass
611
669
  `--require-pinned-dns` to refuse this configuration and keep the connector-
612
670
  level SSRF guarantees in effect.
671
+ HTTP and HTTPS proxies work with the base install; SOCKS proxies require
672
+ `pip install "docpull[proxy]"`.
613
673
 
614
674
  ## Options
615
675
 
@@ -617,7 +677,7 @@ Run `docpull --help` for the full list. Highlights:
617
677
 
618
678
  ```
619
679
  Core:
620
- --profile {rag,mirror,quick,llm,okf}
680
+ --profile {rag,mirror,quick,llm,okf,sec-filing}
621
681
  --single Fetch one URL (no crawl)
622
682
  --format {markdown,json,ndjson,sqlite,okf}
623
683
  --stream Stream NDJSON to stdout
@@ -1,6 +1,6 @@
1
1
  # docpull
2
2
 
3
- **Security-hardened, browser-free web scraper and crawler that turns server-rendered pages into clean, AI-ready Markdown — fast.**
3
+ **Public web to agent-ready Markdown. Fast, local, browser-free.**
4
4
 
5
5
  [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
6
6
  [![PyPI version](https://badge.fury.io/py/docpull.svg)](https://badge.fury.io/py/docpull)
@@ -14,37 +14,35 @@
14
14
  </a>
15
15
  </p>
16
16
 
17
- ## Download History
17
+ docpull is a Python CLI, SDK, and MCP server for pulling public static and
18
+ server-rendered web pages into clean Markdown, NDJSON, OKF bundles, SQLite, or
19
+ local archives. Web-source extraction is the core workflow; docs are one
20
+ high-value lane alongside blogs, API references, vendor pages, OpenAPI specs,
21
+ and other server-rendered web content. It uses async HTTP instead
22
+ of Playwright, preserves source metadata, and is built for agent-selected URLs
23
+ with SSRF, XXE, DNS-rebinding, and CRLF-injection protections enabled by
24
+ default.
18
25
 
19
- <a href="https://pepy.tech/project/docpull">
20
- <img alt="Cumulative PyPI download history chart for docpull" src="docs/cumulative-downloads-history.svg" />
21
- </a>
26
+ Use docpull when you need to:
22
27
 
23
- ## Star History
28
+ - Load public web pages, docs, API references, or blogs into an LLM context.
29
+ - Build inspectable source corpora for RAG/search pipelines.
30
+ - Give a coding agent a local MCP tool for fetching, caching, grepping, and
31
+ reading docs.
32
+ - Mirror public web content for offline work while keeping source attribution.
24
33
 
25
- <a href="https://star-history.com/#raintree-technology/docpull&Date">
26
- <picture>
27
- <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=raintree-technology/docpull&type=Date&theme=dark" />
28
- <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=raintree-technology/docpull&type=Date" />
29
- <img alt="Star history chart for raintree-technology/docpull" src="https://api.star-history.com/svg?repos=raintree-technology/docpull&type=Date" />
30
- </picture>
31
- </a>
34
+ docpull intentionally does not render JavaScript. For JS-only apps, browser
35
+ automation, paid extraction APIs, or general web crawling, see
36
+ [`docs/scraping-boundary.md`](docs/scraping-boundary.md) and
37
+ [`docs/alternatives.md`](docs/alternatives.md).
38
+
39
+ ## Try it in 60 seconds
32
40
 
33
- docpull is a web scraper for static and server-rendered sites, with
34
- documentation crawling as its sharpest default workflow. It uses async HTTP (not
35
- Playwright) to fetch pages, discover links, extract main content, and write clean
36
- Markdown with source-URL frontmatter in seconds, with a small install
37
- footprint. It won't render JavaScript, but for the large class of sites that
38
- don't need it (API references, vendor docs, Python/Go stdlib, blogs, OpenAPI
39
- specs, Next.js and Docusaurus builds), it is a fast, auditable,
40
- sandbox-friendly way to pipe web content into an LLM context, a RAG index, or an
41
- offline archive. SSRF, XXE, DNS-rebinding, and CRLF-injection protections are on
42
- by default — a necessity when an AI agent is choosing the URLs.
43
-
44
- docpull is intentionally not a general browser-automation scraper. See
45
- [`docs/scraping-boundary.md`](docs/scraping-boundary.md) for the exact product
46
- boundary and when to use Scrapy, Crawlee, hosted extraction APIs, or trafilatura
47
- directly.
41
+ ```bash
42
+ pip install docpull
43
+ docpull https://docs.python.org/3/library/asyncio.html --single
44
+ docpull https://docs.python.org/3/library/asyncio.html --profile llm --stream | jq .
45
+ ```
48
46
 
49
47
  ## Install
50
48
 
@@ -73,12 +71,33 @@ docpull https://docs.example.com/guide --single
73
71
  docpull https://docs.example.com --profile llm --stream | jq .
74
72
 
75
73
  # Open Knowledge Format bundle for agent/wiki interoperability
76
- docpull https://docs.example.com --format okf
74
+ docpull https://example.com --format okf -o ./site-okf
75
+
76
+ # SEC filing evidence pack with citation anchors
77
+ docpull evidence-pack filings.ndjson \
78
+ --profile sec-filing \
79
+ --rules docs/examples/vendor-dependency-rules.yml \
80
+ --sec-user-agent "YourOrg your-email@example.com" \
81
+ --output-dir packs/dla-vendors
77
82
 
78
83
  # Mirror scraped content for offline use
79
84
  docpull https://docs.example.com --profile mirror --cache
80
85
  ```
81
86
 
87
+ ## Project traction
88
+
89
+ <a href="https://pepy.tech/project/docpull">
90
+ <img alt="Cumulative PyPI download history chart for docpull" src="docs/cumulative-downloads-history.svg" />
91
+ </a>
92
+
93
+ <a href="https://star-history.com/#raintree-technology/docpull&Date">
94
+ <picture>
95
+ <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=raintree-technology/docpull&type=Date&theme=dark" />
96
+ <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=raintree-technology/docpull&type=Date" />
97
+ <img alt="Star history chart for raintree-technology/docpull" src="https://api.star-history.com/svg?repos=raintree-technology/docpull&type=Date" />
98
+ </picture>
99
+ </a>
100
+
82
101
  ## Framework-aware extraction
83
102
 
84
103
  docpull inspects each page before running the generic extractor and can pull
@@ -184,15 +203,37 @@ async def tool_call(url: str) -> str:
184
203
  docpull https://site.com --profile rag # Default. Dedup, rich metadata.
185
204
  docpull https://site.com --profile llm # NDJSON + chunks + metadata; JS-only pages skip unless --strict-js-required is passed.
186
205
  docpull https://site.com --profile okf # OKF bundle with generated index.md files.
206
+ docpull https://site.com --profile sec-filing # EDGAR-friendly NDJSON chunks, trafilatura, Inline XBRL cleanup.
187
207
  docpull https://site.com --profile mirror # Full archive, polite, cached, hierarchical paths.
188
208
  docpull https://site.com --profile quick # Sampling: 50 pages, depth 2.
189
209
  ```
190
210
 
211
+ ## SEC filing evidence packs
212
+
213
+ `docpull evidence-pack` turns an NDJSON list of filings into a source pack for
214
+ government/vendor-dependency research. Each input row needs a filing URL under
215
+ `primary_document_url` or `url`; filing fields such as `cik`,
216
+ `accession_number`, `form`, `filing_date`, and `issuer_name` are carried into
217
+ the emitted records.
218
+
219
+ Use `docs/examples/vendor-dependency-rules.yml` as a starting rule profile for
220
+ government customer, customer concentration, segment revenue, and related-party
221
+ signals. Rules can use literal strings or regex entries with optional
222
+ confidence values.
223
+
224
+ The pack writes `evidence.pack.json`, `documents.ndjson`, `evidence.ndjson`,
225
+ `diagnostics.ndjson`, `sources.md`, `corpus.manifest.json`,
226
+ `EVIDENCE_CONTEXT.md`, and `AGENT_CONTEXT.md`. Evidence hits include source URL,
227
+ chunk id, section heading when available, quote, surrounding context, source
228
+ document hash, confidence, and extraction method. Diagnostics flag no readable
229
+ content, high XBRL noise, degraded tables, missing evidence categories, and
230
+ source hash changes since the last run.
231
+
191
232
  ## Parallel context packs
192
233
 
193
234
  `docpull[parallel]` adds an optional source-discovery and research layer on top
194
235
  of Parallel web intelligence APIs. Use the core crawler when you already know
195
- the docs URL. Use Parallel packs when an agent needs current web sources found,
236
+ the target URL. Use Parallel packs when an agent needs current web sources found,
196
237
  extracted, researched, organized, and loaded as durable local context.
197
238
 
198
239
  Parallel handles live Search, Extract, Task, Entity Search, FindAll, TaskGroup,
@@ -415,7 +456,7 @@ Write:
415
456
  - `parallel_context_pack(...)` — build or dry-run a Parallel Search + Extract pack without shelling out.
416
457
  - `parallel_api_pack(source, kind?, output_dir?)` — build a pack from `llms.txt` or OpenAPI.
417
458
  - `add_source(name, url, description?, category?, max_pages?, force?)` — register a user alias (HTTPS-only, atomic write to `sources.yaml`).
418
- - `remove_source(name, delete_cache?)` — drop a user alias and (optionally) its cached docs.
459
+ - `remove_source(name, delete_cache?)` — drop a user alias and (optionally) its cached Markdown.
419
460
 
420
461
  All schema-backed tools return `structuredContent` validated against an
421
462
  `outputSchema` for clients that prefer typed output. `fetch_url` intentionally
@@ -468,7 +509,22 @@ NDJSON (one record per page or chunk):
468
509
  Open Knowledge Format:
469
510
 
470
511
  ```bash
471
- docpull https://docs.example.com --format okf
512
+ docpull https://example.com --format okf -o ./site-okf
513
+
514
+ # Equivalent profile form
515
+ docpull https://example.com --profile okf -o ./site-okf
516
+ ```
517
+
518
+ A generated OKF bundle has a normal Markdown tree:
519
+
520
+ ```text
521
+ site-okf/
522
+ index.md
523
+ corpus.manifest.json
524
+ _root.md
525
+ docs/
526
+ index.md
527
+ getting-started.md
472
528
  ```
473
529
 
474
530
  OKF output is an opt-in Markdown bundle. Scraped pages are written as concept
@@ -514,6 +570,8 @@ for hit in hits:
514
570
  When running with `--proxy`, DNS pinning is delegated to the proxy. Pass
515
571
  `--require-pinned-dns` to refuse this configuration and keep the connector-
516
572
  level SSRF guarantees in effect.
573
+ HTTP and HTTPS proxies work with the base install; SOCKS proxies require
574
+ `pip install "docpull[proxy]"`.
517
575
 
518
576
  ## Options
519
577
 
@@ -521,7 +579,7 @@ Run `docpull --help` for the full list. Highlights:
521
579
 
522
580
  ```
523
581
  Core:
524
- --profile {rag,mirror,quick,llm,okf}
582
+ --profile {rag,mirror,quick,llm,okf,sec-filing}
525
583
  --single Fetch one URL (no crawl)
526
584
  --format {markdown,json,ndjson,sqlite,okf}
527
585
  --stream Stream NDJSON to stdout
@@ -4,9 +4,9 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "docpull"
7
- version = "4.3.0"
7
+ version = "4.4.0"
8
8
  dynamic = []
9
- description = "Pull documentation from the web and convert to clean markdown"
9
+ description = "Convert public web pages into clean Markdown for AI agents and RAG"
10
10
  readme = {file = "README.md", content-type = "text/markdown"}
11
11
  requires-python = ">=3.10"
12
12
  license = "MIT"
@@ -18,8 +18,22 @@ maintainers = [
18
18
  {name = "Raintree Technology", email = "support@raintree.technology"}
19
19
  ]
20
20
  keywords = [
21
- "python", "markdown", "documentation", "web-scraping", "developer-tools",
22
- "claude", "ai-training-data"
21
+ "python",
22
+ "markdown",
23
+ "documentation",
24
+ "web-extraction",
25
+ "web-scraping",
26
+ "web-crawler",
27
+ "crawler",
28
+ "developer-tools",
29
+ "cli",
30
+ "rag",
31
+ "llm",
32
+ "mcp",
33
+ "ai-agents",
34
+ "context-engineering",
35
+ "claude",
36
+ "ai-training-data",
23
37
  ]
24
38
  classifiers = [
25
39
  # Development Status
@@ -35,9 +49,7 @@ classifiers = [
35
49
  "Environment :: Console",
36
50
 
37
51
  # Topic
38
- "Topic :: Documentation",
39
52
  "Topic :: Internet :: WWW/HTTP :: Indexing/Search",
40
- "Topic :: Software Development :: Documentation",
41
53
  "Topic :: Text Processing :: Markup :: HTML",
42
54
  "Topic :: Text Processing :: Markup :: Markdown",
43
55
  "Topic :: Utilities",
@@ -67,17 +79,17 @@ dependencies = [
67
79
  "defusedxml>=0.7.1",
68
80
  "extruct>=0.15.0",
69
81
  "aiohttp>=3.14.0", # 3.14.0 fixes CVE-2026-34993 and CVE-2026-47265
70
- "idna>=3.15",
82
+ "idna>=3.15", # Security floor for transitive URL/IDNA handling; Dependabot raises as needed
71
83
  "regex>=2024.11.6",
72
84
  "rich>=13.0.0",
73
85
  "pyyaml>=6.0",
74
86
  "pydantic>=2.0",
75
- "urllib3>=2.7.0",
87
+ "urllib3>=2.7.0", # Security floor for transitive HTTP clients; Dependabot raises as needed
76
88
  ]
77
89
 
78
90
  [project.optional-dependencies]
79
91
  proxy = [
80
- "aiohttp-socks>=0.8.0",
92
+ "aiohttp-socks>=0.11.0",
81
93
  ]
82
94
  normalize = [
83
95
  "url-normalize>=1.4.0",
@@ -104,7 +116,7 @@ llm = [
104
116
  "tiktoken>=0.7.0",
105
117
  ]
106
118
  all = [
107
- "aiohttp-socks>=0.8.0",
119
+ "aiohttp-socks>=0.11.0",
108
120
  "url-normalize>=1.4.0",
109
121
  "trafilatura>=1.12.0",
110
122
  "tiktoken>=0.7.0",
@@ -134,12 +146,16 @@ dev = [
134
146
  docpull = "docpull.cli:main"
135
147
 
136
148
  [project.urls]
137
- Homepage = "https://github.com/raintree-technology/docpull"
138
- Documentation = "https://github.com/raintree-technology/docpull#readme"
149
+ Homepage = "https://docpull.raintree.technology"
150
+ Documentation = "https://docpull.raintree.technology"
139
151
  Repository = "https://github.com/raintree-technology/docpull"
140
152
  "Source Code" = "https://github.com/raintree-technology/docpull"
141
153
  "Bug Tracker" = "https://github.com/raintree-technology/docpull/issues"
142
154
  "Releases" = "https://github.com/raintree-technology/docpull/releases"
155
+ "MCP Plugin" = "https://github.com/raintree-technology/docpull/tree/main/plugin"
156
+ "Comparison Guide" = "https://github.com/raintree-technology/docpull/blob/main/docs/alternatives.md"
157
+ "Download Stats" = "https://pepy.tech/project/docpull"
158
+ "Changelog" = "https://github.com/raintree-technology/docpull/blob/main/docs/CHANGELOG.md"
143
159
 
144
160
  [tool.setuptools.packages.find]
145
161
  where = ["src"]
@@ -1,5 +1,5 @@
1
1
  """
2
- docpull - Fetch and convert static/server-rendered documentation to markdown.
2
+ docpull - Fetch and convert static/server-rendered web content to markdown.
3
3
 
4
4
  Usage:
5
5
  from docpull import Fetcher, DocpullConfig, ProfileName
@@ -14,7 +14,7 @@ Usage:
14
14
  print(event)
15
15
  """
16
16
 
17
- __version__ = "4.3.0"
17
+ __version__ = "4.4.0"
18
18
 
19
19
  from .cache import CacheManager, StreamingDeduplicator
20
20
  from .conversion.chunking import Chunk, TokenCounter, chunk_markdown
@@ -237,7 +237,7 @@ def create_benchmark_parser() -> argparse.ArgumentParser:
237
237
  "quick",
238
238
  help="Run a small real-site benchmark and write JSON/Markdown reports",
239
239
  )
240
- quick.add_argument("--target-url", default=DEFAULT_TARGET_URL, help="Docs URL to crawl")
240
+ quick.add_argument("--target-url", default=DEFAULT_TARGET_URL, help="Target URL to crawl")
241
241
  quick.add_argument(
242
242
  "--target-set",
243
243
  choices=TARGET_SET_CHOICES,
@@ -48,7 +48,7 @@ def create_parser() -> argparse.ArgumentParser:
48
48
  """Create argument parser for CLI."""
49
49
  parser = argparse.ArgumentParser(
50
50
  prog="docpull",
51
- description="Fetch and convert static/server-rendered documentation to markdown",
51
+ description="Fetch and convert static/server-rendered web content to markdown",
52
52
  formatter_class=argparse.RawDescriptionHelpFormatter,
53
53
  epilog="""
54
54
  Examples:
@@ -69,7 +69,7 @@ Examples:
69
69
  parser.add_argument(
70
70
  "url",
71
71
  nargs="?",
72
- help="URL to fetch documentation from",
72
+ help="URL to fetch content from",
73
73
  )
74
74
 
75
75
  parser.add_argument(
@@ -87,9 +87,12 @@ Examples:
87
87
  parser.add_argument(
88
88
  "--profile",
89
89
  "-p",
90
- choices=["rag", "mirror", "quick", "llm", "okf"],
90
+ choices=["rag", "mirror", "quick", "llm", "okf", "sec-filing"],
91
91
  default="rag",
92
- help="Preset profile (default: rag). 'llm' streams chunked NDJSON; 'okf' writes an OKF bundle.",
92
+ help=(
93
+ "Preset profile (default: rag). 'llm' streams chunked NDJSON; "
94
+ "'okf' writes an OKF bundle; 'sec-filing' tunes extraction for EDGAR filings."
95
+ ),
93
96
  )
94
97
 
95
98
  parser.add_argument(
@@ -257,7 +260,7 @@ Examples:
257
260
  "--proxy",
258
261
  type=str,
259
262
  metavar="URL",
260
- help="Proxy URL",
263
+ help="HTTP, HTTPS, or SOCKS proxy URL (SOCKS requires docpull[proxy])",
261
264
  )
262
265
  network_group.add_argument(
263
266
  "--user-agent",
@@ -384,6 +387,7 @@ def run_fetcher(args: argparse.Namespace) -> int:
384
387
  "quick": ProfileName.QUICK,
385
388
  "llm": ProfileName.LLM,
386
389
  "okf": ProfileName.OKF,
390
+ "sec-filing": ProfileName.SEC_FILING,
387
391
  }
388
392
  profile = profile_map.get(args.profile, ProfileName.RAG)
389
393
 
@@ -683,6 +687,10 @@ def main(argv: list[str] | None = None) -> int:
683
687
  from .pack_tools import run_pack_cli
684
688
 
685
689
  return run_pack_cli(raw_argv[1:])
690
+ if raw_argv and raw_argv[0] == "evidence-pack":
691
+ from .evidence_pack import run_evidence_pack_cli
692
+
693
+ return run_evidence_pack_cli(raw_argv[1:])
686
694
  if raw_argv and raw_argv[0] == "benchmark":
687
695
  from .benchmark import run_benchmark_cli
688
696