local-terminal-summarizer 0.7.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. local_terminal_summarizer-0.7.0/CONTRIBUTING.md +84 -0
  2. local_terminal_summarizer-0.7.0/LICENSE +21 -0
  3. local_terminal_summarizer-0.7.0/MANIFEST.in +11 -0
  4. local_terminal_summarizer-0.7.0/PKG-INFO +424 -0
  5. local_terminal_summarizer-0.7.0/README.md +390 -0
  6. local_terminal_summarizer-0.7.0/ROADMAP.md +77 -0
  7. local_terminal_summarizer-0.7.0/SECURITY.md +104 -0
  8. local_terminal_summarizer-0.7.0/config.example.toml +149 -0
  9. local_terminal_summarizer-0.7.0/pyproject.toml +62 -0
  10. local_terminal_summarizer-0.7.0/setup.cfg +4 -0
  11. local_terminal_summarizer-0.7.0/src/local_terminal_summarizer.egg-info/PKG-INFO +424 -0
  12. local_terminal_summarizer-0.7.0/src/local_terminal_summarizer.egg-info/SOURCES.txt +116 -0
  13. local_terminal_summarizer-0.7.0/src/local_terminal_summarizer.egg-info/dependency_links.txt +1 -0
  14. local_terminal_summarizer-0.7.0/src/local_terminal_summarizer.egg-info/entry_points.txt +2 -0
  15. local_terminal_summarizer-0.7.0/src/local_terminal_summarizer.egg-info/requires.txt +14 -0
  16. local_terminal_summarizer-0.7.0/src/local_terminal_summarizer.egg-info/top_level.txt +1 -0
  17. local_terminal_summarizer-0.7.0/src/summarizer/__init__.py +15 -0
  18. local_terminal_summarizer-0.7.0/src/summarizer/__main__.py +8 -0
  19. local_terminal_summarizer-0.7.0/src/summarizer/aggregation/__init__.py +18 -0
  20. local_terminal_summarizer-0.7.0/src/summarizer/aggregation/mapreduce.py +379 -0
  21. local_terminal_summarizer-0.7.0/src/summarizer/aggregation/refine.py +14 -0
  22. local_terminal_summarizer-0.7.0/src/summarizer/aggregation/strategies.py +34 -0
  23. local_terminal_summarizer-0.7.0/src/summarizer/cache/__init__.py +19 -0
  24. local_terminal_summarizer-0.7.0/src/summarizer/cache/cli.py +55 -0
  25. local_terminal_summarizer-0.7.0/src/summarizer/cache/store.py +376 -0
  26. local_terminal_summarizer-0.7.0/src/summarizer/cancel.py +49 -0
  27. local_terminal_summarizer-0.7.0/src/summarizer/chunking/__init__.py +46 -0
  28. local_terminal_summarizer-0.7.0/src/summarizer/chunking/budget.py +136 -0
  29. local_terminal_summarizer-0.7.0/src/summarizer/chunking/splitter.py +361 -0
  30. local_terminal_summarizer-0.7.0/src/summarizer/chunking/strategies.py +284 -0
  31. local_terminal_summarizer-0.7.0/src/summarizer/chunking/token.py +24 -0
  32. local_terminal_summarizer-0.7.0/src/summarizer/cli.py +660 -0
  33. local_terminal_summarizer-0.7.0/src/summarizer/config.py +792 -0
  34. local_terminal_summarizer-0.7.0/src/summarizer/doctor.py +548 -0
  35. local_terminal_summarizer-0.7.0/src/summarizer/document/__init__.py +16 -0
  36. local_terminal_summarizer-0.7.0/src/summarizer/document/metadata.py +32 -0
  37. local_terminal_summarizer-0.7.0/src/summarizer/document/model.py +107 -0
  38. local_terminal_summarizer-0.7.0/src/summarizer/document/reader.py +183 -0
  39. local_terminal_summarizer-0.7.0/src/summarizer/document/tables.py +87 -0
  40. local_terminal_summarizer-0.7.0/src/summarizer/engine/__init__.py +33 -0
  41. local_terminal_summarizer-0.7.0/src/summarizer/engine/backends.py +158 -0
  42. local_terminal_summarizer-0.7.0/src/summarizer/engine/base.py +111 -0
  43. local_terminal_summarizer-0.7.0/src/summarizer/engine/capabilities.py +155 -0
  44. local_terminal_summarizer-0.7.0/src/summarizer/engine/client.py +465 -0
  45. local_terminal_summarizer-0.7.0/src/summarizer/engine/factory.py +64 -0
  46. local_terminal_summarizer-0.7.0/src/summarizer/engine/ollama.py +291 -0
  47. local_terminal_summarizer-0.7.0/src/summarizer/engine/openai.py +426 -0
  48. local_terminal_summarizer-0.7.0/src/summarizer/errors.py +130 -0
  49. local_terminal_summarizer-0.7.0/src/summarizer/evaluation/__init__.py +47 -0
  50. local_terminal_summarizer-0.7.0/src/summarizer/evaluation/cases.py +235 -0
  51. local_terminal_summarizer-0.7.0/src/summarizer/evaluation/cli.py +90 -0
  52. local_terminal_summarizer-0.7.0/src/summarizer/evaluation/report.py +146 -0
  53. local_terminal_summarizer-0.7.0/src/summarizer/evaluation/runner.py +238 -0
  54. local_terminal_summarizer-0.7.0/src/summarizer/introspect.py +341 -0
  55. local_terminal_summarizer-0.7.0/src/summarizer/log.py +92 -0
  56. local_terminal_summarizer-0.7.0/src/summarizer/net.py +51 -0
  57. local_terminal_summarizer-0.7.0/src/summarizer/output/__init__.py +24 -0
  58. local_terminal_summarizer-0.7.0/src/summarizer/output/atomic.py +53 -0
  59. local_terminal_summarizer-0.7.0/src/summarizer/output/json.py +188 -0
  60. local_terminal_summarizer-0.7.0/src/summarizer/output/stream.py +41 -0
  61. local_terminal_summarizer-0.7.0/src/summarizer/pipeline.py +959 -0
  62. local_terminal_summarizer-0.7.0/src/summarizer/profiles/__init__.py +21 -0
  63. local_terminal_summarizer-0.7.0/src/summarizer/profiles/loader.py +138 -0
  64. local_terminal_summarizer-0.7.0/src/summarizer/profiles/prompts/__reduce__.md +20 -0
  65. local_terminal_summarizer-0.7.0/src/summarizer/profiles/prompts/academic.md +16 -0
  66. local_terminal_summarizer-0.7.0/src/summarizer/profiles/prompts/code.md +22 -0
  67. local_terminal_summarizer-0.7.0/src/summarizer/profiles/prompts/meeting.md +22 -0
  68. local_terminal_summarizer-0.7.0/src/summarizer/profiles/prompts/plain.md +19 -0
  69. local_terminal_summarizer-0.7.0/src/summarizer/progress.py +165 -0
  70. local_terminal_summarizer-0.7.0/src/summarizer/prompt/__init__.py +16 -0
  71. local_terminal_summarizer-0.7.0/src/summarizer/prompt/builder.py +269 -0
  72. local_terminal_summarizer-0.7.0/src/summarizer/prompt/identity.py +64 -0
  73. local_terminal_summarizer-0.7.0/src/summarizer/readers/__init__.py +13 -0
  74. local_terminal_summarizer-0.7.0/src/summarizer/readers/code.py +58 -0
  75. local_terminal_summarizer-0.7.0/src/summarizer/readers/markdown.py +36 -0
  76. local_terminal_summarizer-0.7.0/src/summarizer/readers/pdf.py +186 -0
  77. local_terminal_summarizer-0.7.0/src/summarizer/readers/stdin.py +100 -0
  78. local_terminal_summarizer-0.7.0/src/summarizer/readers/text.py +49 -0
  79. local_terminal_summarizer-0.7.0/src/summarizer/session/__init__.py +44 -0
  80. local_terminal_summarizer-0.7.0/src/summarizer/session/digest.py +155 -0
  81. local_terminal_summarizer-0.7.0/src/summarizer/session/lock.py +64 -0
  82. local_terminal_summarizer-0.7.0/src/summarizer/session/manager.py +448 -0
  83. local_terminal_summarizer-0.7.0/src/summarizer/session/run.py +135 -0
  84. local_terminal_summarizer-0.7.0/src/summarizer/update/__init__.py +19 -0
  85. local_terminal_summarizer-0.7.0/src/summarizer/update/install.py +62 -0
  86. local_terminal_summarizer-0.7.0/src/summarizer/update/manager.py +116 -0
  87. local_terminal_summarizer-0.7.0/src/summarizer/update/release.py +95 -0
  88. local_terminal_summarizer-0.7.0/src/summarizer/update/verify.py +60 -0
  89. local_terminal_summarizer-0.7.0/src/summarizer/verify.py +133 -0
  90. local_terminal_summarizer-0.7.0/tests/test_adversarial_corpus.py +336 -0
  91. local_terminal_summarizer-0.7.0/tests/test_backends.py +203 -0
  92. local_terminal_summarizer-0.7.0/tests/test_cache_checkpoints.py +279 -0
  93. local_terminal_summarizer-0.7.0/tests/test_cancellation.py +228 -0
  94. local_terminal_summarizer-0.7.0/tests/test_chunk_budget_limits.py +104 -0
  95. local_terminal_summarizer-0.7.0/tests/test_concurrency.py +170 -0
  96. local_terminal_summarizer-0.7.0/tests/test_config_layering.py +263 -0
  97. local_terminal_summarizer-0.7.0/tests/test_context_overflow.py +215 -0
  98. local_terminal_summarizer-0.7.0/tests/test_endpoint_scope.py +152 -0
  99. local_terminal_summarizer-0.7.0/tests/test_evaluation.py +181 -0
  100. local_terminal_summarizer-0.7.0/tests/test_introspection.py +143 -0
  101. local_terminal_summarizer-0.7.0/tests/test_long_documents.py +183 -0
  102. local_terminal_summarizer-0.7.0/tests/test_model_discovery.py +82 -0
  103. local_terminal_summarizer-0.7.0/tests/test_ollama_warmup.py +125 -0
  104. local_terminal_summarizer-0.7.0/tests/test_output_behavior.py +269 -0
  105. local_terminal_summarizer-0.7.0/tests/test_packaging.py +60 -0
  106. local_terminal_summarizer-0.7.0/tests/test_pipeline_and_cli.py +366 -0
  107. local_terminal_summarizer-0.7.0/tests/test_progress.py +111 -0
  108. local_terminal_summarizer-0.7.0/tests/test_prompt_identity.py +54 -0
  109. local_terminal_summarizer-0.7.0/tests/test_provenance.py +57 -0
  110. local_terminal_summarizer-0.7.0/tests/test_readers_v3.py +303 -0
  111. local_terminal_summarizer-0.7.0/tests/test_reduce_limits.py +110 -0
  112. local_terminal_summarizer-0.7.0/tests/test_repo_guard.py +195 -0
  113. local_terminal_summarizer-0.7.0/tests/test_response_caps.py +141 -0
  114. local_terminal_summarizer-0.7.0/tests/test_security.py +240 -0
  115. local_terminal_summarizer-0.7.0/tests/test_sessions.py +550 -0
  116. local_terminal_summarizer-0.7.0/tests/test_stream_timeouts.py +128 -0
  117. local_terminal_summarizer-0.7.0/tests/test_tables_and_verify.py +183 -0
  118. local_terminal_summarizer-0.7.0/tests/test_truncation.py +295 -0
@@ -0,0 +1,84 @@
1
+ # Contributing
2
+
3
+ Thanks for helping. This is a local-first, Unix-native tool: keep changes
4
+ small, explicit, and testable without a model server.
5
+
6
+ ## Development setup
7
+
8
+ ```sh
9
+ python3 -m venv .venv
10
+ .venv/bin/python -m pip install -e '.[test]'
11
+ ```
12
+
13
+ The base install has zero runtime dependencies; `[test]` adds pytest and
14
+ pypdf so the PDF reader tests can run. Nothing downloads a model.
15
+
16
+ ## Before you push
17
+
18
+ ```sh
19
+ .venv/bin/python -m pytest # full suite, no network, no Ollama
20
+ .venv/bin/python -m compileall -q src tests evaluation scripts
21
+ .venv/bin/python scripts/check_repo_safety.py
22
+ .venv/bin/python -m build --no-isolation # optional: sdist + wheel
23
+ git diff --check
24
+ ```
25
+
26
+ CI runs pytest and `compileall` on Python 3.11 and 3.14, the repository
27
+ safety guard, local CLI smoke tests, and a packaging job that builds the
28
+ distributions. It never needs Ollama, a GPU, or model access. Tests must bind
29
+ and connect only to loopback (`tests/conftest.py` enforces this).
30
+
31
+ ## Real-model validation (manual, optional)
32
+
33
+ When a local Ollama is available, sanity-check against a real model:
34
+
35
+ ```sh
36
+ .venv/bin/summarize doctor
37
+ .venv/bin/summarize models
38
+ .venv/bin/summarize evaluate --model qwen3:8b --tags core
39
+ ```
40
+
41
+ `evaluate --dry-run` is the CI-safe path; the rest is manual integration, not
42
+ required for a contribution to be accepted.
43
+
44
+ ## Ground rules
45
+
46
+ - **Backend names stay out of the pipeline.** If behaviour differs between
47
+ runtimes, put it in an adapter or express it as a capability in
48
+ `engine/capabilities.py`; never add `if backend == ...` to the pipeline.
49
+ - **Adding a local backend** is a `BackendSpec` entry in
50
+ `engine/backends.py` plus, only when needed, one small adapter class. No
51
+ cloud providers.
52
+ - **Untrusted data stays untrusted.** Document text, reader metadata, and
53
+ map-stage interim summaries are always wrapped in a boundary that is
54
+ checked against the content. The reduce instructions and interim summaries
55
+ must remain structurally separate. `--context` is the only user-trusted
56
+ input and is still boundary-checked.
57
+ - **Bounded output.** Every generation request carries `max_tokens`; never
58
+ ship a path that can generate unbounded output.
59
+ - **Local only.** No telemetry, analytics, cloud endpoints, remote URLs in
60
+ normal runtime behaviour, remote log upload, or model downloads. Test
61
+ servers bind `127.0.0.1`.
62
+ - **No secrets in the repo.** Keep real config in
63
+ `~/.config/summarizer/config.toml`; `config.example.toml` is placeholders
64
+ only. The safety guard allows exceptions only with a written reason.
65
+
66
+ ## Scope
67
+
68
+ Out of scope unless a concrete need appears: GUI, URL fetching, cloud models,
69
+ agents, tool execution, embeddings/vector databases, self-update, background
70
+ services, automatic model installation, multi-model ensemble/routing, and
71
+ refine-based aggregation. See `ROADMAP.md`.
72
+
73
+ ## Tests
74
+
75
+ Prefer in-process test engines (see `tests/test_pipeline_and_cli.py`) over a
76
+ live server. Structural security guarantees belong in
77
+ `tests/test_adversarial_corpus.py`; evaluation behaviour in
78
+ `tests/test_evaluation.py`; configuration precedence in
79
+ `tests/test_config_layering.py`; cancellation in `tests/test_cancellation.py`;
80
+ concurrency in `tests/test_concurrency.py`; caching/checkpoints and prompt
81
+ identity in `tests/test_cache_checkpoints.py` and
82
+ `tests/test_prompt_identity.py`; progress in `tests/test_progress.py`; output
83
+ and stats in `tests/test_output_behavior.py`; packaging/version in
84
+ `tests/test_packaging.py`.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 summarizer contributors
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 all
13
+ 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 THE
21
+ SOFTWARE.
@@ -0,0 +1,11 @@
1
+ # Source-distribution contents. The wheel already carries the package and its
2
+ # prompt data (see [tool.setuptools.package-data]); this keeps the docs and the
3
+ # committed example config in the sdist as well.
4
+ include README.md
5
+ include SECURITY.md
6
+ include ROADMAP.md
7
+ include CONTRIBUTING.md
8
+ include LICENSE
9
+ include config.example.toml
10
+ include pyproject.toml
11
+ recursive-include src/summarizer/profiles/prompts *.md
@@ -0,0 +1,424 @@
1
+ Metadata-Version: 2.4
2
+ Name: local-terminal-summarizer
3
+ Version: 0.7.0
4
+ Summary: A local-first, Unix-native terminal summarizer for local LLM servers.
5
+ Author: summarizer contributors
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/adigaiki/local-terminal-summarizer
8
+ Project-URL: Repository, https://github.com/adigaiki/local-terminal-summarizer
9
+ Project-URL: Issues, https://github.com/adigaiki/local-terminal-summarizer/issues
10
+ Keywords: summarize,llm,openai-compatible,local-first,cli
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Environment :: Console
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Operating System :: POSIX :: Linux
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Topic :: Text Processing
18
+ Classifier: Topic :: Utilities
19
+ Requires-Python: >=3.11
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Provides-Extra: pdf
23
+ Requires-Dist: pypdf>=4.0; extra == "pdf"
24
+ Requires-Dist: fonttools>=4.0; extra == "pdf"
25
+ Provides-Extra: ocr
26
+ Requires-Dist: pypdf>=4.0; extra == "ocr"
27
+ Requires-Dist: fonttools>=4.0; extra == "ocr"
28
+ Requires-Dist: pytesseract>=0.3.10; extra == "ocr"
29
+ Requires-Dist: pdf2image>=1.16; extra == "ocr"
30
+ Provides-Extra: test
31
+ Requires-Dist: pytest>=8.0; extra == "test"
32
+ Requires-Dist: pypdf>=4.0; extra == "test"
33
+ Dynamic: license-file
34
+
35
+ # summarizer
36
+
37
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
38
+ [![Python 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](https://www.python.org/)
39
+ [![CI](https://github.com/adigaiki/local-terminal-summarizer/actions/workflows/ci.yml/badge.svg)](https://github.com/adigaiki/local-terminal-summarizer/actions/workflows/ci.yml)
40
+
41
+ `summarize` is a Linux-and-macOS command-line summarizer that turns a file or
42
+ stdin into a concise summary using a **local** LLM server you already run
43
+ (Ollama, llama.cpp, LM Studio, or any OpenAI-compatible server). It is a Unix
44
+ tool: pipe a document in, get a summary on stdout, with long-document chunking,
45
+ a trusted/untrusted input boundary, stable JSON output, and diagnostics.
46
+
47
+ Nothing leaves your machine by default: every request goes to a model server
48
+ over loopback (`http://localhost:11434`). Two opt-ins can produce other
49
+ traffic: `--check-update` (one request to a URL you configure in `[update]`),
50
+ and a non-loopback `endpoint`, which is refused unless you set
51
+ `[engine] allow_non_loopback`. See [docs/security.md](docs/security.md).
52
+
53
+ - MIT-licensed, Python 3.11+, zero runtime dependencies in the core install
54
+ (PDF and OCR are optional extras). No cloud, no telemetry, no model download,
55
+ no self-update.
56
+
57
+ ## Why not just `ollama run`?
58
+
59
+ `ollama run` prints whatever the model says, with no structure around it.
60
+ `summarize` wraps the same local model with the things a summarizer needs:
61
+ documents longer than the context window are chunked and reduced; the document
62
+ is wrapped in a fresh random boundary and treated as untrusted data while
63
+ `--context` stays trusted; `--format json` gives a stable envelope with chunk
64
+ provenance; and `summarize doctor` tells you what is reachable, configured, and
65
+ missing. It is one small tool, not a chat client.
66
+
67
+ ## Example
68
+
69
+ Input (`docs/examples/cache-incident.md`, an original sample), summarized with
70
+ the default `plain` profile on `qwen2.5-3b-16k` (Ollama 0.35.0):
71
+
72
+ ```console
73
+ $ summarize docs/examples/cache-incident.md
74
+ # Cache incident review: Stale Prices During Node Replacement
75
+
76
+ On 14 March, at 02:40, the night-shift on-call engineer replaced a failing cache node in the `pricing` cluster. The node had been experiencing intermittent timeouts for about twenty minutes, and the replacement was treated as routine. During the swap, the pricing API served roughly four percent of requests from a replica that had not yet caught up. This resulted in some customers seeing prices that were up to an hour old for about three and a half minutes. No orders were placed at the stale prices, as the checkout service re-quotes against the primary node before authorization, and the seven orders that arrived during the window were priced from fresh data.
77
+
78
+ The on-call engineer noticed a rise in stale-read metrics at 02:52 and rolled the replica back at 03:05. Error rates were normal by 03:20, and the stale-read rate returned to its baseline shortly afterwards.
79
+
80
+ … (excerpt; the full output also covers impact, cause, and follow-up actions)
81
+ ```
82
+
83
+ Output varies from run to run and between models. The `--format json` envelope
84
+ and the full schema are in
85
+ [docs/readers.md](docs/readers.md#provenance-and-the-json-schema).
86
+
87
+ ## Table of contents
88
+
89
+ - [Why not just `ollama run`?](#why-not-just-ollama-run)
90
+ - [Example](#example)
91
+ - [Requirements](#requirements)
92
+ - [Install](#install)
93
+ - [Quick start (Ollama)](#quick-start-ollama)
94
+ - [Common usage](#common-usage)
95
+ - [Supported backends](#supported-backends)
96
+ - [Supported inputs](#supported-inputs)
97
+ - [Configuration](#configuration)
98
+ - [Profiles](#profiles)
99
+ - [Safety: trusted vs. untrusted input](#safety-trusted-vs-untrusted-input)
100
+ - [Verifying a summary](#verifying-a-summary)
101
+ - [Output and the JSON schema](#output-and-the-json-schema)
102
+ - [Local state](#local-state)
103
+ - [Commands](#commands)
104
+ - [Evaluation is mechanical](#evaluation-is-mechanical)
105
+ - [Speed and system load](#speed-and-system-load)
106
+ - [Troubleshooting](#troubleshooting)
107
+ - [When not to use this](#when-not-to-use-this)
108
+ - [How this differs from other tools](#how-this-differs-from-other-tools)
109
+ - [Documentation](#documentation)
110
+ - [Contributing, changelog, license](#contributing-changelog-license)
111
+
112
+ ## Requirements
113
+
114
+ - **Python 3.11 or newer.**
115
+ - **A local model server** with at least one model pulled. The default backend
116
+ is [Ollama](https://ollama.com/); see [Quick start](#quick-start-ollama).
117
+ - **A model that fits your machine.** A 3B–8B instruct model is plenty. A Q4
118
+ model needs roughly its download size of RAM; if it fits in GPU memory it is
119
+ fast, and if it spills to CPU it can drop to ~1 token/second. `ollama ps`
120
+ shows the CPU/GPU split and the serving window.
121
+ - **Platforms:** tested in CI on **Linux** and **macOS** (both run the full
122
+ suite); WSL is expected to work. The code uses only POSIX APIs (`flock`,
123
+ signals). Native Windows is unsupported.
124
+
125
+ ## Install
126
+
127
+ Not on PyPI yet (planned distribution name: `local-terminal-summarizer`).
128
+ Install from a checkout with
129
+ [pipx](https://pipx.pypa.io/) (isolated, puts `summarize` on your `PATH`):
130
+
131
+ ```sh
132
+ git clone https://github.com/adigaiki/local-terminal-summarizer.git
133
+ cd local-terminal-summarizer
134
+ pipx install . # or: uv tool install .
135
+ ```
136
+
137
+ From a virtualenv instead: `python3 -m venv .venv &&
138
+ .venv/bin/python -m pip install -e '.[test]'`. Optional extras (the base
139
+ install stays dependency-free): `pipx install '.[pdf]'` for PDF text and
140
+ `pipx install '.[ocr]'` for OCR (which also needs system `tesseract` and
141
+ Poppler). Uninstall with `pipx uninstall local-terminal-summarizer`.
142
+
143
+ Releases, packaging, and TestPyPI-first publishing are in
144
+ [docs/installation.md](docs/installation.md).
145
+
146
+ ## Quick start (Ollama)
147
+
148
+ ```sh
149
+ # 1. Install Ollama (Linux) and start it: https://ollama.com/download
150
+ curl -fsSL https://ollama.com/install.sh | sh
151
+
152
+ # 2. Pull a model that fits your machine.
153
+ ollama pull qwen2.5:3b
154
+
155
+ # 3. Check the whole setup before you trust it.
156
+ summarize doctor
157
+ ```
158
+
159
+ `summarize doctor` is the first thing to run when anything is wrong. It checks
160
+ configuration, whether the backend is reachable, whether the configured model
161
+ exists, the context window and where it came from, and which optional extras
162
+ are installed. A healthy run looks like this (with the custom 16k variant from
163
+ the note below configured):
164
+
165
+ ```text
166
+ ✓ Backend reachable — http://localhost:11434
167
+ ✓ Model available — configured model 'qwen2.5-3b-16k' is installed
168
+ ✓ Context window known — 16384 tokens (server)
169
+ ! OCR — optional, not installed
170
+
171
+ Everything looks good.
172
+ ```
173
+
174
+ The full output, and what a failure looks like, are in
175
+ [docs/troubleshooting.md](docs/troubleshooting.md#what-doctor-prints).
176
+
177
+ **The window `doctor` reports is state-dependent.** With the model *unloaded*,
178
+ Ollama answers with the model's **trained maximum** (`/api/show`); once it is
179
+ loaded it serves a smaller window, visible as the `CONTEXT` column of
180
+ `ollama ps`. A plain `ollama pull qwen2.5:3b` reports a trained maximum of
181
+ 32768 while a 4 GiB GPU serves 4096. A normal run warms the model with one
182
+ tiny request and re-reads the *served* window before planning, so it does not
183
+ send an over-large prompt; `doctor` never warms anything and shows a `!`
184
+ "Serving context window" line when it only has the trained maximum.
185
+
186
+ To give a model a larger served window, create a derived model (verified):
187
+
188
+ ```sh
189
+ cat > Modelfile <<'EOF'
190
+ FROM qwen2.5:3b
191
+ PARAMETER num_ctx 16384
192
+ EOF
193
+ ollama create qwen2.5-3b-16k -f Modelfile
194
+ ```
195
+
196
+ Tested against Ollama 0.35.0. The adapter uses `/api/tags` (model list),
197
+ `/api/show` (context), `/api/ps` (serving window of the loaded model), and the
198
+ OpenAI-compatible `/v1/chat/completions` endpoint; `doctor` reports whichever
199
+ of these it can reach.
200
+
201
+ ## Common usage
202
+
203
+ ```sh
204
+ summarize article.md
205
+ git diff | summarize --profile code
206
+ summarize report.pdf --profile academic
207
+ summarize scan.pdf --ocr # OCR a scanned PDF (optional extra)
208
+ summarize notes.txt --format json | jq
209
+ summarize notes.txt -o summary.md
210
+ summarize incident.txt --context glossary.md # glossary.md is trusted, incident.txt stays untrusted
211
+ summarize notes.txt --dry-run # plan only; contacts no backend
212
+ summarize --stats article.md # timing and token counts on stderr
213
+ summarize paper.pdf --verify # flag summary numbers/expansions absent from the source
214
+ summarize doctor # environment diagnostics
215
+ ```
216
+
217
+ `summarize -` reads stdin explicitly. With no input argument and a TTY stdin,
218
+ `summarize` reports an error instead of waiting forever. Omitting `--profile`
219
+ gives the default profile, **`plain`** (see [Profiles](#profiles)).
220
+
221
+ **One input per invocation.** `summarize a.md b.md` is a usage error; batch
222
+ with the shell:
223
+
224
+ ```sh
225
+ for f in docs/*.md; do summarize "$f" -o "${f%.md}.summary.md"; done
226
+ # or: printf '%s\0' docs/*.md | xargs -0 -n1 summarize
227
+ ```
228
+
229
+ ## Supported backends
230
+
231
+ There are no cloud providers. A backend is a local adapter:
232
+
233
+ | `backend` | Default endpoint | Notes |
234
+ | --- | --- | --- |
235
+ | `ollama` | `http://localhost:11434` | native model/context discovery |
236
+ | `openai-compatible` (alias `openai`) | configured | any local OpenAI Chat Completions server — this covers vLLM, text-generation-webui, and other `/v1` servers |
237
+ | `llama.cpp` (alias `llama-cpp`) | `http://localhost:8080` | llama.cpp server |
238
+ | `lmstudio` (alias `lm-studio`) | `http://localhost:1234` | LM Studio |
239
+
240
+ Selecting a backend without setting an endpoint uses its loopback default.
241
+ Capability negotiation (streaming, structured JSON, reasoning control, context
242
+ length) is described in [docs/backends.md](docs/backends.md).
243
+
244
+ ## Supported inputs
245
+
246
+ | Input | Reader | Notes |
247
+ | --- | --- | --- |
248
+ | `-` or no argument | stdin | bounded; a TTY with no input is an error |
249
+ | `.txt`, `.log`, unknown text extensions | text | any file whose content is valid text |
250
+ | `.md`, `.markdown`, `.mdown`, `.mkd` | markdown | preserved verbatim; never rendered or fetched |
251
+ | `.py`, `.rs`, `.c`, `.go`, `.toml`, … | code | passed through unmodified |
252
+ | `.pdf` | PDF (optional extra) | page-aware; scanned PDFs need `--ocr` |
253
+
254
+ There is **no DOCX/HTML/EPUB reader** — convert those to text first. NUL bytes
255
+ are rejected as binary. Details: [docs/readers.md](docs/readers.md).
256
+
257
+ ## Configuration
258
+
259
+ Configuration is layered (later wins): CLI → `SUMMARIZER_*` environment →
260
+ project `./summarizer.toml` (opt-in) → user config → built-in defaults. The
261
+ user config lives at `$XDG_CONFIG_HOME/summarizer/config.toml` or
262
+ `~/.config/summarizer/config.toml`:
263
+
264
+ ```toml
265
+ [engine]
266
+ backend = "ollama"
267
+ endpoint = "http://localhost:11434"
268
+ model = "qwen2.5:3b"
269
+ timeout_seconds = 60
270
+ ```
271
+
272
+ Or override for one run: `SUMMARIZER_MODEL=qwen3:8b summarize article.md`.
273
+ A project `./summarizer.toml` is read only when you opt in
274
+ (`SUMMARIZER_PROJECT_CONFIG=1`). Every key, the precedence rules, the
275
+ environment variables, and a full example are in
276
+ [docs/configuration.md](docs/configuration.md) and
277
+ [config.example.toml](config.example.toml).
278
+
279
+ ## Profiles
280
+
281
+ Built-in profiles: **`plain` (the default)**, `code`, `academic`, and
282
+ `meeting`. Add or override one by dropping `NAME.md` into
283
+ `~/.config/summarizer/prompts/`. `summarize profiles` lists them. See
284
+ [docs/configuration.md](docs/configuration.md#profiles).
285
+
286
+ ## Safety: trusted vs. untrusted input
287
+
288
+ Document text is untrusted: each request wraps it in a fresh random boundary
289
+ checked against the content first, and map-reduce interim summaries get the
290
+ same treatment. `--context FILE` is the one input **you** mark as trusted — it
291
+ is kept in a separate labelled section, never mixed with the document.
292
+
293
+ These are structural guarantees, not a solved problem. See
294
+ [SECURITY.md](SECURITY.md) (threat model and private vulnerability reporting —
295
+ hence two security docs), [docs/design.md](docs/design.md), and
296
+ [docs/security.md](docs/security.md).
297
+
298
+ ## Verifying a summary
299
+
300
+ `--verify` reports numbers and `ACRONYM (expansion)` phrases in the summary
301
+ that do not appear in the source: grep, not a judge model, and advisory (exit
302
+ status unchanged). It does not catch a real number on the wrong label or
303
+ non-numeric fabrication. Details: [docs/verify.md](docs/verify.md).
304
+
305
+ ## Output and the JSON schema
306
+
307
+ Default output is Markdown on stdout. `--format json` emits a stable envelope
308
+ (`document`, `profile`, `engine`, `strategy`, `chunks`, `summary`,
309
+ `chunk_provenance`, `duration_seconds`; `--verify` adds `verification`, and a
310
+ detected truncation adds `truncated`). Schema and example:
311
+ [docs/readers.md](docs/readers.md#provenance-and-the-json-schema). Provenance
312
+ never leaks into plain/Markdown output and never enters trusted prompt
313
+ instructions.
314
+
315
+ ## Local state
316
+
317
+ `summarize` writes nothing by default. Two optional, plain-file stores:
318
+ the **cache** (`summarize cache status|path|clear`; off by default; derived
319
+ summaries only, keyed by content hash + model/profile, under
320
+ `~/.cache/summarizer`, never document text or paths) and **sessions**
321
+ (`summarize session start|end|status`; pointers only, recorded only when
322
+ explicitly selected). See [docs/performance.md](docs/performance.md) and
323
+ [docs/sessions.md](docs/sessions.md).
324
+
325
+ ## Commands
326
+
327
+ | Command | Purpose |
328
+ | --- | --- |
329
+ | `summarize FILE` / `-` | summarize a file or stdin |
330
+ | `summarize doctor` | installation, configuration, backend, model, and cache diagnostics |
331
+ | `summarize models` | models the configured local backend reports |
332
+ | `summarize profiles` | installed profiles and their prompt identity |
333
+ | `summarize config show\|path\|validate` | resolved configuration and locations |
334
+ | `summarize cache status\|path\|clear` | inspect and clear the optional cache (off by default) |
335
+ | `summarize evaluate` | run the local model/backend evaluation corpus |
336
+ | `summarize session start\|end\|status` | group runs under a named local record |
337
+ | `summarize completions bash\|zsh\|fish` | shell completion script |
338
+
339
+ Every option (including `--verify`, `--context`, `--dry-run`, `--stats`,
340
+ `--check-update`, `-o`) and the exit status are in [docs/cli.md](docs/cli.md).
341
+
342
+ ## Evaluation is mechanical
343
+
344
+ `summarize evaluate` applies **mechanical** checks (valid format/JSON, sentinel
345
+ retention, marker absence, strategy, bounds) — not objective quality scores,
346
+ and no judge model. Whether summaries are actually good is addressed, in part
347
+ and with caveats, in [docs/quality.md](docs/quality.md); the harness is
348
+ [docs/evaluation.md](docs/evaluation.md).
349
+
350
+ ## Speed and system load
351
+
352
+ Latency is dominated by whether the model fits your GPU. Measured on a laptop
353
+ with a 4 GiB-VRAM GPU: `qwen2.5-3b-16k` (GPU-resident) summarized a
354
+ 14,000-token paper directly in ~21 s; `qwen3:8b`, which does not fit and spills
355
+ to CPU, runs ~1.2 tok/s. Keeping a document in one direct call is usually
356
+ faster and more coherent than map-reduce. The CLI is GPU-agnostic; the numbers
357
+ and the full picture are in [docs/performance.md](docs/performance.md).
358
+
359
+ ## Troubleshooting
360
+
361
+ - **`connection refused` / `Backend reachable ✗`** — start your model server
362
+ (`ollama serve`, or the llama.cpp/LM Studio server) and confirm the endpoint
363
+ in `summarize doctor`. URL, model, and backend come from config or
364
+ `SUMMARIZER_ENDPOINT` / `SUMMARIZER_MODEL` / `SUMMARIZER_BACKEND`.
365
+ - **`the model is not available on the endpoint`** — pull it (`ollama pull
366
+ qwen2.5:3b`) or set `[engine] model` to an installed name. `summarize models`
367
+ lists what the backend reports.
368
+ - **`the backend processed ... prompt tokens ... truncated`** (or
369
+ `"truncated": true`) — the prompt was likely truncated; see
370
+ [docs/troubleshooting.md](docs/troubleshooting.md) for the fix and the
371
+ streaming caveat.
372
+
373
+ Everything else — PDFs and OCR, timeouts, context overflow, binary files, the
374
+ no-input error — is in [docs/troubleshooting.md](docs/troubleshooting.md).
375
+
376
+ ## When not to use this
377
+
378
+ `summarize` produces a probabilistic summary from a local model. It is the
379
+ wrong tool when being wrong is expensive:
380
+
381
+ - **Where a wrong number or claim matters** (legal, medical, financial,
382
+ safety-critical). `--verify` is a grep, not fact-checking, and a clean report
383
+ is not proof; treat `--format json` as untrusted at any automation boundary.
384
+ - **Wide tables on a small model.** Table structuring fixes column
385
+ *assignment*, not judgment; a 3B model can still garble an 8-column table.
386
+ - **Documents needing reasoning across the whole text.** Map-reduce summarizes
387
+ chunks independently, so a term defined in one part and used far away can be
388
+ lost; prefer a model whose context holds the document in one call
389
+ (`--dry-run` shows the plan, `--strict` refuses to chunk).
390
+ - **Content you would not send to a local model.** The tool does not redact;
391
+ the model sees the document.
392
+
393
+ The honest state of summary quality is in [docs/quality.md](docs/quality.md).
394
+
395
+ ## How this differs from other tools
396
+
397
+ Not better — different. `summarize` is a summarization pipeline with an
398
+ explicit trust boundary, strict context budgeting, and validated output;
399
+ general-purpose tools may fit better if you need chat, plugins, or cloud
400
+ models. Claims below are from each project's own README.
401
+
402
+ | Tool | What it is | How `summarize` differs |
403
+ | --- | --- | --- |
404
+ | `ollama run` | One prompt against a local model | No chunking, budgeting, trust boundary, or output validation |
405
+ | [`llm`](https://github.com/simonw/llm) | CLI/Python for many models, remote and local (Ollama via a plugin), with SQLite logging, embeddings, schemas and tools | General-purpose; long-document summarization is on you |
406
+ | [`fabric`](https://github.com/danielmiessler/fabric) | A library of reusable prompts ("Patterns", including `summarize`) over many providers, with a REST API | Prompt-library-first; `summarize` is pipeline-first and local-only |
407
+
408
+ ## Documentation
409
+
410
+ The README is the orientation; the detail lives in [docs/](docs/index.md):
411
+ [Installation](docs/installation.md) · [Configuration](docs/configuration.md) ·
412
+ [CLI](docs/cli.md) · [Backends](docs/backends.md) ·
413
+ [Performance and local state](docs/performance.md) · [Readers](docs/readers.md) ·
414
+ [Claim verification](docs/verify.md) · [Troubleshooting](docs/troubleshooting.md) ·
415
+ [Sessions](docs/sessions.md) · [Design and safety](docs/design.md) ·
416
+ [Security and privacy](docs/security.md) · [Evaluation](docs/evaluation.md) ·
417
+ [Summary quality](docs/quality.md).
418
+
419
+ ## Contributing, changelog, license
420
+
421
+ - Contributing and the repository safety guard: [CONTRIBUTING.md](CONTRIBUTING.md).
422
+ - Version history: [CHANGELOG.md](CHANGELOG.md).
423
+ - Design notes and deferred work: [ROADMAP.md](ROADMAP.md).
424
+ - License: MIT — see [LICENSE](LICENSE).