codetool-shell 0.1.1__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 (176) hide show
  1. codetool_shell-0.1.1/.gitignore +24 -0
  2. codetool_shell-0.1.1/PKG-INFO +152 -0
  3. codetool_shell-0.1.1/README.md +125 -0
  4. codetool_shell-0.1.1/benchmarks/benchmark_compress_text.py +79 -0
  5. codetool_shell-0.1.1/hatch_build.py +203 -0
  6. codetool_shell-0.1.1/pyproject.toml +85 -0
  7. codetool_shell-0.1.1/rust/Cargo.lock +128 -0
  8. codetool_shell-0.1.1/rust/Cargo.toml +13 -0
  9. codetool_shell-0.1.1/rust/src/compression.rs +518 -0
  10. codetool_shell-0.1.1/rust/src/filters/build_compiler/detector.rs +469 -0
  11. codetool_shell-0.1.1/rust/src/filters/build_compiler/mod.rs +5 -0
  12. codetool_shell-0.1.1/rust/src/filters/build_compiler/reducer.rs +914 -0
  13. codetool_shell-0.1.1/rust/src/filters/build_compiler/summary.rs +999 -0
  14. codetool_shell-0.1.1/rust/src/filters/ci_job_log/detector.rs +59 -0
  15. codetool_shell-0.1.1/rust/src/filters/ci_job_log/mod.rs +5 -0
  16. codetool_shell-0.1.1/rust/src/filters/ci_job_log/reducer.rs +295 -0
  17. codetool_shell-0.1.1/rust/src/filters/ci_job_log/summary.rs +265 -0
  18. codetool_shell-0.1.1/rust/src/filters/diff/detector.rs +191 -0
  19. codetool_shell-0.1.1/rust/src/filters/diff/mod.rs +4 -0
  20. codetool_shell-0.1.1/rust/src/filters/diff/reducer.rs +573 -0
  21. codetool_shell-0.1.1/rust/src/filters/generic_log/detector.rs +195 -0
  22. codetool_shell-0.1.1/rust/src/filters/generic_log/mod.rs +5 -0
  23. codetool_shell-0.1.1/rust/src/filters/generic_log/reducer.rs +288 -0
  24. codetool_shell-0.1.1/rust/src/filters/generic_log/summary.rs +272 -0
  25. codetool_shell-0.1.1/rust/src/filters/git.rs +669 -0
  26. codetool_shell-0.1.1/rust/src/filters/html_cleanup/detector.rs +136 -0
  27. codetool_shell-0.1.1/rust/src/filters/html_cleanup/mod.rs +8 -0
  28. codetool_shell-0.1.1/rust/src/filters/html_cleanup/reducer.rs +24 -0
  29. codetool_shell-0.1.1/rust/src/filters/html_cleanup/summary.rs +812 -0
  30. codetool_shell-0.1.1/rust/src/filters/html_cleanup/tests.rs +345 -0
  31. codetool_shell-0.1.1/rust/src/filters/json_payload/detector.rs +50 -0
  32. codetool_shell-0.1.1/rust/src/filters/json_payload/mod.rs +5 -0
  33. codetool_shell-0.1.1/rust/src/filters/json_payload/reducer.rs +252 -0
  34. codetool_shell-0.1.1/rust/src/filters/json_payload/summary.rs +365 -0
  35. codetool_shell-0.1.1/rust/src/filters/listing/detector.rs +427 -0
  36. codetool_shell-0.1.1/rust/src/filters/listing/mod.rs +4 -0
  37. codetool_shell-0.1.1/rust/src/filters/listing/reducer.rs +234 -0
  38. codetool_shell-0.1.1/rust/src/filters/log_template/constants.rs +58 -0
  39. codetool_shell-0.1.1/rust/src/filters/log_template/detector.rs +360 -0
  40. codetool_shell-0.1.1/rust/src/filters/log_template/mod.rs +10 -0
  41. codetool_shell-0.1.1/rust/src/filters/log_template/reducer.rs +106 -0
  42. codetool_shell-0.1.1/rust/src/filters/log_template/template.rs +354 -0
  43. codetool_shell-0.1.1/rust/src/filters/log_template/tests.rs +166 -0
  44. codetool_shell-0.1.1/rust/src/filters/log_template/types.rs +13 -0
  45. codetool_shell-0.1.1/rust/src/filters/mod.rs +49 -0
  46. codetool_shell-0.1.1/rust/src/filters/opaque_payload/detector.rs +646 -0
  47. codetool_shell-0.1.1/rust/src/filters/opaque_payload/mod.rs +8 -0
  48. codetool_shell-0.1.1/rust/src/filters/opaque_payload/reducer.rs +120 -0
  49. codetool_shell-0.1.1/rust/src/filters/opaque_payload/summary.rs +51 -0
  50. codetool_shell-0.1.1/rust/src/filters/opaque_payload/tests.rs +228 -0
  51. codetool_shell-0.1.1/rust/src/filters/package_manager/detector.rs +557 -0
  52. codetool_shell-0.1.1/rust/src/filters/package_manager/mod.rs +5 -0
  53. codetool_shell-0.1.1/rust/src/filters/package_manager/reducer.rs +619 -0
  54. codetool_shell-0.1.1/rust/src/filters/package_manager/summary.rs +396 -0
  55. codetool_shell-0.1.1/rust/src/filters/rg.rs +524 -0
  56. codetool_shell-0.1.1/rust/src/filters/system_output/detector.rs +687 -0
  57. codetool_shell-0.1.1/rust/src/filters/system_output/mod.rs +8 -0
  58. codetool_shell-0.1.1/rust/src/filters/system_output/reducer.rs +340 -0
  59. codetool_shell-0.1.1/rust/src/filters/system_output/summary.rs +196 -0
  60. codetool_shell-0.1.1/rust/src/filters/system_output/tests.rs +452 -0
  61. codetool_shell-0.1.1/rust/src/filters/table/detector.rs +364 -0
  62. codetool_shell-0.1.1/rust/src/filters/table/mod.rs +5 -0
  63. codetool_shell-0.1.1/rust/src/filters/table/reducer.rs +293 -0
  64. codetool_shell-0.1.1/rust/src/filters/table/summary.rs +110 -0
  65. codetool_shell-0.1.1/rust/src/filters/test_runner/ansi.rs +79 -0
  66. codetool_shell-0.1.1/rust/src/filters/test_runner/detector.rs +725 -0
  67. codetool_shell-0.1.1/rust/src/filters/test_runner/go_jsonl.rs +233 -0
  68. codetool_shell-0.1.1/rust/src/filters/test_runner/mod.rs +9 -0
  69. codetool_shell-0.1.1/rust/src/filters/test_runner/reducer.rs +130 -0
  70. codetool_shell-0.1.1/rust/src/filters/test_runner/summary.rs +643 -0
  71. codetool_shell-0.1.1/rust/src/filters/test_runner/tests.rs +966 -0
  72. codetool_shell-0.1.1/rust/src/filters/traceback/detector.rs +246 -0
  73. codetool_shell-0.1.1/rust/src/filters/traceback/mod.rs +5 -0
  74. codetool_shell-0.1.1/rust/src/filters/traceback/reducer.rs +398 -0
  75. codetool_shell-0.1.1/rust/src/filters/traceback/summary.rs +168 -0
  76. codetool_shell-0.1.1/rust/src/filters/tree.rs +117 -0
  77. codetool_shell-0.1.1/rust/src/main.rs +78 -0
  78. codetool_shell-0.1.1/rust/src/text.rs +80 -0
  79. codetool_shell-0.1.1/scripts/benchmark_filters.py +513 -0
  80. codetool_shell-0.1.1/scripts/manual_compress_test.py +62 -0
  81. codetool_shell-0.1.1/scripts/package_rust_binary.py +194 -0
  82. codetool_shell-0.1.1/scripts/stage_prebuilt.py +122 -0
  83. codetool_shell-0.1.1/src/codetool_shell/__init__.py +11 -0
  84. codetool_shell-0.1.1/src/codetool_shell/api.py +59 -0
  85. codetool_shell-0.1.1/src/codetool_shell/filters/__init__.py +14 -0
  86. codetool_shell-0.1.1/src/codetool_shell/filters/build_compiler/__init__.py +7 -0
  87. codetool_shell-0.1.1/src/codetool_shell/filters/build_compiler/detector.py +412 -0
  88. codetool_shell-0.1.1/src/codetool_shell/filters/build_compiler/reducer.py +166 -0
  89. codetool_shell-0.1.1/src/codetool_shell/filters/build_compiler/summary.py +617 -0
  90. codetool_shell-0.1.1/src/codetool_shell/filters/ci_job_log/__init__.py +7 -0
  91. codetool_shell-0.1.1/src/codetool_shell/filters/ci_job_log/detector.py +64 -0
  92. codetool_shell-0.1.1/src/codetool_shell/filters/ci_job_log/reducer.py +99 -0
  93. codetool_shell-0.1.1/src/codetool_shell/filters/ci_job_log/summary.py +243 -0
  94. codetool_shell-0.1.1/src/codetool_shell/filters/diff/__init__.py +7 -0
  95. codetool_shell-0.1.1/src/codetool_shell/filters/diff/detector.py +136 -0
  96. codetool_shell-0.1.1/src/codetool_shell/filters/diff/reducer.py +308 -0
  97. codetool_shell-0.1.1/src/codetool_shell/filters/generic_log/__init__.py +7 -0
  98. codetool_shell-0.1.1/src/codetool_shell/filters/generic_log/detector.py +175 -0
  99. codetool_shell-0.1.1/src/codetool_shell/filters/generic_log/reducer.py +99 -0
  100. codetool_shell-0.1.1/src/codetool_shell/filters/generic_log/summary.py +161 -0
  101. codetool_shell-0.1.1/src/codetool_shell/filters/git.py +514 -0
  102. codetool_shell-0.1.1/src/codetool_shell/filters/html_cleanup/__init__.py +7 -0
  103. codetool_shell-0.1.1/src/codetool_shell/filters/html_cleanup/detector.py +136 -0
  104. codetool_shell-0.1.1/src/codetool_shell/filters/html_cleanup/reducer.py +27 -0
  105. codetool_shell-0.1.1/src/codetool_shell/filters/html_cleanup/summary.py +422 -0
  106. codetool_shell-0.1.1/src/codetool_shell/filters/json_payload/__init__.py +7 -0
  107. codetool_shell-0.1.1/src/codetool_shell/filters/json_payload/detector.py +62 -0
  108. codetool_shell-0.1.1/src/codetool_shell/filters/json_payload/reducer.py +81 -0
  109. codetool_shell-0.1.1/src/codetool_shell/filters/json_payload/summary.py +233 -0
  110. codetool_shell-0.1.1/src/codetool_shell/filters/listing/__init__.py +7 -0
  111. codetool_shell-0.1.1/src/codetool_shell/filters/listing/detector.py +294 -0
  112. codetool_shell-0.1.1/src/codetool_shell/filters/listing/reducer.py +30 -0
  113. codetool_shell-0.1.1/src/codetool_shell/filters/log_template/__init__.py +7 -0
  114. codetool_shell-0.1.1/src/codetool_shell/filters/log_template/constants.py +76 -0
  115. codetool_shell-0.1.1/src/codetool_shell/filters/log_template/detector.py +331 -0
  116. codetool_shell-0.1.1/src/codetool_shell/filters/log_template/reducer.py +78 -0
  117. codetool_shell-0.1.1/src/codetool_shell/filters/log_template/template.py +280 -0
  118. codetool_shell-0.1.1/src/codetool_shell/filters/log_template/types.py +21 -0
  119. codetool_shell-0.1.1/src/codetool_shell/filters/opaque_payload/__init__.py +7 -0
  120. codetool_shell-0.1.1/src/codetool_shell/filters/opaque_payload/detector.py +563 -0
  121. codetool_shell-0.1.1/src/codetool_shell/filters/opaque_payload/reducer.py +142 -0
  122. codetool_shell-0.1.1/src/codetool_shell/filters/opaque_payload/summary.py +61 -0
  123. codetool_shell-0.1.1/src/codetool_shell/filters/package_manager/__init__.py +7 -0
  124. codetool_shell-0.1.1/src/codetool_shell/filters/package_manager/detector.py +220 -0
  125. codetool_shell-0.1.1/src/codetool_shell/filters/package_manager/reducer.py +110 -0
  126. codetool_shell-0.1.1/src/codetool_shell/filters/package_manager/summary.py +172 -0
  127. codetool_shell-0.1.1/src/codetool_shell/filters/pipeline.py +65 -0
  128. codetool_shell-0.1.1/src/codetool_shell/filters/rg.py +250 -0
  129. codetool_shell-0.1.1/src/codetool_shell/filters/system_output/__init__.py +7 -0
  130. codetool_shell-0.1.1/src/codetool_shell/filters/system_output/detector.py +600 -0
  131. codetool_shell-0.1.1/src/codetool_shell/filters/system_output/reducer.py +331 -0
  132. codetool_shell-0.1.1/src/codetool_shell/filters/system_output/summary.py +164 -0
  133. codetool_shell-0.1.1/src/codetool_shell/filters/table/__init__.py +7 -0
  134. codetool_shell-0.1.1/src/codetool_shell/filters/table/detector.py +244 -0
  135. codetool_shell-0.1.1/src/codetool_shell/filters/table/reducer.py +57 -0
  136. codetool_shell-0.1.1/src/codetool_shell/filters/table/summary.py +37 -0
  137. codetool_shell-0.1.1/src/codetool_shell/filters/test_runner/__init__.py +7 -0
  138. codetool_shell-0.1.1/src/codetool_shell/filters/test_runner/ansi.py +80 -0
  139. codetool_shell-0.1.1/src/codetool_shell/filters/test_runner/detector.py +409 -0
  140. codetool_shell-0.1.1/src/codetool_shell/filters/test_runner/reducer.py +288 -0
  141. codetool_shell-0.1.1/src/codetool_shell/filters/test_runner/summary.py +449 -0
  142. codetool_shell-0.1.1/src/codetool_shell/filters/text.py +38 -0
  143. codetool_shell-0.1.1/src/codetool_shell/filters/traceback/__init__.py +7 -0
  144. codetool_shell-0.1.1/src/codetool_shell/filters/traceback/detector.py +209 -0
  145. codetool_shell-0.1.1/src/codetool_shell/filters/traceback/reducer.py +141 -0
  146. codetool_shell-0.1.1/src/codetool_shell/filters/traceback/summary.py +122 -0
  147. codetool_shell-0.1.1/src/codetool_shell/filters/tree.py +59 -0
  148. codetool_shell-0.1.1/src/codetool_shell/py.typed +0 -0
  149. codetool_shell-0.1.1/src/codetool_shell/python_backend.py +38 -0
  150. codetool_shell-0.1.1/src/codetool_shell/rust_backend.py +254 -0
  151. codetool_shell-0.1.1/tests/test_api.py +92 -0
  152. codetool_shell-0.1.1/tests/test_benchmark_compress_text.py +50 -0
  153. codetool_shell-0.1.1/tests/test_build_compiler_filter.py +770 -0
  154. codetool_shell-0.1.1/tests/test_diff_filter.py +190 -0
  155. codetool_shell-0.1.1/tests/test_filter_benchmarks.py +99 -0
  156. codetool_shell-0.1.1/tests/test_generic_log_filter.py +151 -0
  157. codetool_shell-0.1.1/tests/test_hatch_build.py +58 -0
  158. codetool_shell-0.1.1/tests/test_html_cleanup_filter.py +279 -0
  159. codetool_shell-0.1.1/tests/test_init.py +9 -0
  160. codetool_shell-0.1.1/tests/test_json_payload_filter.py +137 -0
  161. codetool_shell-0.1.1/tests/test_listing_filter.py +175 -0
  162. codetool_shell-0.1.1/tests/test_log_template_filter.py +213 -0
  163. codetool_shell-0.1.1/tests/test_opaque_payload_filter.py +194 -0
  164. codetool_shell-0.1.1/tests/test_package_manager_filter.py +446 -0
  165. codetool_shell-0.1.1/tests/test_package_rust_binary.py +57 -0
  166. codetool_shell-0.1.1/tests/test_python_backend.py +1219 -0
  167. codetool_shell-0.1.1/tests/test_rg_filter.py +127 -0
  168. codetool_shell-0.1.1/tests/test_rust_backend.py +222 -0
  169. codetool_shell-0.1.1/tests/test_rust_main.py +51 -0
  170. codetool_shell-0.1.1/tests/test_source_code_noop.py +93 -0
  171. codetool_shell-0.1.1/tests/test_stage_prebuilt.py +22 -0
  172. codetool_shell-0.1.1/tests/test_system_output_filter.py +401 -0
  173. codetool_shell-0.1.1/tests/test_table_filter.py +201 -0
  174. codetool_shell-0.1.1/tests/test_test_runner_filter.py +203 -0
  175. codetool_shell-0.1.1/tests/test_traceback_filter.py +201 -0
  176. codetool_shell-0.1.1/uv.lock +237 -0
@@ -0,0 +1,24 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+ .pytest_cache/
9
+
10
+ # Virtual environments
11
+ .venv
12
+
13
+ # Test-generated executable shims
14
+ .pytest-executables/
15
+
16
+ # Staged Rust helper binaries
17
+ src/codetool_shell/bin/
18
+
19
+ # Rust build artifacts
20
+ rust/target/
21
+
22
+ # Research file
23
+
24
+ research/
@@ -0,0 +1,152 @@
1
+ Metadata-Version: 2.4
2
+ Name: codetool-shell
3
+ Version: 0.1.1
4
+ Summary: Compress shell output text with Python and optional Rust backends
5
+ Project-URL: Homepage, https://github.com/pbi-agent/codetool-shell
6
+ Project-URL: Repository, https://github.com/pbi-agent/codetool-shell
7
+ Project-URL: Issues, https://github.com/pbi-agent/codetool-shell/issues
8
+ Project-URL: Changelog, https://github.com/pbi-agent/codetool-shell/releases
9
+ Author-email: drod <naceur.bs@gmail.com>
10
+ Maintainer-email: drod <naceur.bs@gmail.com>
11
+ Keywords: agent,compression,developer-tools,llm,rust,shell,terminal
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: MacOS
15
+ Classifier: Operating System :: Microsoft :: Windows
16
+ Classifier: Operating System :: POSIX :: Linux
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3 :: Only
19
+ Classifier: Programming Language :: Rust
20
+ Classifier: Topic :: Software Development
21
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
22
+ Classifier: Topic :: System :: Shells
23
+ Classifier: Topic :: Text Processing
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.9
26
+ Description-Content-Type: text/markdown
27
+
28
+ # codetool-shell
29
+
30
+ Python interface library for compressing plain shell-output text before it is
31
+ returned to an LLM/tool consumer.
32
+
33
+ ```python
34
+ from codetool_shell import compress_text
35
+
36
+ compressed = compress_text("one \n\n\n two\t\n")
37
+ print(compressed)
38
+ ```
39
+
40
+ The core pipeline is intentionally conservative:
41
+
42
+ - normalize CRLF/CR line endings to LF
43
+ - trim trailing whitespace on each line
44
+ - run dedicated output filters only when they make the text smaller
45
+ - collapse repeated blank lines to `max_blank_lines` (default: `1`)
46
+
47
+ Initial dedicated filters cover:
48
+
49
+ - git output: short and long status, tabbed name-status, diff stats, and patch
50
+ hunks/binary-patch payloads with verbose unchanged or encoded context
51
+ summarized
52
+ - test-runner output: conservative ANSI-aware pytest (including quiet
53
+ progress/summary output), unittest-like, vitest-like, Playwright, and
54
+ cargo-test summaries that preserve failure identifiers, assertion/error text,
55
+ source locations, artifacts/traces, and final totals
56
+ - CI/job logs: conservative GitHub Actions / `gh run`-style summaries that
57
+ collapse repeated timestamped step noise while preserving step names,
58
+ annotations, warnings/errors, file locations, artifact/URL/path references,
59
+ and job/workflow conclusions
60
+ - standalone Python tracebacks: conservative traceback summaries that preserve
61
+ exception type/message, file stack frames, chained-exception separators,
62
+ source/caret context, and small command noise while collapsing repeated or
63
+ internal stack frames
64
+ - structured tables: conservative psql, SQLite/column, MySQL boxed, and long
65
+ Markdown pipe table summaries that preserve headers, footers, first/last
66
+ rows, and warning/error/URL/path/ID rows while counting omitted middle rows
67
+ - file/tree/listing output: conservative path-only, `tree`, and safe `ls -R`
68
+ reducers that render repeated path prefixes as compact trees while preserving
69
+ visible summary totals and leaving metadata-rich listings unchanged
70
+ - ripgrep output: direct `path:line[:column]:text`, heading-mode, context-mode
71
+ `path-line-context` with `--` separators, and ANSI-colored matches grouped
72
+ into a compact path tree when repeated paths or prefixes would otherwise be
73
+ duplicated, while diagnostic/table/prose-looking output is left alone
74
+ - generic application logs: conservative long timestamp/level-prefixed log
75
+ summaries that collapse repeated INFO/DEBUG/TRACE patterns while preserving
76
+ first/last context, warnings/errors/fatal/panic/exception lines,
77
+ file paths/URLs, failure summaries, and unusual levels
78
+ - package-manager logs: conservative uv/pip, npm, bun, and cargo package
79
+ install/update summaries that collapse progress/download/resolve/install
80
+ noise while preserving warnings/errors, package summaries, versions,
81
+ lock/frozen conflicts, audit/vulnerability summaries, hints, paths, and URLs
82
+ - build/compiler diagnostics: conservative cargo/rustc, tsc, ESLint, and
83
+ mypy-like reducers that collapse build progress while preserving
84
+ warnings/errors, codes, file spans, source/caret snippets, notes/help, rule
85
+ IDs, and final summaries
86
+
87
+ ## Backends
88
+
89
+ - `backend="python"` uses the pure-Python implementation.
90
+ - `backend="rust"` requires the optional Rust CLI backend.
91
+ - `backend="auto"` prefers Rust when a compatible binary is discoverable and
92
+ falls back to Python otherwise.
93
+
94
+ Rust discovery order:
95
+
96
+ 1. `CODETOOL_SHELL_RUST_BINARY`
97
+ 2. packaged wheel binaries under `codetool_shell/bin/<platform>/`
98
+ 3. development binaries under `rust/target/{release,debug}/`
99
+ 4. `PATH`
100
+
101
+ Build and stage the Rust CLI before creating a target wheel with:
102
+
103
+ ```bash
104
+ python scripts/package_rust_binary.py --target-runtime linux-x86_64
105
+ ```
106
+
107
+ Then build the Python distributions with:
108
+
109
+ ```bash
110
+ uv build
111
+ ```
112
+
113
+ During local development you can also rely on the development build directly:
114
+
115
+ ```bash
116
+ cd rust
117
+ cargo build
118
+ ```
119
+
120
+ ## Packaging and release
121
+
122
+ `codetool-shell` publishes target-specific wheels that include one matching
123
+ Rust helper binary under `codetool_shell/bin/<runtime>/`. Supported runtime keys
124
+ are:
125
+
126
+ - `linux-x86_64`
127
+ - `linux-aarch64`
128
+ - `macos-x86_64`
129
+ - `macos-arm64`
130
+ - `windows-x86_64`
131
+ - `windows-arm64`
132
+
133
+ To build a Linux x86_64 wheel locally (replace the runtime key for other
134
+ targets):
135
+
136
+ ```bash
137
+ uv run python scripts/package_rust_binary.py --target-runtime linux-x86_64
138
+ CODETOOL_SHELL_TARGET_RUNTIME=linux-x86_64 uv build --wheel
139
+ ```
140
+
141
+ The release workflow builds an sdist, manylinux wheels, native macOS/Windows
142
+ wheels, checks the distributions, and publishes to PyPI via trusted publishing
143
+ for tagged releases (`v*`) or manual dispatch with `publish=true`.
144
+
145
+ ## Development
146
+
147
+ ```bash
148
+ uv sync --dev
149
+ uv run pytest
150
+ uv run python benchmarks/benchmark_compress_text.py
151
+ uv run python scripts/benchmark_filters.py --iterations 1000
152
+ ```
@@ -0,0 +1,125 @@
1
+ # codetool-shell
2
+
3
+ Python interface library for compressing plain shell-output text before it is
4
+ returned to an LLM/tool consumer.
5
+
6
+ ```python
7
+ from codetool_shell import compress_text
8
+
9
+ compressed = compress_text("one \n\n\n two\t\n")
10
+ print(compressed)
11
+ ```
12
+
13
+ The core pipeline is intentionally conservative:
14
+
15
+ - normalize CRLF/CR line endings to LF
16
+ - trim trailing whitespace on each line
17
+ - run dedicated output filters only when they make the text smaller
18
+ - collapse repeated blank lines to `max_blank_lines` (default: `1`)
19
+
20
+ Initial dedicated filters cover:
21
+
22
+ - git output: short and long status, tabbed name-status, diff stats, and patch
23
+ hunks/binary-patch payloads with verbose unchanged or encoded context
24
+ summarized
25
+ - test-runner output: conservative ANSI-aware pytest (including quiet
26
+ progress/summary output), unittest-like, vitest-like, Playwright, and
27
+ cargo-test summaries that preserve failure identifiers, assertion/error text,
28
+ source locations, artifacts/traces, and final totals
29
+ - CI/job logs: conservative GitHub Actions / `gh run`-style summaries that
30
+ collapse repeated timestamped step noise while preserving step names,
31
+ annotations, warnings/errors, file locations, artifact/URL/path references,
32
+ and job/workflow conclusions
33
+ - standalone Python tracebacks: conservative traceback summaries that preserve
34
+ exception type/message, file stack frames, chained-exception separators,
35
+ source/caret context, and small command noise while collapsing repeated or
36
+ internal stack frames
37
+ - structured tables: conservative psql, SQLite/column, MySQL boxed, and long
38
+ Markdown pipe table summaries that preserve headers, footers, first/last
39
+ rows, and warning/error/URL/path/ID rows while counting omitted middle rows
40
+ - file/tree/listing output: conservative path-only, `tree`, and safe `ls -R`
41
+ reducers that render repeated path prefixes as compact trees while preserving
42
+ visible summary totals and leaving metadata-rich listings unchanged
43
+ - ripgrep output: direct `path:line[:column]:text`, heading-mode, context-mode
44
+ `path-line-context` with `--` separators, and ANSI-colored matches grouped
45
+ into a compact path tree when repeated paths or prefixes would otherwise be
46
+ duplicated, while diagnostic/table/prose-looking output is left alone
47
+ - generic application logs: conservative long timestamp/level-prefixed log
48
+ summaries that collapse repeated INFO/DEBUG/TRACE patterns while preserving
49
+ first/last context, warnings/errors/fatal/panic/exception lines,
50
+ file paths/URLs, failure summaries, and unusual levels
51
+ - package-manager logs: conservative uv/pip, npm, bun, and cargo package
52
+ install/update summaries that collapse progress/download/resolve/install
53
+ noise while preserving warnings/errors, package summaries, versions,
54
+ lock/frozen conflicts, audit/vulnerability summaries, hints, paths, and URLs
55
+ - build/compiler diagnostics: conservative cargo/rustc, tsc, ESLint, and
56
+ mypy-like reducers that collapse build progress while preserving
57
+ warnings/errors, codes, file spans, source/caret snippets, notes/help, rule
58
+ IDs, and final summaries
59
+
60
+ ## Backends
61
+
62
+ - `backend="python"` uses the pure-Python implementation.
63
+ - `backend="rust"` requires the optional Rust CLI backend.
64
+ - `backend="auto"` prefers Rust when a compatible binary is discoverable and
65
+ falls back to Python otherwise.
66
+
67
+ Rust discovery order:
68
+
69
+ 1. `CODETOOL_SHELL_RUST_BINARY`
70
+ 2. packaged wheel binaries under `codetool_shell/bin/<platform>/`
71
+ 3. development binaries under `rust/target/{release,debug}/`
72
+ 4. `PATH`
73
+
74
+ Build and stage the Rust CLI before creating a target wheel with:
75
+
76
+ ```bash
77
+ python scripts/package_rust_binary.py --target-runtime linux-x86_64
78
+ ```
79
+
80
+ Then build the Python distributions with:
81
+
82
+ ```bash
83
+ uv build
84
+ ```
85
+
86
+ During local development you can also rely on the development build directly:
87
+
88
+ ```bash
89
+ cd rust
90
+ cargo build
91
+ ```
92
+
93
+ ## Packaging and release
94
+
95
+ `codetool-shell` publishes target-specific wheels that include one matching
96
+ Rust helper binary under `codetool_shell/bin/<runtime>/`. Supported runtime keys
97
+ are:
98
+
99
+ - `linux-x86_64`
100
+ - `linux-aarch64`
101
+ - `macos-x86_64`
102
+ - `macos-arm64`
103
+ - `windows-x86_64`
104
+ - `windows-arm64`
105
+
106
+ To build a Linux x86_64 wheel locally (replace the runtime key for other
107
+ targets):
108
+
109
+ ```bash
110
+ uv run python scripts/package_rust_binary.py --target-runtime linux-x86_64
111
+ CODETOOL_SHELL_TARGET_RUNTIME=linux-x86_64 uv build --wheel
112
+ ```
113
+
114
+ The release workflow builds an sdist, manylinux wheels, native macOS/Windows
115
+ wheels, checks the distributions, and publishes to PyPI via trusted publishing
116
+ for tagged releases (`v*`) or manual dispatch with `publish=true`.
117
+
118
+ ## Development
119
+
120
+ ```bash
121
+ uv sync --dev
122
+ uv run pytest
123
+ uv run python benchmarks/benchmark_compress_text.py
124
+ uv run python scripts/benchmark_filters.py --iterations 1000
125
+ ```
@@ -0,0 +1,79 @@
1
+ """Tiny benchmark for compression backends."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import argparse
6
+ import statistics
7
+ import time
8
+
9
+ from codetool_shell import compress_text
10
+
11
+ SAMPLE_OUTPUT = "\n".join(
12
+ [
13
+ "collecting workspace metadata ",
14
+ "",
15
+ "",
16
+ "src/package/module.py:10: info: ok\t",
17
+ "",
18
+ "",
19
+ "",
20
+ "tests/test_module.py::test_case PASSED ",
21
+ "",
22
+ ]
23
+ )
24
+
25
+
26
+ def benchmark_backend(
27
+ backend: str,
28
+ text: str,
29
+ max_blank_lines: int,
30
+ iterations: int,
31
+ ) -> dict:
32
+ compress_text(text, max_blank_lines=max_blank_lines, backend=backend)
33
+
34
+ durations = []
35
+ output = ""
36
+ for _ in range(iterations):
37
+ started = time.perf_counter()
38
+ output = compress_text(text, max_blank_lines=max_blank_lines, backend=backend)
39
+ durations.append((time.perf_counter() - started) * 1000)
40
+
41
+ verify_result(backend, text, output)
42
+ return {
43
+ "backend": backend,
44
+ "min_ms": min(durations),
45
+ "median_ms": statistics.median(durations),
46
+ "max_ms": max(durations),
47
+ "compressed_chars": len(output),
48
+ }
49
+
50
+
51
+ def verify_result(backend: str, original: str, compressed: str) -> None:
52
+ if not isinstance(compressed, str):
53
+ raise RuntimeError(f"{backend} benchmark did not return text")
54
+ if len(compressed) > len(original):
55
+ raise RuntimeError(f"{backend} benchmark expanded the sample")
56
+
57
+
58
+ def main() -> int:
59
+ parser = argparse.ArgumentParser()
60
+ parser.add_argument("--backend", choices=["auto", "python", "rust"], default="auto")
61
+ parser.add_argument("--iterations", type=int, default=1000)
62
+ parser.add_argument("--max-blank-lines", type=int, default=1)
63
+ args = parser.parse_args()
64
+
65
+ row = benchmark_backend(
66
+ args.backend,
67
+ SAMPLE_OUTPUT,
68
+ args.max_blank_lines,
69
+ args.iterations,
70
+ )
71
+ print(
72
+ "{backend}: min={min_ms:.4f}ms median={median_ms:.4f}ms "
73
+ "max={max_ms:.4f}ms chars={compressed_chars}".format(**row)
74
+ )
75
+ return 0
76
+
77
+
78
+ if __name__ == "__main__":
79
+ raise SystemExit(main())
@@ -0,0 +1,203 @@
1
+ """Target-aware Hatch wheel hook for prebuilt Rust helper wheels."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import sysconfig
7
+ from pathlib import Path
8
+
9
+ from hatchling.builders.hooks.plugin.interface import BuildHookInterface
10
+
11
+ PACKAGE = "codetool_shell"
12
+ RUST_BINARY_NAME = "codetool-shell-rust"
13
+ TARGET_RUNTIME_ENVS = (
14
+ "CODETOOL_SHELL_TARGET_RUNTIME",
15
+ "CODETOOL_TARGET_RUNTIME",
16
+ )
17
+ TARGET_WHEEL_TAG_ENVS = (
18
+ "CODETOOL_SHELL_TARGET_WHEEL_TAG",
19
+ "CODETOOL_TARGET_WHEEL_TAG",
20
+ )
21
+
22
+ SUPPORTED_RUNTIME_KEYS = (
23
+ "linux-x86_64",
24
+ "linux-aarch64",
25
+ "macos-x86_64",
26
+ "macos-arm64",
27
+ "windows-x86_64",
28
+ "windows-arm64",
29
+ )
30
+
31
+ RUNTIME_ALIASES = {
32
+ "linux-amd64": "linux-x86_64",
33
+ "linux-x64": "linux-x86_64",
34
+ "darwin-x86_64": "macos-x86_64",
35
+ "darwin-amd64": "macos-x86_64",
36
+ "darwin-arm64": "macos-arm64",
37
+ "darwin-aarch64": "macos-arm64",
38
+ "macos-aarch64": "macos-arm64",
39
+ "macosx-x86_64": "macos-x86_64",
40
+ "macosx-arm64": "macos-arm64",
41
+ "linux-arm64": "linux-aarch64",
42
+ "windows-amd64": "windows-x86_64",
43
+ "windows-x64": "windows-x86_64",
44
+ "windows-aarch64": "windows-arm64",
45
+ "win-x86_64": "windows-x86_64",
46
+ "win-amd64": "windows-x86_64",
47
+ "win-arm64": "windows-arm64",
48
+ "linux_x86_64": "linux-x86_64",
49
+ "linux_aarch64": "linux-aarch64",
50
+ "macos_x86_64": "macos-x86_64",
51
+ "macos_arm64": "macos-arm64",
52
+ "windows_x86_64": "windows-x86_64",
53
+ "windows_arm64": "windows-arm64",
54
+ }
55
+
56
+ DEFAULT_WHEEL_TAGS = {
57
+ "linux-x86_64": "manylinux2014_x86_64",
58
+ "linux-aarch64": "manylinux2014_aarch64",
59
+ "macos-x86_64": "macosx_10_12_x86_64",
60
+ "macos-arm64": "macosx_11_0_arm64",
61
+ "windows-x86_64": "win_amd64",
62
+ "windows-arm64": "win_arm64",
63
+ }
64
+
65
+
66
+ class CustomBuildHook(BuildHookInterface):
67
+ """Include exactly one target runtime binary and tag the wheel for it."""
68
+
69
+ def initialize(self, version: str, build_data: dict) -> None:
70
+ if self.target_name != "wheel":
71
+ return
72
+
73
+ runtime_key = target_runtime_key()
74
+ binary = staged_binary(Path(self.root), runtime_key)
75
+ if binary is None:
76
+ if target_runtime_env_is_set():
77
+ raise FileNotFoundError(
78
+ f"no staged Rust helper for {runtime_key}; run "
79
+ f"scripts/package_rust_binary.py --target-runtime {runtime_key} first"
80
+ )
81
+ return
82
+
83
+ build_data["pure_python"] = False
84
+ build_data["tag"] = f"py3-none-{target_wheel_tag(runtime_key)}"
85
+
86
+ force_include = build_data.setdefault("force_include", {})
87
+ src_root = Path(self.root) / "src"
88
+ force_include[binary.relative_to(self.root).as_posix()] = binary.relative_to(src_root).as_posix()
89
+
90
+
91
+ def staged_binary(root: Path, runtime_key: str) -> Path | None:
92
+ runtime_key = canonical_runtime_key(runtime_key)
93
+ package_bin = root / "src" / PACKAGE / "bin"
94
+ binary_name = binary_name_for_runtime(runtime_key)
95
+ for key in runtime_key_candidates(runtime_key):
96
+ candidate = package_bin / key / binary_name
97
+ if candidate.is_file():
98
+ return candidate
99
+ return None
100
+
101
+
102
+ def target_runtime_env_is_set() -> bool:
103
+ return _first_env(TARGET_RUNTIME_ENVS) is not None
104
+
105
+
106
+ def target_runtime_key() -> str:
107
+ override = _first_env(TARGET_RUNTIME_ENVS)
108
+ if override:
109
+ return canonical_runtime_key(override)
110
+ return host_runtime_key()
111
+
112
+
113
+ def target_wheel_tag(runtime_key: str) -> str:
114
+ override = _first_env(TARGET_WHEEL_TAG_ENVS)
115
+ if override:
116
+ return override.strip().replace("-", "_").replace(".", "_")
117
+ return DEFAULT_WHEEL_TAGS[canonical_runtime_key(runtime_key)]
118
+
119
+
120
+ def binary_name_for_runtime(runtime_key: str) -> str:
121
+ if canonical_runtime_key(runtime_key).startswith("windows-"):
122
+ return f"{RUST_BINARY_NAME}.exe"
123
+ return RUST_BINARY_NAME
124
+
125
+
126
+ def runtime_key_candidates(runtime_key: str) -> list[str]:
127
+ canonical = canonical_runtime_key(runtime_key)
128
+ candidates = [canonical]
129
+ for alias, target in RUNTIME_ALIASES.items():
130
+ if target == canonical and alias not in candidates:
131
+ candidates.append(alias)
132
+ return candidates
133
+
134
+
135
+ def canonical_runtime_key(key: str) -> str:
136
+ normalized = key.strip().lower().replace("_", "-")
137
+ normalized = normalized.replace("linux-x86-64", "linux-x86_64")
138
+ normalized = normalized.replace("macos-x86-64", "macos-x86_64")
139
+ normalized = normalized.replace("windows-x86-64", "windows-x86_64")
140
+ normalized = normalized.replace("darwin-x86-64", "darwin-x86_64")
141
+ normalized = RUNTIME_ALIASES.get(normalized, normalized)
142
+ if normalized not in SUPPORTED_RUNTIME_KEYS:
143
+ raise ValueError(
144
+ f"unsupported runtime key {key!r}; expected one of: "
145
+ + ", ".join(SUPPORTED_RUNTIME_KEYS)
146
+ )
147
+ return normalized
148
+
149
+
150
+ def _first_env(names: tuple[str, ...]) -> str | None:
151
+ for name in names:
152
+ value = os.environ.get(name)
153
+ if value:
154
+ return value
155
+ return None
156
+
157
+
158
+ def host_runtime_key() -> str:
159
+ platform_tag = sysconfig.get_platform().replace(".", "_").replace("-", "_").lower()
160
+ if platform_tag.startswith("linux"):
161
+ if "aarch64" in platform_tag or "arm64" in platform_tag:
162
+ return "linux-aarch64"
163
+ return "linux-x86_64"
164
+ if platform_tag.startswith("macos"):
165
+ if "arm64" in platform_tag or "aarch64" in platform_tag:
166
+ return "macos-arm64"
167
+ return "macos-x86_64"
168
+ if platform_tag.startswith("win") or platform_tag.startswith("windows"):
169
+ if "arm64" in platform_tag or "aarch64" in platform_tag:
170
+ return "windows-arm64"
171
+ return "windows-x86_64"
172
+ return canonical_runtime_key(platform_tag.replace("_", "-"))
173
+
174
+
175
+ def runtime_key_for_system(system: str, machine: str) -> str:
176
+ """Compatibility helper for older tests/importers."""
177
+
178
+ normalized_system = (system or "").strip().lower()
179
+ normalized_machine = (machine or "").strip().lower().replace("_", "-")
180
+ os_key = {
181
+ "linux": "linux",
182
+ "darwin": "macos",
183
+ "macos": "macos",
184
+ "windows": "windows",
185
+ "mingw": "windows",
186
+ "msys": "windows",
187
+ "cygwin": "windows",
188
+ }.get(normalized_system, normalized_system)
189
+ arch_key = {
190
+ "amd64": "x86_64",
191
+ "x64": "x86_64",
192
+ "x86-64": "x86_64",
193
+ "x86_64": "x86_64",
194
+ "arm64": "arm64",
195
+ "aarch64": "aarch64" if os_key == "linux" else "arm64",
196
+ }.get(normalized_machine, normalized_machine)
197
+ return canonical_runtime_key(f"{os_key}-{arch_key}")
198
+
199
+
200
+ def _platform_tag() -> str:
201
+ """Compatibility helper for earlier tests/importers."""
202
+
203
+ return target_runtime_key()
@@ -0,0 +1,85 @@
1
+ [project]
2
+ name = "codetool-shell"
3
+ version = "0.1.1"
4
+ description = "Compress shell output text with Python and optional Rust backends"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "drod", email = "naceur.bs@gmail.com" }
8
+ ]
9
+ maintainers = [
10
+ { name = "drod", email = "naceur.bs@gmail.com" }
11
+ ]
12
+ requires-python = ">=3.9"
13
+ keywords = [
14
+ "agent",
15
+ "compression",
16
+ "developer-tools",
17
+ "llm",
18
+ "rust",
19
+ "shell",
20
+ "terminal",
21
+ ]
22
+ classifiers = [
23
+ "Development Status :: 3 - Alpha",
24
+ "Intended Audience :: Developers",
25
+ "Operating System :: MacOS",
26
+ "Operating System :: Microsoft :: Windows",
27
+ "Operating System :: POSIX :: Linux",
28
+ "Programming Language :: Python :: 3",
29
+ "Programming Language :: Python :: 3 :: Only",
30
+ "Programming Language :: Rust",
31
+ "Topic :: Software Development",
32
+ "Topic :: Software Development :: Libraries :: Python Modules",
33
+ "Topic :: System :: Shells",
34
+ "Topic :: Text Processing",
35
+ "Typing :: Typed",
36
+ ]
37
+ dependencies = []
38
+
39
+ [project.urls]
40
+ Homepage = "https://github.com/pbi-agent/codetool-shell"
41
+ Repository = "https://github.com/pbi-agent/codetool-shell"
42
+ Issues = "https://github.com/pbi-agent/codetool-shell/issues"
43
+ Changelog = "https://github.com/pbi-agent/codetool-shell/releases"
44
+
45
+ [dependency-groups]
46
+ dev = [
47
+ "hatchling>=1.27",
48
+ "pytest>=8,<9",
49
+ ]
50
+
51
+ [tool.pytest.ini_options]
52
+ testpaths = ["tests"]
53
+
54
+ [tool.hatch.build.targets.wheel]
55
+ packages = ["src/codetool_shell"]
56
+ exclude = ["src/codetool_shell/bin/**"]
57
+
58
+ [tool.hatch.build.targets.wheel.hooks.custom]
59
+ path = "hatch_build.py"
60
+
61
+ [tool.hatch.build.targets.sdist]
62
+ include = [
63
+ "src/**",
64
+ "tests/**",
65
+ "benchmarks/**",
66
+ "scripts/**",
67
+ "rust/Cargo.toml",
68
+ "rust/Cargo.lock",
69
+ "rust/src/**",
70
+ "hatch_build.py",
71
+ "README.md",
72
+ "pyproject.toml",
73
+ "uv.lock",
74
+ ]
75
+ exclude = [
76
+ "src/codetool_shell/bin/**",
77
+ "**/__pycache__/**",
78
+ "**/*.py[cod]",
79
+ "rust/target/**",
80
+ "dist/**",
81
+ ]
82
+
83
+ [build-system]
84
+ requires = ["hatchling>=1.27"]
85
+ build-backend = "hatchling.build"