truthlint 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 (57) hide show
  1. truthlint-0.1.0/LICENSE +21 -0
  2. truthlint-0.1.0/PKG-INFO +281 -0
  3. truthlint-0.1.0/README.md +249 -0
  4. truthlint-0.1.0/pyproject.toml +78 -0
  5. truthlint-0.1.0/setup.cfg +4 -0
  6. truthlint-0.1.0/src/truthlint/__init__.py +10 -0
  7. truthlint-0.1.0/src/truthlint/__main__.py +4 -0
  8. truthlint-0.1.0/src/truthlint/cli.py +408 -0
  9. truthlint-0.1.0/src/truthlint/config.py +57 -0
  10. truthlint-0.1.0/src/truthlint/context.py +440 -0
  11. truthlint-0.1.0/src/truthlint/engine.py +131 -0
  12. truthlint-0.1.0/src/truthlint/fixes.py +132 -0
  13. truthlint-0.1.0/src/truthlint/ignore.py +129 -0
  14. truthlint-0.1.0/src/truthlint/intel.py +237 -0
  15. truthlint-0.1.0/src/truthlint/models.py +198 -0
  16. truthlint-0.1.0/src/truthlint/onboarding.py +227 -0
  17. truthlint-0.1.0/src/truthlint/parsers/__init__.py +1 -0
  18. truthlint-0.1.0/src/truthlint/parsers/ci.py +201 -0
  19. truthlint-0.1.0/src/truthlint/parsers/compose.py +101 -0
  20. truthlint-0.1.0/src/truthlint/parsers/dockerfile.py +150 -0
  21. truthlint-0.1.0/src/truthlint/parsers/envfile.py +41 -0
  22. truthlint-0.1.0/src/truthlint/parsers/lines.py +78 -0
  23. truthlint-0.1.0/src/truthlint/parsers/makefile.py +119 -0
  24. truthlint-0.1.0/src/truthlint/parsers/markdown.py +193 -0
  25. truthlint-0.1.0/src/truthlint/parsers/node_meta.py +139 -0
  26. truthlint-0.1.0/src/truthlint/parsers/python_meta.py +467 -0
  27. truthlint-0.1.0/src/truthlint/parsers/shell.py +235 -0
  28. truthlint-0.1.0/src/truthlint/parsers/source.py +506 -0
  29. truthlint-0.1.0/src/truthlint/parsers/versions.py +273 -0
  30. truthlint-0.1.0/src/truthlint/parsers/yamlx.py +65 -0
  31. truthlint-0.1.0/src/truthlint/py.typed +0 -0
  32. truthlint-0.1.0/src/truthlint/render/__init__.py +1 -0
  33. truthlint-0.1.0/src/truthlint/render/json_report.py +36 -0
  34. truthlint-0.1.0/src/truthlint/render/markdown.py +158 -0
  35. truthlint-0.1.0/src/truthlint/render/terminal.py +355 -0
  36. truthlint-0.1.0/src/truthlint/rules/__init__.py +34 -0
  37. truthlint-0.1.0/src/truthlint/rules/base.py +85 -0
  38. truthlint-0.1.0/src/truthlint/rules/build.py +188 -0
  39. truthlint-0.1.0/src/truthlint/rules/commands.py +1671 -0
  40. truthlint-0.1.0/src/truthlint/rules/containers.py +403 -0
  41. truthlint-0.1.0/src/truthlint/rules/deps.py +713 -0
  42. truthlint-0.1.0/src/truthlint/rules/docs.py +416 -0
  43. truthlint-0.1.0/src/truthlint/rules/env.py +380 -0
  44. truthlint-0.1.0/src/truthlint/rules/runtime.py +552 -0
  45. truthlint-0.1.0/src/truthlint/scoring.py +83 -0
  46. truthlint-0.1.0/src/truthlint.egg-info/PKG-INFO +281 -0
  47. truthlint-0.1.0/src/truthlint.egg-info/SOURCES.txt +55 -0
  48. truthlint-0.1.0/src/truthlint.egg-info/dependency_links.txt +1 -0
  49. truthlint-0.1.0/src/truthlint.egg-info/entry_points.txt +2 -0
  50. truthlint-0.1.0/src/truthlint.egg-info/requires.txt +9 -0
  51. truthlint-0.1.0/src/truthlint.egg-info/top_level.txt +1 -0
  52. truthlint-0.1.0/tests/test_cli.py +98 -0
  53. truthlint-0.1.0/tests/test_fixes.py +94 -0
  54. truthlint-0.1.0/tests/test_parsers.py +182 -0
  55. truthlint-0.1.0/tests/test_rules.py +466 -0
  56. truthlint-0.1.0/tests/test_safety.py +109 -0
  57. truthlint-0.1.0/tests/test_versions.py +71 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 truthlint 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,281 @@
1
+ Metadata-Version: 2.4
2
+ Name: truthlint
3
+ Version: 0.1.0
4
+ Summary: Find where your repository contradicts itself: docs, configs, CI, containers and code checked against each other, offline.
5
+ Author: truthlint contributors
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/harshith7002/truthlint
8
+ Project-URL: Issues, https://github.com/harshith7002/truthlint/issues
9
+ Keywords: static-analysis,documentation,consistency,developer-tools,linter,onboarding,repository,drift
10
+ Classifier: Development Status :: 4 - Beta
11
+ Classifier: Environment :: Console
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Software Development :: Quality Assurance
19
+ Classifier: Topic :: Software Development :: Documentation
20
+ Requires-Python: >=3.11
21
+ Description-Content-Type: text/markdown
22
+ License-File: LICENSE
23
+ Requires-Dist: typer>=0.12
24
+ Requires-Dist: rich>=13.7
25
+ Requires-Dist: pydantic>=2.6
26
+ Requires-Dist: PyYAML>=6.0
27
+ Provides-Extra: dev
28
+ Requires-Dist: pytest>=8.0; extra == "dev"
29
+ Requires-Dist: pytest-cov>=5.0; extra == "dev"
30
+ Requires-Dist: ruff>=0.6; extra == "dev"
31
+ Dynamic: license-file
32
+
33
+ <div align="center">
34
+
35
+ # truthlint
36
+
37
+ ### Your repository is lying. truthlint finds where.
38
+
39
+ **Find every place where your repository's docs, configs, CI, containers and code contradict each other.**
40
+
41
+ [![CI](https://github.com/harshith7002/truthlint/actions/workflows/ci.yml/badge.svg)](https://github.com/harshith7002/truthlint/actions/workflows/ci.yml)
42
+ ![Python](https://img.shields.io/badge/python-3.11%2B-blue)
43
+ ![License](https://img.shields.io/badge/license-MIT-green)
44
+ ![Offline](https://img.shields.io/badge/works-offline-success)
45
+
46
+ </div>
47
+
48
+ ---
49
+
50
+ <!-- truthlint-ignore TL101: the next line is an illustration, not a requirement -->
51
+ Your README says **Python 3.8+**. `pyproject.toml` says `>=3.10`. The Dockerfile runs **3.9**. CI tests a version
52
+ users can't even install on. The setup guide tells newcomers to run `make lint` — a target that was renamed a year
53
+ ago. The code reads `JWT_SECRET`, which is nowhere in `.env.example`.
54
+
55
+ Every one of those files is individually "fine". Together they lie.
56
+
57
+ **truthlint** reads a repository the way a careful new contributor would — then cross-examines every file against every
58
+ other file and reports, with evidence, where the project's **claims** and its **reality** disagree.
59
+
60
+ ```text
61
+ HIGH TL101-406201 Python version contradiction high confidence
62
+ README.md says Python 3.8+ is enough, but the project requires >=3.10
63
+ (minimum 3.10). Users following the docs on Python 3.8 will be refused by pip.
64
+ ↳ README.md:8 claims Python 3.8+
65
+ ↳ pyproject.toml:9 declares requires-python '>=3.10'
66
+ Fix: State Python 3.10+ in README.md, or raise the constraint. [auto-fix available]
67
+
68
+ HIGH TL202-6F78F4 Command runs an undefined make target high confidence
69
+ `README.md` documents `make lint`, but target `lint` is not defined in
70
+ `Makefile` (defined: format, serve, test).
71
+ ↳ README.md:28 documents `make lint`
72
+ ↳ Makefile defines targets: format, serve, test
73
+ ```
74
+
75
+ ## Why truthlint?
76
+
77
+ Linters check one file at a time. Type checkers check code. Link checkers check URLs. **Nothing checks that the
78
+ files agree with each other** — and that is exactly where onboarding breaks, deployments drift and "works on my
79
+ machine" is born.
80
+
81
+ | | |
82
+ |---|---|
83
+ | 🔎 **Cross-file analysis** | 35 rules compare README, docs, `pyproject.toml`, `requirements*.txt`, `package.json`, lockfiles, Dockerfiles, compose files, GitHub Actions / GitLab CI, Makefiles, `.env` examples and source code. |
84
+ | 🧾 **Evidence, always** | Every finding cites the exact file and line on *each side* of the contradiction, a confidence level, and how it was verified. truthlint never reports something it didn't check. |
85
+ | 🧭 **Onboarding audit** | Walks your setup instructions step by step (tracking `cd`, `cp`, `venv` …) and verifies each command statically: does that npm script, make target, file, compose service or pip extra actually exist? |
86
+ | 📊 **Explainable scores** | A 0–100 **Repository Truth Score** and an **Onboarding Reliability Score**, with every deduction itemised. |
87
+ | 🩹 **Reviewable fixes** | Suggested fix for every finding; many come with a unified-diff patch. Nothing is written without your explicit confirmation. |
88
+ | 🔒 **Safe by construction** | Fully offline, no API keys, no AI service. **Never executes anything** from the repository — not even `git` (a repo's `.git/config` can make git run commands). |
89
+
90
+ ## Installation
91
+
92
+ Requires Python **3.11+**.
93
+
94
+ ```bash
95
+ pipx install truthlint # recommended: isolated CLI
96
+ # or
97
+ pip install truthlint
98
+ ```
99
+
100
+ From source:
101
+
102
+ ```bash
103
+ git clone https://github.com/harshith7002/truthlint.git
104
+ cd truthlint
105
+ pip install -e ".[dev]"
106
+ ```
107
+
108
+ This installs the `truthlint` command (`python -m truthlint` works too).
109
+
110
+ ## Quick start
111
+
112
+ ```bash
113
+ truthlint scan . # investigate the current repository
114
+ truthlint scan . --deep # + heuristic checks (unused deps/env vars, stale doc paths, CI coverage)
115
+ truthlint explain TL202-6F78F4 # evidence in context, rationale and the proposed patch
116
+ truthlint fix # preview every available patch (dry run)
117
+ truthlint fix --interactive # review and apply patches one by one
118
+ truthlint report --format markdown -o truthlint-report.md
119
+ truthlint report --format json -o truthlint.json
120
+ ```
121
+
122
+ Try it on the bundled sample repositories:
123
+
124
+ ```bash
125
+ truthlint scan examples/drifting-python-api
126
+ truthlint scan examples/drifting-node-service --deep
127
+ truthlint scan examples/healthy-library # 100/100, zero findings
128
+ ```
129
+
130
+ ## What truthlint detects
131
+
132
+ Run `truthlint rules` for the full list, or `truthlint explain TL201` for details on any rule. See [docs/rules.md](docs/rules.md).
133
+
134
+ | Area | Examples |
135
+ |---|---|
136
+ | **Runtime versions** | README / Dockerfile / CI matrix / `.python-version` / `.nvmrc` / classifiers / `ruff target-version` vs `requires-python` or `engines.node`; container runtime drifting from the local pin; declared minimum never tested in CI |
137
+ | **Documented commands** | `npm run X`, `yarn X`, `pnpm X` with no such script · `make X` with no such target · scripts, requirements files, directories and modules that don't exist · `pip install ".[dev]"` with no `dev` extra · `docker compose up svc` with no such service · `npm install` in a pnpm-locked repo · `poetry install` in a non-Poetry project · documented CLI never declared as an entry point |
138
+ | **Documentation** | Broken relative links and heading anchors (case-exact, like GitHub) · outdated project-structure trees · inline references to files that no longer exist |
139
+ | **Environment** | Variables read in code (Python AST incl. pydantic-settings, JS/TS, Go, Ruby, Rust, Java, PHP, C#, Elixir, Prisma, shell) but missing from `.env.example`; documented variables nothing reads; no example file at all |
140
+ | **Dependencies** | Imports not declared in any manifest (works only on the author's machine) · declared deps never imported · runtime code importing dev-only deps — for Python and JS/TS |
141
+ | **Containers** | `COPY` of missing files or files excluded by `.dockerignore` · Docker pinning different versions than the manifest · lockfile not copied before install · `CMD` pointing at missing modules · compose build contexts, Dockerfiles, `env_file`s and `depends_on` that don't exist |
142
+ | **CI** | Workflow `run:` steps calling missing scripts/targets/files · missing `working-directory` · missing local actions |
143
+ | **Build metadata** | Malformed TOML/JSON/YAML · console scripts pointing to missing modules/functions · `readme`, `main`, `bin` files that don't exist |
144
+ | **Onboarding** | No setup instructions · required runtimes/package managers never mentioned · env configuration never explained |
145
+
146
+ ## Scores
147
+
148
+ **Repository Truth Score (0–100).** Each finding contributes `severity weight × confidence`
149
+ (critical 20 · high 10 · medium 5 · low 1.5; confidence 1.0 / 0.7 / 0.4). Repeats of the same rule count half as
150
+ much each time and each rule is capped at 25 points, so one noisy area can't dominate. The score is
151
+ `100 × e^(−points/90)`. Every deduction is listed in the Markdown/JSON report.
152
+
153
+ **Onboarding Reliability Score (0–100).** Starts at 100: −40 if there are no runnable setup instructions, −15 per
154
+ documented step that fails verification (max −60), −8 per required tool the docs never mention, −5 if
155
+ environment configuration is never explained. Steps that would need execution to verify (e.g. `git clone`,
156
+ `pytest`) are reported as *unverified* and never counted as passing **or** failing.
157
+
158
+ ## Using truthlint in CI
159
+
160
+ ```yaml
161
+ # .github/workflows/truthlint.yml
162
+ name: truthlint
163
+ on: [pull_request]
164
+ jobs:
165
+ truthlint:
166
+ runs-on: ubuntu-latest
167
+ steps:
168
+ - uses: actions/checkout@v4
169
+ - uses: actions/setup-python@v5
170
+ with:
171
+ python-version: "3.12"
172
+ - run: pip install truthlint
173
+ - run: truthlint scan . --fail-on high # fail on high/critical contradictions
174
+ - run: truthlint report --format markdown -o truthlint-report.md
175
+ if: always()
176
+ ```
177
+
178
+ Exit codes: `0` success · `1` a `--fail-under` / `--fail-on` gate tripped · `2` usage error.
179
+
180
+ ## Configuration
181
+
182
+ Add a `[tool.truthlint]` table to `pyproject.toml` or create `.truthlint.toml`:
183
+
184
+ ```toml
185
+ [tool.truthlint]
186
+ exclude = ["examples/**", "vendor/"] # gitignore syntax, on top of .gitignore
187
+ disable = ["TL402"] # rule IDs to skip
188
+ ignore_env = ["MY_CI_ONLY_VAR"] # env vars that never need documenting
189
+ min_severity = "low" # hide findings below this severity
190
+ ```
191
+
192
+ Suppress a single finding inline with a comment on the same line (or a comment line directly above):
193
+
194
+ ```markdown
195
+ <!-- truthlint-ignore TL302 -->
196
+ Generated files land in `build/output/report.html`.
197
+ ```
198
+
199
+ ```bash
200
+ make docs # truthlint-ignore TL202, TL203
201
+ ```
202
+
203
+ ## Accuracy principles
204
+
205
+ truthlint is built to be trusted in CI, so it prefers silence to guessing:
206
+
207
+ - **Evidence or nothing.** A finding can't be constructed without at least one file/line citation (enforced in code).
208
+ - **Unverifiable ≠ broken.** Anything that depends on the network or installed tools is reported as *unverified*.
209
+ - **Ambiguity is downgraded.** If a script exists in another package of a monorepo, or a Makefile uses pattern rules/includes, truthlint reports *unverified* instead of *missing*.
210
+ - **Generated paths are respected.** Missing files under `dist/`, `build/`, gitignored paths, or outputs of earlier documented steps (`cp`, `mkdir`, `python -m venv`) aren't reported.
211
+ - **Heuristic rules are opt-in.** Rules with a higher false-positive risk run only with `--deep`.
212
+ - **Malformed files are reported, not guessed at.** If a file can't be parsed, dependent checks are skipped and the file is listed under "Not analysed".
213
+
214
+ The bundled [`examples/healthy-library`](examples/healthy-library) must always score 100 with zero findings — it's part of the test suite.
215
+
216
+ ## Architecture
217
+
218
+ ```text
219
+ ┌──────────────────────────────────────────────────────────┐
220
+ repository ─▶ │ discovery own .gitignore engine, no git, no symlink │
221
+ │ escapes, size/binary limits │
222
+ └───────────────────────┬──────────────────────────────────┘
223
+ ▼
224
+ ┌──────────────────────────────────────────────────────────┐
225
+ │ RepoContext (lazy, cached) │
226
+ │ parsers: markdown · shell · pyproject/requirements · │
227
+ │ package.json · Dockerfile · compose · GH Actions · │
228
+ │ GitLab CI · Makefile · .env · Python AST · JS/TS │
229
+ └───────┬───────────────────────┬──────────────────────────┘
230
+ ▼ ▼
231
+ ┌──────────────────────┐ ┌──────────────────────────────────────┐
232
+ │ intel: profile │ │ checks → Findings (pydantic models) │
233
+ │ languages, tooling │ │ runtime · commands · docs · env · │
234
+ └──────────┬───────────┘ │ deps · containers · CI · build │
235
+ │ └──────────────┬───────────────────────┘
236
+ │ ▼
237
+ │ onboarding audit · suppression · scoring
238
+ ▼ ▼
239
+ ┌──────────────────────────────────────────────────────────┐
240
+ │ ScanResult → terminal (Rich) · Markdown · JSON · patches │
241
+ └──────────────────────────────────────────────────────────┘
242
+ ```
243
+
244
+ | Module | Responsibility |
245
+ |---|---|
246
+ | `src/truthlint/context.py` | File discovery and the lazily-parsed, cached view every rule reads from |
247
+ | `src/truthlint/ignore.py` | `.gitignore` / `.dockerignore` matcher (git semantics, no subprocess) |
248
+ | `src/truthlint/parsers/` | One parser per format; all line-number aware; none execute code |
249
+ | `src/truthlint/intel.py` | Repository intelligence: languages, frameworks, package managers, CI, key files |
250
+ | `src/truthlint/rules/` | The checks. `commands.py` hosts the static command verifier shared by docs, CI, Makefiles and npm scripts |
251
+ | `src/truthlint/onboarding.py` | Setup-instruction audit and onboarding score |
252
+ | `src/truthlint/scoring.py` | Explainable Truth Score |
253
+ | `src/truthlint/fixes.py` | Edit validation, unified diffs, consent-gated application |
254
+ | `src/truthlint/render/` | Rich terminal UI, Markdown and JSON reports |
255
+ | `src/truthlint/cli.py` | Typer commands |
256
+
257
+ More detail in [docs/architecture.md](docs/architecture.md), including how to write a new rule.
258
+
259
+ ## Contributing
260
+
261
+ Contributions are very welcome — especially new rules, parsers for more ecosystems, and **false-positive
262
+ reports** (there's a dedicated issue template). See [CONTRIBUTING.md](CONTRIBUTING.md).
263
+
264
+ ```bash
265
+ pip install -e ".[dev]"
266
+ pytest
267
+ ruff check src tests
268
+ ruff format --check src tests
269
+ truthlint scan . # truthlint dogfoods itself
270
+ ```
271
+
272
+ ## Roadmap
273
+
274
+ - More ecosystems: Go modules, Cargo, Gemfile, Gradle/Maven version checks
275
+ - `truthlint scan --diff origin/main` to report only findings introduced by a change
276
+ - SARIF output for GitHub code scanning
277
+ - Verifying CLI sub-commands documented in the README against argparse/Typer/Click definitions
278
+
279
+ ## License
280
+
281
+ [MIT](LICENSE)
@@ -0,0 +1,249 @@
1
+ <div align="center">
2
+
3
+ # truthlint
4
+
5
+ ### Your repository is lying. truthlint finds where.
6
+
7
+ **Find every place where your repository's docs, configs, CI, containers and code contradict each other.**
8
+
9
+ [![CI](https://github.com/harshith7002/truthlint/actions/workflows/ci.yml/badge.svg)](https://github.com/harshith7002/truthlint/actions/workflows/ci.yml)
10
+ ![Python](https://img.shields.io/badge/python-3.11%2B-blue)
11
+ ![License](https://img.shields.io/badge/license-MIT-green)
12
+ ![Offline](https://img.shields.io/badge/works-offline-success)
13
+
14
+ </div>
15
+
16
+ ---
17
+
18
+ <!-- truthlint-ignore TL101: the next line is an illustration, not a requirement -->
19
+ Your README says **Python 3.8+**. `pyproject.toml` says `>=3.10`. The Dockerfile runs **3.9**. CI tests a version
20
+ users can't even install on. The setup guide tells newcomers to run `make lint` — a target that was renamed a year
21
+ ago. The code reads `JWT_SECRET`, which is nowhere in `.env.example`.
22
+
23
+ Every one of those files is individually "fine". Together they lie.
24
+
25
+ **truthlint** reads a repository the way a careful new contributor would — then cross-examines every file against every
26
+ other file and reports, with evidence, where the project's **claims** and its **reality** disagree.
27
+
28
+ ```text
29
+ HIGH TL101-406201 Python version contradiction high confidence
30
+ README.md says Python 3.8+ is enough, but the project requires >=3.10
31
+ (minimum 3.10). Users following the docs on Python 3.8 will be refused by pip.
32
+ ↳ README.md:8 claims Python 3.8+
33
+ ↳ pyproject.toml:9 declares requires-python '>=3.10'
34
+ Fix: State Python 3.10+ in README.md, or raise the constraint. [auto-fix available]
35
+
36
+ HIGH TL202-6F78F4 Command runs an undefined make target high confidence
37
+ `README.md` documents `make lint`, but target `lint` is not defined in
38
+ `Makefile` (defined: format, serve, test).
39
+ ↳ README.md:28 documents `make lint`
40
+ ↳ Makefile defines targets: format, serve, test
41
+ ```
42
+
43
+ ## Why truthlint?
44
+
45
+ Linters check one file at a time. Type checkers check code. Link checkers check URLs. **Nothing checks that the
46
+ files agree with each other** — and that is exactly where onboarding breaks, deployments drift and "works on my
47
+ machine" is born.
48
+
49
+ | | |
50
+ |---|---|
51
+ | 🔎 **Cross-file analysis** | 35 rules compare README, docs, `pyproject.toml`, `requirements*.txt`, `package.json`, lockfiles, Dockerfiles, compose files, GitHub Actions / GitLab CI, Makefiles, `.env` examples and source code. |
52
+ | 🧾 **Evidence, always** | Every finding cites the exact file and line on *each side* of the contradiction, a confidence level, and how it was verified. truthlint never reports something it didn't check. |
53
+ | 🧭 **Onboarding audit** | Walks your setup instructions step by step (tracking `cd`, `cp`, `venv` …) and verifies each command statically: does that npm script, make target, file, compose service or pip extra actually exist? |
54
+ | 📊 **Explainable scores** | A 0–100 **Repository Truth Score** and an **Onboarding Reliability Score**, with every deduction itemised. |
55
+ | 🩹 **Reviewable fixes** | Suggested fix for every finding; many come with a unified-diff patch. Nothing is written without your explicit confirmation. |
56
+ | 🔒 **Safe by construction** | Fully offline, no API keys, no AI service. **Never executes anything** from the repository — not even `git` (a repo's `.git/config` can make git run commands). |
57
+
58
+ ## Installation
59
+
60
+ Requires Python **3.11+**.
61
+
62
+ ```bash
63
+ pipx install truthlint # recommended: isolated CLI
64
+ # or
65
+ pip install truthlint
66
+ ```
67
+
68
+ From source:
69
+
70
+ ```bash
71
+ git clone https://github.com/harshith7002/truthlint.git
72
+ cd truthlint
73
+ pip install -e ".[dev]"
74
+ ```
75
+
76
+ This installs the `truthlint` command (`python -m truthlint` works too).
77
+
78
+ ## Quick start
79
+
80
+ ```bash
81
+ truthlint scan . # investigate the current repository
82
+ truthlint scan . --deep # + heuristic checks (unused deps/env vars, stale doc paths, CI coverage)
83
+ truthlint explain TL202-6F78F4 # evidence in context, rationale and the proposed patch
84
+ truthlint fix # preview every available patch (dry run)
85
+ truthlint fix --interactive # review and apply patches one by one
86
+ truthlint report --format markdown -o truthlint-report.md
87
+ truthlint report --format json -o truthlint.json
88
+ ```
89
+
90
+ Try it on the bundled sample repositories:
91
+
92
+ ```bash
93
+ truthlint scan examples/drifting-python-api
94
+ truthlint scan examples/drifting-node-service --deep
95
+ truthlint scan examples/healthy-library # 100/100, zero findings
96
+ ```
97
+
98
+ ## What truthlint detects
99
+
100
+ Run `truthlint rules` for the full list, or `truthlint explain TL201` for details on any rule. See [docs/rules.md](docs/rules.md).
101
+
102
+ | Area | Examples |
103
+ |---|---|
104
+ | **Runtime versions** | README / Dockerfile / CI matrix / `.python-version` / `.nvmrc` / classifiers / `ruff target-version` vs `requires-python` or `engines.node`; container runtime drifting from the local pin; declared minimum never tested in CI |
105
+ | **Documented commands** | `npm run X`, `yarn X`, `pnpm X` with no such script · `make X` with no such target · scripts, requirements files, directories and modules that don't exist · `pip install ".[dev]"` with no `dev` extra · `docker compose up svc` with no such service · `npm install` in a pnpm-locked repo · `poetry install` in a non-Poetry project · documented CLI never declared as an entry point |
106
+ | **Documentation** | Broken relative links and heading anchors (case-exact, like GitHub) · outdated project-structure trees · inline references to files that no longer exist |
107
+ | **Environment** | Variables read in code (Python AST incl. pydantic-settings, JS/TS, Go, Ruby, Rust, Java, PHP, C#, Elixir, Prisma, shell) but missing from `.env.example`; documented variables nothing reads; no example file at all |
108
+ | **Dependencies** | Imports not declared in any manifest (works only on the author's machine) · declared deps never imported · runtime code importing dev-only deps — for Python and JS/TS |
109
+ | **Containers** | `COPY` of missing files or files excluded by `.dockerignore` · Docker pinning different versions than the manifest · lockfile not copied before install · `CMD` pointing at missing modules · compose build contexts, Dockerfiles, `env_file`s and `depends_on` that don't exist |
110
+ | **CI** | Workflow `run:` steps calling missing scripts/targets/files · missing `working-directory` · missing local actions |
111
+ | **Build metadata** | Malformed TOML/JSON/YAML · console scripts pointing to missing modules/functions · `readme`, `main`, `bin` files that don't exist |
112
+ | **Onboarding** | No setup instructions · required runtimes/package managers never mentioned · env configuration never explained |
113
+
114
+ ## Scores
115
+
116
+ **Repository Truth Score (0–100).** Each finding contributes `severity weight × confidence`
117
+ (critical 20 · high 10 · medium 5 · low 1.5; confidence 1.0 / 0.7 / 0.4). Repeats of the same rule count half as
118
+ much each time and each rule is capped at 25 points, so one noisy area can't dominate. The score is
119
+ `100 × e^(−points/90)`. Every deduction is listed in the Markdown/JSON report.
120
+
121
+ **Onboarding Reliability Score (0–100).** Starts at 100: −40 if there are no runnable setup instructions, −15 per
122
+ documented step that fails verification (max −60), −8 per required tool the docs never mention, −5 if
123
+ environment configuration is never explained. Steps that would need execution to verify (e.g. `git clone`,
124
+ `pytest`) are reported as *unverified* and never counted as passing **or** failing.
125
+
126
+ ## Using truthlint in CI
127
+
128
+ ```yaml
129
+ # .github/workflows/truthlint.yml
130
+ name: truthlint
131
+ on: [pull_request]
132
+ jobs:
133
+ truthlint:
134
+ runs-on: ubuntu-latest
135
+ steps:
136
+ - uses: actions/checkout@v4
137
+ - uses: actions/setup-python@v5
138
+ with:
139
+ python-version: "3.12"
140
+ - run: pip install truthlint
141
+ - run: truthlint scan . --fail-on high # fail on high/critical contradictions
142
+ - run: truthlint report --format markdown -o truthlint-report.md
143
+ if: always()
144
+ ```
145
+
146
+ Exit codes: `0` success · `1` a `--fail-under` / `--fail-on` gate tripped · `2` usage error.
147
+
148
+ ## Configuration
149
+
150
+ Add a `[tool.truthlint]` table to `pyproject.toml` or create `.truthlint.toml`:
151
+
152
+ ```toml
153
+ [tool.truthlint]
154
+ exclude = ["examples/**", "vendor/"] # gitignore syntax, on top of .gitignore
155
+ disable = ["TL402"] # rule IDs to skip
156
+ ignore_env = ["MY_CI_ONLY_VAR"] # env vars that never need documenting
157
+ min_severity = "low" # hide findings below this severity
158
+ ```
159
+
160
+ Suppress a single finding inline with a comment on the same line (or a comment line directly above):
161
+
162
+ ```markdown
163
+ <!-- truthlint-ignore TL302 -->
164
+ Generated files land in `build/output/report.html`.
165
+ ```
166
+
167
+ ```bash
168
+ make docs # truthlint-ignore TL202, TL203
169
+ ```
170
+
171
+ ## Accuracy principles
172
+
173
+ truthlint is built to be trusted in CI, so it prefers silence to guessing:
174
+
175
+ - **Evidence or nothing.** A finding can't be constructed without at least one file/line citation (enforced in code).
176
+ - **Unverifiable ≠ broken.** Anything that depends on the network or installed tools is reported as *unverified*.
177
+ - **Ambiguity is downgraded.** If a script exists in another package of a monorepo, or a Makefile uses pattern rules/includes, truthlint reports *unverified* instead of *missing*.
178
+ - **Generated paths are respected.** Missing files under `dist/`, `build/`, gitignored paths, or outputs of earlier documented steps (`cp`, `mkdir`, `python -m venv`) aren't reported.
179
+ - **Heuristic rules are opt-in.** Rules with a higher false-positive risk run only with `--deep`.
180
+ - **Malformed files are reported, not guessed at.** If a file can't be parsed, dependent checks are skipped and the file is listed under "Not analysed".
181
+
182
+ The bundled [`examples/healthy-library`](examples/healthy-library) must always score 100 with zero findings — it's part of the test suite.
183
+
184
+ ## Architecture
185
+
186
+ ```text
187
+ ┌──────────────────────────────────────────────────────────┐
188
+ repository ─▶ │ discovery own .gitignore engine, no git, no symlink │
189
+ │ escapes, size/binary limits │
190
+ └───────────────────────┬──────────────────────────────────┘
191
+ ▼
192
+ ┌──────────────────────────────────────────────────────────┐
193
+ │ RepoContext (lazy, cached) │
194
+ │ parsers: markdown · shell · pyproject/requirements · │
195
+ │ package.json · Dockerfile · compose · GH Actions · │
196
+ │ GitLab CI · Makefile · .env · Python AST · JS/TS │
197
+ └───────┬───────────────────────┬──────────────────────────┘
198
+ ▼ ▼
199
+ ┌──────────────────────┐ ┌──────────────────────────────────────┐
200
+ │ intel: profile │ │ checks → Findings (pydantic models) │
201
+ │ languages, tooling │ │ runtime · commands · docs · env · │
202
+ └──────────┬───────────┘ │ deps · containers · CI · build │
203
+ │ └──────────────┬───────────────────────┘
204
+ │ ▼
205
+ │ onboarding audit · suppression · scoring
206
+ ▼ ▼
207
+ ┌──────────────────────────────────────────────────────────┐
208
+ │ ScanResult → terminal (Rich) · Markdown · JSON · patches │
209
+ └──────────────────────────────────────────────────────────┘
210
+ ```
211
+
212
+ | Module | Responsibility |
213
+ |---|---|
214
+ | `src/truthlint/context.py` | File discovery and the lazily-parsed, cached view every rule reads from |
215
+ | `src/truthlint/ignore.py` | `.gitignore` / `.dockerignore` matcher (git semantics, no subprocess) |
216
+ | `src/truthlint/parsers/` | One parser per format; all line-number aware; none execute code |
217
+ | `src/truthlint/intel.py` | Repository intelligence: languages, frameworks, package managers, CI, key files |
218
+ | `src/truthlint/rules/` | The checks. `commands.py` hosts the static command verifier shared by docs, CI, Makefiles and npm scripts |
219
+ | `src/truthlint/onboarding.py` | Setup-instruction audit and onboarding score |
220
+ | `src/truthlint/scoring.py` | Explainable Truth Score |
221
+ | `src/truthlint/fixes.py` | Edit validation, unified diffs, consent-gated application |
222
+ | `src/truthlint/render/` | Rich terminal UI, Markdown and JSON reports |
223
+ | `src/truthlint/cli.py` | Typer commands |
224
+
225
+ More detail in [docs/architecture.md](docs/architecture.md), including how to write a new rule.
226
+
227
+ ## Contributing
228
+
229
+ Contributions are very welcome — especially new rules, parsers for more ecosystems, and **false-positive
230
+ reports** (there's a dedicated issue template). See [CONTRIBUTING.md](CONTRIBUTING.md).
231
+
232
+ ```bash
233
+ pip install -e ".[dev]"
234
+ pytest
235
+ ruff check src tests
236
+ ruff format --check src tests
237
+ truthlint scan . # truthlint dogfoods itself
238
+ ```
239
+
240
+ ## Roadmap
241
+
242
+ - More ecosystems: Go modules, Cargo, Gemfile, Gradle/Maven version checks
243
+ - `truthlint scan --diff origin/main` to report only findings introduced by a change
244
+ - SARIF output for GitHub code scanning
245
+ - Verifying CLI sub-commands documented in the README against argparse/Typer/Click definitions
246
+
247
+ ## License
248
+
249
+ [MIT](LICENSE)
@@ -0,0 +1,78 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "truthlint"
7
+ version = "0.1.0"
8
+ description = "Find where your repository contradicts itself: docs, configs, CI, containers and code checked against each other, offline."
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "truthlint contributors" }]
14
+ keywords = [
15
+ "static-analysis",
16
+ "documentation",
17
+ "consistency",
18
+ "developer-tools",
19
+ "linter",
20
+ "onboarding",
21
+ "repository",
22
+ "drift",
23
+ ]
24
+ classifiers = [
25
+ "Development Status :: 4 - Beta",
26
+ "Environment :: Console",
27
+ "Intended Audience :: Developers",
28
+ "Operating System :: OS Independent",
29
+ "Programming Language :: Python :: 3",
30
+ "Programming Language :: Python :: 3.11",
31
+ "Programming Language :: Python :: 3.12",
32
+ "Programming Language :: Python :: 3.13",
33
+ "Topic :: Software Development :: Quality Assurance",
34
+ "Topic :: Software Development :: Documentation",
35
+ ]
36
+ dependencies = [
37
+ "typer>=0.12",
38
+ "rich>=13.7",
39
+ "pydantic>=2.6",
40
+ "PyYAML>=6.0",
41
+ ]
42
+
43
+ [project.optional-dependencies]
44
+ dev = ["pytest>=8.0", "pytest-cov>=5.0", "ruff>=0.6"]
45
+
46
+ [project.scripts]
47
+ truthlint = "truthlint.cli:main"
48
+
49
+ [project.urls]
50
+ Homepage = "https://github.com/harshith7002/truthlint"
51
+ Issues = "https://github.com/harshith7002/truthlint/issues"
52
+
53
+ [tool.setuptools.packages.find]
54
+ where = ["src"]
55
+
56
+ [tool.setuptools.package-data]
57
+ truthlint = ["py.typed"]
58
+
59
+ [tool.ruff]
60
+ line-length = 100
61
+ target-version = "py311"
62
+ src = ["src", "tests"]
63
+ extend-exclude = ["examples"]
64
+
65
+ [tool.ruff.lint]
66
+ select = ["E", "F", "W", "I", "B", "UP", "SIM", "RUF", "C4"]
67
+ ignore = ["E501", "RUF001", "RUF002", "RUF003", "SIM108"]
68
+
69
+ [tool.ruff.lint.per-file-ignores]
70
+ "tests/**" = ["B011"]
71
+
72
+ [tool.pytest.ini_options]
73
+ testpaths = ["tests"]
74
+ addopts = "-q"
75
+
76
+ # truthlint dogfoods itself. The sample repositories contain deliberate inconsistencies.
77
+ [tool.truthlint]
78
+ exclude = ["examples/**", "tests/fixtures/**"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,10 @@
1
+ """truthlint - find where your repository contradicts itself.
2
+
3
+ Finds the places where a repository's documentation, configuration, CI,
4
+ containers and code contradict each other. Fully offline; never executes
5
+ anything from the repository it inspects.
6
+ """
7
+
8
+ __version__ = "0.1.0"
9
+
10
+ __all__ = ["__version__"]
@@ -0,0 +1,4 @@
1
+ from .cli import main
2
+
3
+ if __name__ == "__main__":
4
+ main()