simplibs-extractor 0.1.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 (147) hide show
  1. simplibs_extractor-0.1.0/LICENSE +21 -0
  2. simplibs_extractor-0.1.0/PKG-INFO +605 -0
  3. simplibs_extractor-0.1.0/README.md +566 -0
  4. simplibs_extractor-0.1.0/pyproject.toml +90 -0
  5. simplibs_extractor-0.1.0/setup.cfg +4 -0
  6. simplibs_extractor-0.1.0/src/simplibs/extractor/__init__.py +121 -0
  7. simplibs_extractor-0.1.0/src/simplibs/extractor/__main__.py +12 -0
  8. simplibs_extractor-0.1.0/src/simplibs/extractor/annotations/FileStatus.py +16 -0
  9. simplibs_extractor-0.1.0/src/simplibs/extractor/annotations/OutputFormat.py +25 -0
  10. simplibs_extractor-0.1.0/src/simplibs/extractor/annotations/OverwritePolicy.py +24 -0
  11. simplibs_extractor-0.1.0/src/simplibs/extractor/annotations/StepsSpec.py +26 -0
  12. simplibs_extractor-0.1.0/src/simplibs/extractor/annotations/__init__.py +26 -0
  13. simplibs_extractor-0.1.0/src/simplibs/extractor/annotations/_helpers/__init__.py +15 -0
  14. simplibs_extractor-0.1.0/src/simplibs/extractor/annotations/_helpers/is_output_format.py +36 -0
  15. simplibs_extractor-0.1.0/src/simplibs/extractor/cli/__init__.py +32 -0
  16. simplibs_extractor-0.1.0/src/simplibs/extractor/cli/_helpers/__init__.py +22 -0
  17. simplibs_extractor-0.1.0/src/simplibs/extractor/cli/_helpers/describe_result.py +34 -0
  18. simplibs_extractor-0.1.0/src/simplibs/extractor/cli/_helpers/is_profile_reference.py +34 -0
  19. simplibs_extractor-0.1.0/src/simplibs/extractor/cli/_helpers/print_error.py +25 -0
  20. simplibs_extractor-0.1.0/src/simplibs/extractor/cli/_helpers/profile_options.py +25 -0
  21. simplibs_extractor-0.1.0/src/simplibs/extractor/cli/_helpers/system_options.py +22 -0
  22. simplibs_extractor-0.1.0/src/simplibs/extractor/cli/_validations/__init__.py +22 -0
  23. simplibs_extractor-0.1.0/src/simplibs/extractor/cli/_validations/raise_duplicate_output_error.py +32 -0
  24. simplibs_extractor-0.1.0/src/simplibs/extractor/cli/_validations/raise_list_unknown_system_error.py +34 -0
  25. simplibs_extractor-0.1.0/src/simplibs/extractor/cli/_validations/raise_output_is_file_error.py +30 -0
  26. simplibs_extractor-0.1.0/src/simplibs/extractor/cli/_validations/raise_too_many_positionals_error.py +37 -0
  27. simplibs_extractor-0.1.0/src/simplibs/extractor/cli/_validations/raise_unknown_system_error.py +32 -0
  28. simplibs_extractor-0.1.0/src/simplibs/extractor/cli/build_configs.py +77 -0
  29. simplibs_extractor-0.1.0/src/simplibs/extractor/cli/build_parser.py +68 -0
  30. simplibs_extractor-0.1.0/src/simplibs/extractor/cli/collect_dry_run.py +39 -0
  31. simplibs_extractor-0.1.0/src/simplibs/extractor/cli/collect_profile_keys.py +53 -0
  32. simplibs_extractor-0.1.0/src/simplibs/extractor/cli/collect_source.py +44 -0
  33. simplibs_extractor-0.1.0/src/simplibs/extractor/cli/handle_list_options.py +47 -0
  34. simplibs_extractor-0.1.0/src/simplibs/extractor/cli/normalize_positionals.py +56 -0
  35. simplibs_extractor-0.1.0/src/simplibs/extractor/cli/run_cli.py +101 -0
  36. simplibs_extractor-0.1.0/src/simplibs/extractor/cli/run_init.py +65 -0
  37. simplibs_extractor-0.1.0/src/simplibs/extractor/constants/DEFAULT_MAX_FILE_SIZE.py +18 -0
  38. simplibs_extractor-0.1.0/src/simplibs/extractor/constants/DEFAULT_OUTPUT_NAME.py +14 -0
  39. simplibs_extractor-0.1.0/src/simplibs/extractor/constants/DEFAULT_PROFILE_KEY.py +14 -0
  40. simplibs_extractor-0.1.0/src/simplibs/extractor/constants/DEFAULT_SCRIPT_NAME.py +15 -0
  41. simplibs_extractor-0.1.0/src/simplibs/extractor/constants/GENERATED_MARKER.py +20 -0
  42. simplibs_extractor-0.1.0/src/simplibs/extractor/constants/LANGUAGE_BY_SUFFIX.py +34 -0
  43. simplibs_extractor-0.1.0/src/simplibs/extractor/constants/MARKER_SCAN_LINES.py +18 -0
  44. simplibs_extractor-0.1.0/src/simplibs/extractor/constants/PLUGIN_GROUP.py +20 -0
  45. simplibs_extractor-0.1.0/src/simplibs/extractor/constants/SCRIPT_TEMPLATE.py +32 -0
  46. simplibs_extractor-0.1.0/src/simplibs/extractor/constants/__init__.py +30 -0
  47. simplibs_extractor-0.1.0/src/simplibs/extractor/definitions/Entry.py +26 -0
  48. simplibs_extractor-0.1.0/src/simplibs/extractor/definitions/ExtractionConfig.py +75 -0
  49. simplibs_extractor-0.1.0/src/simplibs/extractor/definitions/ExtractionResult.py +74 -0
  50. simplibs_extractor-0.1.0/src/simplibs/extractor/definitions/FileReport.py +39 -0
  51. simplibs_extractor-0.1.0/src/simplibs/extractor/definitions/Profile.py +95 -0
  52. simplibs_extractor-0.1.0/src/simplibs/extractor/definitions/Resolved.py +28 -0
  53. simplibs_extractor-0.1.0/src/simplibs/extractor/definitions/__init__.py +24 -0
  54. simplibs_extractor-0.1.0/src/simplibs/extractor/definitions/_helpers/__init__.py +16 -0
  55. simplibs_extractor-0.1.0/src/simplibs/extractor/definitions/_helpers/is_key_part.py +31 -0
  56. simplibs_extractor-0.1.0/src/simplibs/extractor/definitions/_helpers/is_suffix_key.py +35 -0
  57. simplibs_extractor-0.1.0/src/simplibs/extractor/definitions/_validations/__init__.py +18 -0
  58. simplibs_extractor-0.1.0/src/simplibs/extractor/definitions/_validations/raise_profile_key_part_error.py +34 -0
  59. simplibs_extractor-0.1.0/src/simplibs/extractor/definitions/_validations/raise_profile_output_name_error.py +33 -0
  60. simplibs_extractor-0.1.0/src/simplibs/extractor/definitions/_validations/raise_profile_steps_key_error.py +33 -0
  61. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/__init__.py +90 -0
  62. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/_common/__init__.py +15 -0
  63. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/_common/describe_failure.py +39 -0
  64. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/_helpers/RunLog.py +57 -0
  65. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/_helpers/__init__.py +18 -0
  66. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/_helpers/build_title.py +28 -0
  67. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/_helpers/process_source.py +59 -0
  68. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/extract.py +119 -0
  69. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/extract_many.py +60 -0
  70. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/failure_result.py +47 -0
  71. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/read/__init__.py +16 -0
  72. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/read/is_generated_file.py +45 -0
  73. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/read/read_source.py +93 -0
  74. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/render/RenderContext.py +34 -0
  75. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/render/__init__.py +31 -0
  76. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/render/_helpers/__init__.py +14 -0
  77. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/render/_helpers/format_tree_lines.py +35 -0
  78. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/render/build_tree.py +36 -0
  79. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/render/register_builtin_renderers.py +26 -0
  80. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/render/render_markdown.py +84 -0
  81. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/render/render_plain.py +57 -0
  82. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/report/__init__.py +16 -0
  83. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/report/_validations/__init__.py +14 -0
  84. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/report/_validations/raise_chars_per_token_error.py +29 -0
  85. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/report/estimate_tokens.py +47 -0
  86. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/report/format_summary.py +67 -0
  87. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/select/__init__.py +24 -0
  88. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/select/_validations/__init__.py +18 -0
  89. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/select/_validations/raise_missing_source_error.py +26 -0
  90. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/select/_validations/raise_source_not_found_error.py +37 -0
  91. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/select/_validations/raise_unknown_parameter_error.py +35 -0
  92. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/select/all_of.py +32 -0
  93. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/select/any_of.py +29 -0
  94. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/select/resolve_config.py +85 -0
  95. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/select/resolve_ignore_names.py +36 -0
  96. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/select/resolve_profile.py +38 -0
  97. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/select/select_sources.py +85 -0
  98. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/transform/__init__.py +16 -0
  99. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/transform/_validations/__init__.py +14 -0
  100. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/transform/_validations/raise_step_result_error.py +42 -0
  101. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/transform/apply_steps.py +57 -0
  102. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/transform/steps_for.py +40 -0
  103. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/write/__init__.py +14 -0
  104. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/write/_validations/__init__.py +16 -0
  105. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/write/_validations/raise_target_exists_error.py +34 -0
  106. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/write/_validations/raise_target_is_dir_error.py +34 -0
  107. simplibs_extractor-0.1.0/src/simplibs/extractor/logic/write/write_output.py +77 -0
  108. simplibs_extractor-0.1.0/src/simplibs/extractor/plugins/__init__.py +14 -0
  109. simplibs_extractor-0.1.0/src/simplibs/extractor/plugins/load_plugins.py +64 -0
  110. simplibs_extractor-0.1.0/src/simplibs/extractor/py.typed +1 -0
  111. simplibs_extractor-0.1.0/src/simplibs/extractor/registry/__init__.py +37 -0
  112. simplibs_extractor-0.1.0/src/simplibs/extractor/registry/_helpers/GENERIC_ALL.py +24 -0
  113. simplibs_extractor-0.1.0/src/simplibs/extractor/registry/_helpers/PROFILE_REGISTRY.py +21 -0
  114. simplibs_extractor-0.1.0/src/simplibs/extractor/registry/_helpers/RENDERER_REGISTRY.py +21 -0
  115. simplibs_extractor-0.1.0/src/simplibs/extractor/registry/_helpers/__init__.py +18 -0
  116. simplibs_extractor-0.1.0/src/simplibs/extractor/registry/_validations/__init__.py +24 -0
  117. simplibs_extractor-0.1.0/src/simplibs/extractor/registry/_validations/raise_missing_system_error.py +33 -0
  118. simplibs_extractor-0.1.0/src/simplibs/extractor/registry/_validations/raise_no_profile_given_error.py +33 -0
  119. simplibs_extractor-0.1.0/src/simplibs/extractor/registry/_validations/raise_profile_exists_error.py +33 -0
  120. simplibs_extractor-0.1.0/src/simplibs/extractor/registry/_validations/raise_profile_not_found_error.py +45 -0
  121. simplibs_extractor-0.1.0/src/simplibs/extractor/registry/_validations/raise_renderer_exists_error.py +30 -0
  122. simplibs_extractor-0.1.0/src/simplibs/extractor/registry/_validations/raise_unknown_renderer_error.py +37 -0
  123. simplibs_extractor-0.1.0/src/simplibs/extractor/registry/expand_profile_keys.py +60 -0
  124. simplibs_extractor-0.1.0/src/simplibs/extractor/registry/get_profile.py +39 -0
  125. simplibs_extractor-0.1.0/src/simplibs/extractor/registry/get_renderer.py +40 -0
  126. simplibs_extractor-0.1.0/src/simplibs/extractor/registry/list_formats.py +20 -0
  127. simplibs_extractor-0.1.0/src/simplibs/extractor/registry/list_profiles.py +32 -0
  128. simplibs_extractor-0.1.0/src/simplibs/extractor/registry/list_systems.py +20 -0
  129. simplibs_extractor-0.1.0/src/simplibs/extractor/registry/register_profile.py +46 -0
  130. simplibs_extractor-0.1.0/src/simplibs/extractor/registry/register_renderer.py +47 -0
  131. simplibs_extractor-0.1.0/src/simplibs/extractor/scripts/__init__.py +18 -0
  132. simplibs_extractor-0.1.0/src/simplibs/extractor/scripts/_helpers/__init__.py +14 -0
  133. simplibs_extractor-0.1.0/src/simplibs/extractor/scripts/_helpers/print_result.py +32 -0
  134. simplibs_extractor-0.1.0/src/simplibs/extractor/scripts/_validations/__init__.py +16 -0
  135. simplibs_extractor-0.1.0/src/simplibs/extractor/scripts/_validations/raise_invalid_output_name_error.py +30 -0
  136. simplibs_extractor-0.1.0/src/simplibs/extractor/scripts/_validations/raise_not_python_script_error.py +30 -0
  137. simplibs_extractor-0.1.0/src/simplibs/extractor/scripts/extract_here.py +93 -0
  138. simplibs_extractor-0.1.0/src/simplibs/extractor/scripts/generate_local_script.py +65 -0
  139. simplibs_extractor-0.1.0/src/simplibs/extractor/scripts/render_script.py +26 -0
  140. simplibs_extractor-0.1.0/src/simplibs_extractor.egg-info/PKG-INFO +605 -0
  141. simplibs_extractor-0.1.0/src/simplibs_extractor.egg-info/SOURCES.txt +145 -0
  142. simplibs_extractor-0.1.0/src/simplibs_extractor.egg-info/dependency_links.txt +1 -0
  143. simplibs_extractor-0.1.0/src/simplibs_extractor.egg-info/entry_points.txt +2 -0
  144. simplibs_extractor-0.1.0/src/simplibs_extractor.egg-info/requires.txt +14 -0
  145. simplibs_extractor-0.1.0/src/simplibs_extractor.egg-info/top_level.txt +1 -0
  146. simplibs_extractor-0.1.0/tests/test_architecture.py +133 -0
  147. simplibs_extractor-0.1.0/tests/test_public_api.py +76 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Dalibor Sova (Sudip2708)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,605 @@
1
+ Metadata-Version: 2.4
2
+ Name: simplibs-extractor
3
+ Version: 0.1.0
4
+ Summary: Gathers the text files of a folder into one document: select, read, transform with steps, render as Markdown or plain text, write atomically and report โ€” as a Python function, a command and a two-line script, with language knowledge brought by add-ons.
5
+ Author-email: "Dalibor Sova (Sudip2708)" <daliborsova@seznam.cz>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/simplibs/simplibs-extractor
8
+ Project-URL: Repository, https://github.com/simplibs/simplibs-extractor
9
+ Project-URL: Issues, https://github.com/simplibs/simplibs-extractor/issues
10
+ Project-URL: Changelog, https://github.com/simplibs/simplibs-extractor/blob/main/CHANGELOG.md
11
+ Keywords: extractor,code-extractor,project-to-text,files,folder,markdown,llm,context,cli,command-line,profiles,plugins,simplibs,simplibs-extractor,developer-tools
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
20
+ Classifier: Topic :: Utilities
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.11
23
+ Description-Content-Type: text/markdown
24
+ License-File: LICENSE
25
+ Requires-Dist: simplibs-exception>=1.1.4
26
+ Requires-Dist: simplibs-validate>=0.3.2
27
+ Requires-Dist: simplibs-path>=0.1.1
28
+ Requires-Dist: simplibs-file>=0.1.0
29
+ Requires-Dist: simplibs-rules>=0.4.0
30
+ Requires-Dist: simplibs-actions>=0.2.2
31
+ Requires-Dist: simplibs-markdown>=0.1.0
32
+ Requires-Dist: simplibs-cli>=0.1.0
33
+ Requires-Dist: simplibs-logger>=0.1.0
34
+ Requires-Dist: simplibs-style>=0.1.0
35
+ Provides-Extra: dev
36
+ Requires-Dist: pytest>=9.0; extra == "dev"
37
+ Requires-Dist: pytest-cov>=5.0; extra == "dev"
38
+ Dynamic: license-file
39
+
40
+ # ๐Ÿ—‚๏ธ `simplibs-extractor`
41
+
42
+ [![PyPI](https://img.shields.io/pypi/v/simplibs-extractor)](https://pypi.org/project/simplibs-extractor/)
43
+ [![Python](https://img.shields.io/badge/python-3.11%2B-blue)](https://www.python.org/downloads/)
44
+ [![Licence](https://img.shields.io/badge/licence-MIT-green)](https://github.com/simplibs/simplibs-extractor/blob/main/LICENSE)
45
+ [![tests](https://github.com/simplibs/simplibs-extractor/actions/workflows/tests.yml/badge.svg)](https://github.com/simplibs/simplibs-extractor/actions)
46
+
47
+ **Many files in, one document out โ€” and a clear report of what happened to every one of them.**
48
+
49
+ A Python library, a command and a two-line script that gather the **text files of a folder into one
50
+ document**: pick the files, read them, clean their text with steps, render a Markdown (or plain)
51
+ document, write it atomically and tell you what was taken, what was left out and why. The core knows
52
+ **no language**: what a project looks like โ€” which files, how to clean them โ€” comes from **profiles**,
53
+ which add-on packages bring (`simplibs-code-extractor` is the first). On its own the extractor takes
54
+ every text file and changes nothing.
55
+
56
+ ```bash
57
+ simplibs-extractor generic/all my_project
58
+ ```
59
+
60
+ ```
61
+ Extraction: generic/all
62
+ source : /home/me/my_project
63
+ target : /home/me/my_project/extraction.md (written)
64
+ files : 4 ok, 1 skipped, 0 errors
65
+ size : 133 B in -> 502 B out (~115 tokens)
66
+ skipped: logo.png (not a text file)
67
+ ```
68
+
69
+ ```python
70
+ from simplibs.extractor import extract
71
+
72
+ result = extract(source="my_project") # the same extraction from Python
73
+ print(result.success, result.target) # -> True /home/me/my_project/extraction.md
74
+ ```
75
+
76
+ > The extractor writes a **report, not a stream of prints**: every run returns an `ExtractionResult`
77
+ > that the command, a script or your own code turns into text, an exit code or a log. The only
78
+ > places that print are the command line and `extract_here` (for people running a script).
79
+
80
+ ---
81
+
82
+ ## ๐Ÿงญ The Core Philosophy
83
+
84
+ Collecting a project into one file is easy to do badly: the output is read again on the next run, a
85
+ binary or a 50 MB lock file drowns everything, one unreadable file stops the whole job, a dry run
86
+ says "fine" and the real run fails. `simplibs-extractor` is built around those traps:
87
+
88
+ * **Six phases, each one a function.** `Select โ†’ Read โ†’ Transform โ†’ Render โ†’ Write โ†’ Report`. The
89
+ run only wires them together, so each can be understood โ€” and tested โ€” alone.
90
+ * **The core knows no language.** Language knowledge lives in **profiles** (rules on paths + steps on
91
+ text) registered by add-ons. A new kind of project is a new profile, not a change in the core.
92
+ * **One bad file never stops a run.** A file that cannot be read or transformed is a line in the
93
+ report with its reason; the run continues. A configuration that cannot run fails **before any file
94
+ is touched**.
95
+ * **`skipped` is not `error`.** A binary file, a file over the size limit, an earlier output โ€” left
96
+ out on purpose, not a problem. An `error` file was meant to be included and could not be; it makes
97
+ the run unsuccessful.
98
+ * **The output is never its own input.** The target and the files you list are removed from the scan,
99
+ and every output carries a **marker** in its first lines, so even `extraction (1).md` or a backup
100
+ is recognised and skipped on the next run.
101
+ * **A dry run tells the truth.** It makes exactly the decisions a real run makes โ€” including refusing
102
+ an existing output under the `error` policy โ€” and writes nothing.
103
+ * **Rules judge the relative path.** A project that merely lies inside a folder called `tests` is not
104
+ dropped as "a test". What a rule can see is the path **inside** the source, so a profile works the
105
+ same wherever the project is.
106
+ * **A config can only narrow.** The `include` you pass is combined with the profile's by AND, the
107
+ `exclude` by OR; a config never widens what a profile selected.
108
+ * **Safe by default.** One megabyte per file, housekeeping folders (`.git`, `node_modules`, virtual
109
+ environments, ...) and half-written drafts are never scanned, the output is written atomically
110
+ (draft and swap), and only the **name** of the source folder โ€” never its absolute path โ€” goes into
111
+ the output.
112
+ * **Every error is a card.** A wrong call is a structured card from
113
+ [`simplibs-exception`](https://pypi.org/project/simplibs-exception/) with a name starting with
114
+ `EXTRACTOR_`: what, why, how to fix. The library defines no exception classes of its own.
115
+ * **Layers that only look left.** `annotations` โ† `constants` โ† `definitions` โ†
116
+ `registry` โ† `logic` โ† `plugins` โ† `scripts` โ† `cli`.
117
+
118
+ ---
119
+
120
+ ## ๐Ÿ“ฆ Installation
121
+
122
+ ```bash
123
+ pip install simplibs-extractor
124
+ ```
125
+
126
+ Requires Python 3.11+. The command is available as `simplibs-extractor` and as
127
+ `python -m simplibs.extractor`. Runtime dependencies, all from the same family:
128
+ [`simplibs-path`](https://pypi.org/project/simplibs-path/) and
129
+ [`simplibs-file`](https://pypi.org/project/simplibs-file/) (scanning, reading, atomic writing),
130
+ [`simplibs-rules`](https://pypi.org/project/simplibs-rules/) and
131
+ [`simplibs-actions`](https://pypi.org/project/simplibs-actions/) (the rules and steps of a profile),
132
+ [`simplibs-markdown`](https://pypi.org/project/simplibs-markdown/) (the Markdown output),
133
+ [`simplibs-cli`](https://pypi.org/project/simplibs-cli/),
134
+ [`simplibs-logger`](https://pypi.org/project/simplibs-logger/) and
135
+ [`simplibs-style`](https://pypi.org/project/simplibs-style/) (the command line),
136
+ [`simplibs-validate`](https://pypi.org/project/simplibs-validate/) and
137
+ [`simplibs-exception`](https://pypi.org/project/simplibs-exception/).
138
+
139
+ A language **add-on** is installed the same way โ€” `pip install simplibs-code-extractor` โ€” and its
140
+ profiles show up on their own in `simplibs-extractor --list-systems`. Nothing needs to be imported. From
141
+ Python, call `load_plugins()` once before you look a profile up โ€” the command does that for you.
142
+
143
+ The text steps used in the examples below come from
144
+ [`simplibs-text`](https://pypi.org/project/simplibs-text/), which is **not** a dependency of the extractor:
145
+ `pip install simplibs-text` when you want them.
146
+
147
+ ---
148
+
149
+ ## ๐Ÿš€ Quick Start in 60 Seconds
150
+
151
+ ### Level 1: The command line
152
+
153
+ ```bash
154
+ simplibs-extractor generic/all my_project # every text file, unchanged -> my_project/extraction.md
155
+ simplibs-extractor generic/all my_project --dry-run # plan and report, write nothing
156
+ simplibs-extractor generic/all my_project -o out --format plain --no-tree
157
+ simplibs-extractor --list-systems # what is installed
158
+ simplibs-extractor --list-profiles python # the profiles of one system (an add-on's)
159
+ simplibs-extractor python code-clean,data-all my_project # two profiles -> two files, one run
160
+ simplibs-extractor # in a terminal: asks for what is missing
161
+ ```
162
+
163
+ The result of the first command is a Markdown document (the paths are shown as inline code, a
164
+ fenced block per file, the fence always longer than any backticks inside the code):
165
+
166
+ ````markdown
167
+ <!-- simplibs-extractor:generated -->
168
+
169
+ # Extraction: my_project
170
+
171
+ Profile: `generic/all`
172
+
173
+ ```text
174
+ โ”œโ”€โ”€ src/
175
+ โ”‚ โ”œโ”€โ”€ app.py
176
+ โ”‚ โ””โ”€โ”€ config.py
177
+ โ”œโ”€โ”€ tests/
178
+ โ”‚ โ””โ”€โ”€ test_app.py
179
+ โ””โ”€โ”€ README.md
180
+ ```
181
+
182
+ ## `README.md`
183
+
184
+ ```markdown
185
+ # My project
186
+
187
+ A small demo.
188
+ ```
189
+
190
+ ## `src/app.py`
191
+
192
+ ```python
193
+ import os
194
+
195
+ def main():
196
+ # greet
197
+ print("hi") # inline
198
+ ```
199
+ ````
200
+
201
+ The command ends with an **exit code**: `0` everything succeeded, `1` some extraction failed or
202
+ selected nothing, `2` the command was refused before anything ran (a typo in a profile, a missing
203
+ source with nobody to ask).
204
+
205
+ ### Level 2: From Python
206
+
207
+ ```python
208
+ from simplibs.extractor import extract, format_summary
209
+
210
+ result = extract(source="my_project", dry_run=True) # everything is a keyword; a typo is refused: extract(sourse=...)
211
+
212
+ result.success # -> True
213
+ [f.relative for f in result.ok_files] # -> ['README.md', 'src/app.py', 'src/config.py', 'tests/test_app.py']
214
+ [(f.relative, f.reason) for f in result.skipped_files] # -> [('logo.png', 'not a text file')]
215
+ result.tokens_estimate # -> 115 a rough guess: characters / 4
216
+ print(format_summary(result)) # the same text the command prints
217
+ ```
218
+
219
+ A ready `ExtractionConfig` can be reused with one change โ€” keywords change a copy of it:
220
+
221
+ ```python
222
+ from simplibs.extractor import ExtractionConfig, extract
223
+
224
+ config = ExtractionConfig(source="my_project", overwrite="unique", max_file_size=200_000)
225
+ extract(config) # my_project/extraction.md
226
+ extract(config, dry_run=True, add_tree=False) # the same, changed for this call only
227
+ ```
228
+
229
+ ### Level 3: Your own profile
230
+
231
+ A **profile** is a name for "which files, how to clean them, what the output is called": rules on the
232
+ relative path plus **steps** that transform each file's text (any `Action` โ€” the text steps of
233
+ [`simplibs-text`](https://pypi.org/project/simplibs-text/) fit directly).
234
+
235
+ ```python
236
+ import re
237
+ from simplibs.extractor import Profile, extract, register_profile
238
+ from simplibs.path.rules import HasSuffix, ParentNameIs
239
+ from simplibs.text.actions import RemoveMatches
240
+ from simplibs.text.presets import COMPACT
241
+
242
+ register_profile(Profile(
243
+ system="python", name="code-clean",
244
+ description="Python sources without comments and blank lines.",
245
+ include=HasSuffix(".py"), # a rule on the path inside the source
246
+ exclude=ParentNameIs("tests"), # a file directly in a folder named tests
247
+ steps=RemoveMatches(pattern=re.compile(r"#.*")) >> COMPACT, # one Action for every file
248
+ output_name="python_clean.md",
249
+ ))
250
+
251
+ result = extract(source="my_project", profile="python/code-clean")
252
+ [(f.relative, f.bytes_in, f.bytes_out) for f in result.ok_files]
253
+ # -> [('src/app.py', 62, 38), ('src/config.py', 10, 10)] what the profile saved, file by file
254
+ ```
255
+
256
+ `steps` may also be a mapping `suffix -> Action` (`{".py": clean_python, ".md": keep_text, "*": other}`),
257
+ because a step sees only the text, never the path โ€” a profile that cleans two languages needs two
258
+ pipelines. Rules may be combined with `&`, `|` and `~`; `include` and `exclude` of a single `extract`
259
+ call narrow the profile's.
260
+
261
+ ### Level 4: Scripts, batches, formats and add-ons
262
+
263
+ ```python
264
+ # extract_here.py โ€” dropped into a project folder (or: simplibs-extractor init my_project)
265
+ from simplibs.extractor import extract_here
266
+
267
+ extract_here(__file__, profile="python/code-clean") # source = this folder, output next to the script,
268
+ # the script itself is never part of the output
269
+ ```
270
+
271
+ ```python
272
+ from simplibs.extractor import GENERATED_MARKER, ExtractionConfig, extract, extract_many, register_renderer
273
+
274
+ configs = [ExtractionConfig(source="my_project", profile=key) for key in ("python/code-clean", "generic/all")]
275
+ results = extract_many(configs) # a broken config is a result with `fatal`, the others still run
276
+
277
+ def render_names(entries, context): # a new output format is a function: entries in, text out
278
+ names = "\n".join(entry.relative for entry in entries)
279
+ return f"# {GENERATED_MARKER}\n{names}\n" # the marker keeps the output out of the next run
280
+
281
+ register_renderer("names", render_names)
282
+ extract(source="my_project", format="names", target="my_project/files.txt")
283
+ ```
284
+
285
+ An **add-on** announces itself in its own `pyproject.toml`, and the extractor finds it without
286
+ importing it by name:
287
+
288
+ ```toml
289
+ [project.entry-points."simplibs.extractor.plugins"]
290
+ code = "simplibs.code_extractor:register_all" # a function with no arguments that registers profiles
291
+ ```
292
+
293
+ The command loads add-ons itself; from Python, `load_plugins()` does it (`extract()` never loads add-ons on its own).
294
+
295
+ ---
296
+
297
+ ## ๐Ÿ”ฌ The six phases
298
+
299
+ ```
300
+ Select โ”€โ”€โ–บ Read โ”€โ”€โ–บ Transform โ”€โ”€โ–บ Render โ”€โ”€โ–บ Write โ”€โ”€โ–บ Report
301
+ which text or steps of one atomic, an ExtractionResult,
302
+ files skipped? the profile document policy a summary, an exit code
303
+ ```
304
+
305
+ | Phase | What it does | Never does |
306
+ |---------------|-------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------|
307
+ | **Select** | Scans the folder (without ignored names), drops the target and `skip_paths`, applies `include` (AND) and `exclude` (OR) to the **relative** path | reads a file's content |
308
+ | **Read** | Per file, cheapest check first: binary โ†’ generated by us โ†’ too large โ†’ read; reports `skipped` or `error` and never raises for a file | decides which files are taken |
309
+ | **Transform** | Runs the profile's steps on the text (the action of the file's suffix, or the one for all); a step that fails marks **that file** `error` | sees a path, only the text |
310
+ | **Render** | Turns the entries into one text through the renderer of the format; writes the marker, the title and the tree | touches the disk |
311
+ | **Write** | `overwrite`, `unique`, `backup` or `error`; a dry run decides the same and stops; the write itself is atomic | writes in a dry run |
312
+ | **Report** | Builds the `ExtractionResult`: per-file reports, sizes, a token estimate, success | prints (the command and scripts do) |
313
+
314
+ Two kinds of failure, two behaviours. A **configuration that cannot run** (a missing source, an
315
+ unknown profile or format, an unknown parameter) raises a card before any file is read. A **file that
316
+ cannot be used** is a line in the result. A **write that fails** raises, because then there is no
317
+ output at all. An extraction that selected nothing writes nothing and counts as **unsuccessful**: it is
318
+ almost always a wrong filter or a wrong folder, and a file with just a title would hide it.
319
+
320
+ โžก๏ธ [README_EXTRACTOR_RUN](https://github.com/simplibs/simplibs-extractor/blob/main/docs/logic/README_EXTRACTOR_RUN.md) ยท [SELECT](https://github.com/simplibs/simplibs-extractor/blob/main/docs/logic/README_EXTRACTOR_SELECT.md) ยท [READ](https://github.com/simplibs/simplibs-extractor/blob/main/docs/logic/README_EXTRACTOR_READ.md) ยท [TRANSFORM](https://github.com/simplibs/simplibs-extractor/blob/main/docs/logic/README_EXTRACTOR_TRANSFORM.md) ยท [RENDER](https://github.com/simplibs/simplibs-extractor/blob/main/docs/logic/README_EXTRACTOR_RENDER.md) ยท [WRITE](https://github.com/simplibs/simplibs-extractor/blob/main/docs/logic/README_EXTRACTOR_WRITE.md) ยท [REPORT](https://github.com/simplibs/simplibs-extractor/blob/main/docs/logic/README_EXTRACTOR_REPORT.md)
321
+
322
+ ---
323
+
324
+ ## ๐Ÿ› ๏ธ Architecture & Package Structure
325
+
326
+ ```
327
+ src/simplibs/extractor/
328
+ โ”œโ”€โ”€ annotations/ โ—„โ”€โ”€ FileStatus, OutputFormat, OverwritePolicy, StepsSpec
329
+ โ”œโ”€โ”€ constants/ โ—„โ”€โ”€ DEFAULT_*, GENERATED_MARKER, LANGUAGE_BY_SUFFIX, PLUGIN_GROUP, SCRIPT_TEMPLATE, ...
330
+ โ”œโ”€โ”€ definitions/ โ—„โ”€โ”€ Frozen dataclasses: ExtractionConfig, Profile, ExtractionResult, FileReport, Entry
331
+ โ”œโ”€โ”€ registry/ โ—„โ”€โ”€ Profiles and renderers by name: register_*, get_*, list_*, expand_profile_keys
332
+ โ”œโ”€โ”€ logic/ โ—„โ”€โ”€ The six phases and the run
333
+ โ”‚ โ”œโ”€โ”€ select/ read/ transform/ render/ write/ report/
334
+ โ”‚ โ”œโ”€โ”€ extract.py extract_many.py failure_result.py โ—„โ”€โ”€ The run: the phases put together
335
+ โ”‚ โ””โ”€โ”€ _common/ _helpers/ โ—„โ”€โ”€ Private helpers of the phases and of the run
336
+ โ”œโ”€โ”€ plugins/ โ—„โ”€โ”€ load_plugins: add-ons found through entry points
337
+ โ”œโ”€โ”€ scripts/ โ—„โ”€โ”€ extract_here, generate_local_script
338
+ โ”œโ”€โ”€ cli/ โ—„โ”€โ”€ The command (not imported by the package root)
339
+ โ”œโ”€โ”€ __main__.py โ—„โ”€โ”€ python -m simplibs.extractor
340
+ โ””โ”€โ”€ py.typed
341
+ ```
342
+
343
+ Folders named `_validations/`, `_helpers/` and `_common/` are private: they hold the functions that raise the
344
+ library's error cards and the small helpers of the code next to them.
345
+
346
+ Everything a program needs is importable from `simplibs.extractor`. The command line is the one
347
+ exception: `from simplibs.extractor.cli import run_cli` โ€” so a program that only calls `extract()` does
348
+ not load the prompt and logger libraries it never uses. **Importing the package has two side effects**:
349
+ the profile `generic/all` and the formats `markdown` and `plain` are registered. Nothing else happens
350
+ until a function is called; add-ons are looked up only by `load_plugins()` (which the command calls).
351
+
352
+ ---
353
+
354
+ ## ๐Ÿƒ Running an extraction
355
+
356
+ | Function | Description / Parameters |
357
+ |-----------------|-----------------------------------------------------------------------------------------------------------------------------|
358
+ | `extract` | One extraction through all six phases (`config=None`, keyword-only: `logger=None`, and any field of `ExtractionConfig` as a keyword). Returns an `ExtractionResult`. |
359
+ | `extract_many` | Several extractions in a row; a run that cannot start or write appears as a result with `fatal` set and does not stop the others (`configs`, keyword-only: `logger=None`, `stop_on_error=False`). |
360
+
361
+ Progress goes to the `logger` you pass โ€” a `SimpleLogger`, a standard `logging.Logger` or nothing โ€”
362
+ and never to the screen.
363
+
364
+ โžก๏ธ [README_EXTRACTOR_RUN](https://github.com/simplibs/simplibs-extractor/blob/main/docs/logic/README_EXTRACTOR_RUN.md)
365
+
366
+ ---
367
+
368
+ ## ๐Ÿ“œ `scripts/` โ€” Two lines in a folder
369
+
370
+ | Function | Description / Parameters |
371
+ |-------------------------|-----------------------------------------------------------------------------------------------------------------------|
372
+ | `extract_here` | Extracts the folder a script lies in into a file next to it; the script itself is left out (`anchor`, `profile=None`, keyword-only: `output_name=None`, `quiet=False`, `logger=None`, any `ExtractionConfig` field). Prints the summary unless `quiet`. |
373
+ | `generate_local_script` | Writes a ready-to-run `extract_here.py` (`path`, `profile=None`, keyword-only: `overwrite=False`); only the profile's **key** is stored. |
374
+
375
+ โžก๏ธ [README_EXTRACTOR_SCRIPTS](https://github.com/simplibs/simplibs-extractor/blob/main/docs/README_EXTRACTOR_SCRIPTS.md)
376
+
377
+ ---
378
+
379
+ ## ๐Ÿงฉ Profiles, registry and add-ons
380
+
381
+ | Function | Description / Parameters |
382
+ |------------------------|-------------------------------------------------------------------------------------------------------------------|
383
+ | `register_profile` | Adds a profile under its `system/name` key and returns it (`profile`, keyword-only: `replace_existing=False`). |
384
+ | `get_profile` | The profile of a key, or a `NotFoundError` card that lists the registered keys (`key`). |
385
+ | `list_profiles` | The registered profiles, sorted by key; a new list (`system=None`). |
386
+ | `list_systems` | The systems that have at least one profile, sorted. |
387
+ | `expand_profile_keys` | Turns what a person typed into full keys: `("python", "a,b")` โ†’ `["python/a", "python/b"]` (`system`, `names`). |
388
+ | `register_renderer` | Adds an output format: a function `(entries, context) -> str` (`name`, `func`, keyword-only: `replace_existing=False`). |
389
+ | `get_renderer` | The renderer of a format, or a card listing the known formats (`name`). |
390
+ | `list_formats` | The registered output formats, sorted. |
391
+ | `load_plugins` | Lets every installed add-on register its profiles; returns `(loaded, failed)` and never lets one add-on stop another (keyword-only: `group`, `entry_points`). |
392
+
393
+ โžก๏ธ [README_EXTRACTOR_REGISTRY](https://github.com/simplibs/simplibs-extractor/blob/main/docs/README_EXTRACTOR_REGISTRY.md) ยท [README_EXTRACTOR_PLUGINS](https://github.com/simplibs/simplibs-extractor/blob/main/docs/README_EXTRACTOR_PLUGINS.md) (writing an add-on)
394
+
395
+ ---
396
+
397
+ ## ๐Ÿ“ฆ `definitions/` โ€” Data objects
398
+
399
+ | Class | Description |
400
+ |--------------------|---------------------------------------------------------------------------------------------------------------------------|
401
+ | `ExtractionConfig` | Everything one run needs: `source`, `target`, `profile`, `include`, `exclude`, `ignore_names`, `recursive`, `format`, `overwrite`, `dry_run`, `max_file_size`, `add_tree`, `title`, `skip_paths`. Frozen and validated. |
402
+ | `Profile` | A named set of selection rules and steps: `system`, `name`, `description`, `include`, `exclude`, `steps`, `output_name`, `format`; `key` is `"system/name"`. Frozen and validated when built. |
403
+ | `ExtractionResult` | The outcome of one run: `config`, `profile_key`, `target`, `written`, `dry_run`, `files`, `bytes_in`, `bytes_out`, `tokens_estimate`, `duration`, `fatal`; properties `ok_files`, `skipped_files`, `error_files`, `success`. |
404
+ | `FileReport` | What happened to one file: `path`, `relative`, `status` (`ok`, `skipped`, `error`), `reason`, `bytes_in`, `bytes_out`. |
405
+ | `Entry` | One file ready to be rendered: `relative`, `suffix`, `text`. |
406
+
407
+ `bytes_out` of an `ExtractionResult` is the size of the whole **rendered output** (title, tree and fences
408
+ included); the `bytes_out` of a `FileReport` is the size of that file's text **after the steps** โ€” so the
409
+ report shows what a profile saves, file by file. `RenderContext`, the other object a renderer receives, is
410
+ described with the [output formats](#-output-formats).
411
+
412
+ โžก๏ธ [README_EXTRACTOR_DEFINITIONS](https://github.com/simplibs/simplibs-extractor/blob/main/docs/README_EXTRACTOR_DEFINITIONS.md)
413
+
414
+ ---
415
+
416
+ ## ๐Ÿ–จ๏ธ Output formats
417
+
418
+ Two formats are built in, registered through the same door as any later one (the core never branches
419
+ on a format name). Both start with the **marker** `simplibs-extractor:generated` in their first
420
+ lines, which is how an output is recognised โ€” whatever it is called โ€” when it lies in a scanned folder.
421
+ **A renderer of your own should write the marker too** (`GENERATED_MARKER`, in its first three lines), or its output would be read back as input on the next run.
422
+
423
+ | Format | What it looks like |
424
+ |------------|-------------------------------------------------------------------------------------------------------------------------------------|
425
+ | `markdown` | A comment with the marker, a title, `Profile: ...`, an optional tree, then `## `path`` and a fenced block per file with the language of its suffix. The default. |
426
+ | `plain` | `# simplibs-extractor:generated`, `# <title>`, `# profile: ...`, an optional `# TREE`, then `# FILE: path` followed by the text โ€” the layout of the old code extractor. |
427
+
428
+ | Helper | Description |
429
+ |------------------|---------------------------------------------------------------------------------------------------|
430
+ | `RenderContext` | What a renderer receives besides the entries: `title`, `source`, `profile_key`, `add_tree` (exported from the package root, like `Entry`). |
431
+ | `build_tree` | Draws a directory tree from relative paths, folders first (`from simplibs.extractor.logic import build_tree`). |
432
+ | `estimate_tokens` | A **rough** token count: characters / `chars_per_token` (default 4), rounded up. No model name, no tokenizer. |
433
+ | `format_summary` | The readable text of an `ExtractionResult`: counts, sizes, errors first (`result`, keyword-only: `max_listed=10`). |
434
+
435
+ โžก๏ธ [README_EXTRACTOR_RENDER](https://github.com/simplibs/simplibs-extractor/blob/main/docs/logic/README_EXTRACTOR_RENDER.md) ยท [README_EXTRACTOR_REPORT](https://github.com/simplibs/simplibs-extractor/blob/main/docs/logic/README_EXTRACTOR_REPORT.md)
436
+
437
+ ---
438
+
439
+ ## โŒจ๏ธ The command line
440
+
441
+ ```
442
+ simplibs-extractor [SYSTEM] [PROFILES] [SOURCE] [-o FOLDER] [--format NAME] [--overwrite POLICY]
443
+ [--no-tree] [--dry-run] [-q] [-v | -vv] [--no-color]
444
+ simplibs-extractor --list-systems | --list-profiles SYSTEM
445
+ simplibs-extractor init [DIRECTORY] [--profile KEY] [--force]
446
+ ```
447
+
448
+ | Option | Meaning |
449
+ |-----------------------|---------------------------------------------------------------------------------------------------------------|
450
+ | `SYSTEM PROFILES` | `python code-clean,data-all`, or full keys: `python/code-clean`, `generic/all,python/data-all`. Several profiles are several runs and several files. |
451
+ | `SOURCE` | The folder to extract from. A first word that is a folder and not a known profile is taken as the source: `simplibs-extractor ./src`. |
452
+ | `-o`, `--output` | The output **folder** (default: the source). The file name always comes from the profile. |
453
+ | `--format` | An output format; the choices are the registered ones. Default: the profile's. |
454
+ | `--overwrite` | `overwrite` (default), `unique`, `backup` or `error`. |
455
+ | `--no-tree` | Leave the directory tree out of the output. |
456
+ | `--dry-run` | Plan and report, write nothing. |
457
+ | `-q`, `-v`, `-vv` | Only errors / more detail / everything; `--no-color` for plain text. |
458
+ | `--list-systems`, `--list-profiles SYSTEM` | Answered from the registry; nothing is extracted. An unknown system is an error, not an empty list. |
459
+ | `init` | Writes a local `extract_here.py` (refuses to replace one without `--force`). |
460
+
461
+ What is missing is asked for in a terminal (the system, then the profiles, then the source; a dry-run
462
+ question only to a person who is already being asked). Without a terminal the same situation is a
463
+ **card** that names the missing argument โ€” a CI job never hangs. Add-ons that fail to load are
464
+ reported as a warning and do not stop the command.
465
+
466
+ | Exit code | Meaning |
467
+ |-----------|------------------------------------------------------------------------------------------------------|
468
+ | `0` | Everything succeeded. |
469
+ | `1` | Some extraction failed, an `error` file occurred, or nothing was selected. |
470
+ | `2` | The command was refused before anything ran: a mistyped profile, a missing source with nobody to ask, two profiles that would write the same file, an unknown option. |
471
+
472
+ โžก๏ธ [README_EXTRACTOR_CLI](https://github.com/simplibs/simplibs-extractor/blob/main/docs/README_EXTRACTOR_CLI.md)
473
+
474
+ ---
475
+
476
+ ## ๐Ÿท๏ธ `annotations/` and `constants/`
477
+
478
+ | Name | Kind | Description |
479
+ |-----------------------|----------------|------------------------------------------------------------------------------------------------------|
480
+ | `FileStatus` | Literal | `"ok"`, `"skipped"` or `"error"`. |
481
+ | `OutputFormat` | Annotated type | The **name** of a format: lowercase, starts with a letter (`"markdown"`, `"json-manifest"`). Whether a renderer exists is checked when the config is resolved. |
482
+ | `OverwritePolicy` | Literal | `"overwrite"`, `"unique"`, `"backup"` or `"error"`. |
483
+ | `StepsSpec` | Type alias | `None`, one `Action`, or a mapping `suffix -> Action` (the key `"*"` is the fallback). |
484
+ | `DEFAULT_PROFILE_KEY` | Constant | `"generic/all"`, the built-in profile: every text file, unchanged. |
485
+ | `GENERATED_MARKER` | Constant | `"simplibs-extractor:generated"`, the text that marks an output. |
486
+
487
+ The other constants (`DEFAULT_MAX_FILE_SIZE` = 1 MB, `DEFAULT_OUTPUT_NAME` = `"extraction.md"`,
488
+ `DEFAULT_SCRIPT_NAME`, `LANGUAGE_BY_SUFFIX`, `MARKER_SCAN_LINES` = 3, `PLUGIN_GROUP`,
489
+ `SCRIPT_TEMPLATE`) are documented in the reference below and importable from `simplibs.extractor.constants`.
490
+
491
+ โžก๏ธ [README_EXTRACTOR_ANNOTATIONS](https://github.com/simplibs/simplibs-extractor/blob/main/docs/README_EXTRACTOR_ANNOTATIONS.md) ยท [README_EXTRACTOR_CONSTANTS](https://github.com/simplibs/simplibs-extractor/blob/main/docs/README_EXTRACTOR_CONSTANTS.md)
492
+
493
+ ---
494
+
495
+ ## โš ๏ธ Exceptions
496
+
497
+ The library defines no exceptions of its own. A wrong call is a structured
498
+ [`simplibs.exception`](https://pypi.org/project/simplibs-exception/) card with a name starting with
499
+ `EXTRACTOR_`; a caller that wants "any error of this library" catches `SimpleError`. Problems of
500
+ **single files** (a read that fails, a step that raises) are not exceptions at all โ€” they are
501
+ `error` lines in the result.
502
+
503
+ | Error name | Class | Wraps | Raised when |
504
+ |--------------------------------|----------------------|----------------------|------------------------------------------------------------------------------|
505
+ | `EXTRACTOR_CONFIG_INVALID` | `ParamError` | `ValueError` | An unknown config parameter, a missing `source`, an unknown output format, a bad `output_name` or script name, a renderer name that is taken, `chars_per_token <= 0`, unusable command-line arguments |
506
+ | `EXTRACTOR_PROFILE_EXISTS` | `AlreadyExistsError` | `ValueError` | `register_profile` gets a key that is registered and `replace_existing` is `False` |
507
+ | `EXTRACTOR_PROFILE_INVALID` | `ParamError` | `ValueError` | A `Profile` is built with an unusable system, name, `output_name` or `steps` key; `expand_profile_keys` gets a name without a system, or nothing |
508
+ | `EXTRACTOR_PROFILE_NOT_FOUND` | `NotFoundError` | `LookupError` | A profile key is not registered (the card lists the registered ones) |
509
+ | `EXTRACTOR_SOURCE_NOT_FOUND` | `NotFoundError` | `FileNotFoundError` | The source is not an existing directory |
510
+ | `EXTRACTOR_STEP_RESULT_INVALID` | `OperationError` | `TypeError` | A step returned something other than a `str` โ€” reported against **that file** |
511
+ | `EXTRACTOR_TARGET_EXISTS` | `AlreadyExistsError` | `FileExistsError` | The output exists and the policy is `error` (also in a dry run) |
512
+ | `EXTRACTOR_TARGET_IS_DIR` | `StateError` | `IsADirectoryError` | The output path is an existing directory |
513
+
514
+ A write that fails for other reasons raises the cards of
515
+ [`simplibs-file`](https://pypi.org/project/simplibs-file/) (`AccessError`, `ConversionError`), and a
516
+ missing terminal the card of [`simplibs-cli`](https://pypi.org/project/simplibs-cli/)
517
+ (`CLI_NOT_INTERACTIVE`).
518
+
519
+ What is an exception and what is not:
520
+
521
+ | Situation | How it is reported |
522
+ |---------------------------------------------------------------------------------|----------------------------------------------------------------------|
523
+ | A **wrong call** โ€” a typo in a parameter, an unknown profile, a missing folder | a card with an `EXTRACTOR_` name, **raised** |
524
+ | A problem of **one file** โ€” a read that fails, a step that raises | **not raised**: the file is `error` in the result, the run continues |
525
+ | A run that cannot start or cannot write, inside `extract_many` | **not raised**: a result with `fatal` set, the other runs continue |
526
+ | A wrong **type** of a parameter or a field (`overwrite="clobber"`, `format=5`) | a `ValidationError` from `simplibs-validate` |
527
+
528
+ Only the card โ€” its class and its `EXTRACTOR_` name โ€” is part of the interface. The functions that raise
529
+ it are private; each lives in a `_validations/` folder next to the code that uses it.
530
+
531
+ ---
532
+
533
+ ## ๐Ÿ“š Documentation
534
+
535
+ The detailed references โ€” every function and class with its parameters, return value, raised errors, an
536
+ example and a look under the hood:
537
+
538
+ | Reference | What it describes |
539
+ |-----------|-------------------|
540
+ | [ANNOTATIONS](https://github.com/simplibs/simplibs-extractor/blob/main/docs/README_EXTRACTOR_ANNOTATIONS.md) | `FileStatus`, `OutputFormat`, `OverwritePolicy`, `StepsSpec` |
541
+ | [CONSTANTS](https://github.com/simplibs/simplibs-extractor/blob/main/docs/README_EXTRACTOR_CONSTANTS.md) | the fixed values: limits, names, the marker, the script template |
542
+ | [DEFINITIONS](https://github.com/simplibs/simplibs-extractor/blob/main/docs/README_EXTRACTOR_DEFINITIONS.md) | `ExtractionConfig`, `Profile`, `ExtractionResult`, `FileReport`, `Entry` |
543
+ | [REGISTRY](https://github.com/simplibs/simplibs-extractor/blob/main/docs/README_EXTRACTOR_REGISTRY.md) | profiles and output formats by name |
544
+ | [SELECT](https://github.com/simplibs/simplibs-extractor/blob/main/docs/logic/README_EXTRACTOR_SELECT.md) ยท [READ](https://github.com/simplibs/simplibs-extractor/blob/main/docs/logic/README_EXTRACTOR_READ.md) ยท [TRANSFORM](https://github.com/simplibs/simplibs-extractor/blob/main/docs/logic/README_EXTRACTOR_TRANSFORM.md) | phases 1โ€“3: which files, what is in them, cleaning the text |
545
+ | [RENDER](https://github.com/simplibs/simplibs-extractor/blob/main/docs/logic/README_EXTRACTOR_RENDER.md) ยท [WRITE](https://github.com/simplibs/simplibs-extractor/blob/main/docs/logic/README_EXTRACTOR_WRITE.md) ยท [REPORT](https://github.com/simplibs/simplibs-extractor/blob/main/docs/logic/README_EXTRACTOR_REPORT.md) | phases 4โ€“6: the document, the disk, the report |
546
+ | [RUN](https://github.com/simplibs/simplibs-extractor/blob/main/docs/logic/README_EXTRACTOR_RUN.md) | `extract`, `extract_many` โ€” putting the phases together |
547
+ | [PLUGINS](https://github.com/simplibs/simplibs-extractor/blob/main/docs/README_EXTRACTOR_PLUGINS.md) | finding add-ons, and writing one |
548
+ | [SCRIPTS](https://github.com/simplibs/simplibs-extractor/blob/main/docs/README_EXTRACTOR_SCRIPTS.md) | `extract_here`, `generate_local_script` |
549
+ | [CLI](https://github.com/simplibs/simplibs-extractor/blob/main/docs/README_EXTRACTOR_CLI.md) | the command `simplibs-extractor` |
550
+
551
+ ---
552
+
553
+ ## ๐Ÿ”— Related libraries
554
+
555
+ * **[`simplibs-text`](https://pypi.org/project/simplibs-text/)** โ€” the text steps (`RemoveMatches`, `COMPACT`, `KeepLineRange`, ...) a profile's `steps` are made of.
556
+ * **`simplibs-code-extractor`** โ€” the first add-on: profiles for Python and React projects, built on this extractor.
557
+ * **[`simplibs-path`](https://pypi.org/project/simplibs-path/)** โ€” scanning, normalized paths and the path rules (`HasSuffix`, `ParentNameIs`, ...) behind `include` and `exclude`.
558
+ * **[`simplibs-file`](https://pypi.org/project/simplibs-file/)** โ€” the atomic write, the backup, the text check and the size.
559
+ * **[`simplibs-rules`](https://pypi.org/project/simplibs-rules/)** and **[`simplibs-actions`](https://pypi.org/project/simplibs-actions/)** โ€” the `Rule` and `Action` objects a profile is made of.
560
+ * **[`simplibs-markdown`](https://pypi.org/project/simplibs-markdown/)** โ€” builds the Markdown output.
561
+ * **[`simplibs-cli`](https://pypi.org/project/simplibs-cli/)**, **[`simplibs-logger`](https://pypi.org/project/simplibs-logger/)** and **[`simplibs-style`](https://pypi.org/project/simplibs-style/)** โ€” the prompts, the logger and the colors of the command.
562
+ * **[`simplibs-validate`](https://pypi.org/project/simplibs-validate/)** and **[`simplibs-exception`](https://pypi.org/project/simplibs-exception/)** โ€” the validation and the error cards.
563
+
564
+ ---
565
+
566
+ ## โ˜ฏ๏ธ About simplibs
567
+
568
+ All libraries in the **simplibs** (Simple Libraries) ecosystem share a common
569
+ engineering philosophy:
570
+
571
+ * **Dyslexia-friendly:**
572
+ We actively minimize cognitive load. Code is atomized into small, self-contained units,
573
+ files are named directly after the logical task they perform, and explanations describe
574
+ *why* something is designed, not just *what* it is.
575
+ * **Programmer's Zen:**
576
+ Nothing should be missing, and nothing should be superfluous. We value clean execution
577
+ paths and robust, understandable code architectures over rushed, messy feature sets.
578
+ * **Defensive Style:**
579
+ We actively anticipate edge cases and failure modes so that only safe operational paths
580
+ remain. Our code is built to degrade gracefully rather than crash unexpectedly.
581
+ * **Minimalism:**
582
+ Find the most direct path to the goal in as few operational steps as possible without
583
+ taking shortcuts on safety, readability, or completeness.
584
+ * **Code as Craft:**
585
+ Code should be pleasant to look at, readable at a glance, and evoke structural harmony.
586
+ We treat software engineering as a precision trade.
587
+
588
+ ---
589
+
590
+ ### ๐Ÿค Contributing & Community
591
+
592
+ This is an **open-source project** built with love and care. We strongly believe in
593
+ community collaboration and welcome any feedback, bug reports, or feature ideas!
594
+
595
+ * **Want to contribute?** Feel free to open an Issue or submit a Pull Request.
596
+ * **Want to get in touch?** If you'd like to discuss the project further, collaborate,
597
+ or just say hello, feel free to open a GitHub Issue or start a Discussion.
598
+
599
+ ---
600
+
601
+ ### ๐Ÿ“ License
602
+
603
+ This library is released under the **MIT License**. Build great things!
604
+
605
+ ---