Metadata-Version: 2.4
Name: truthlint
Version: 0.1.0
Summary: Find where your repository contradicts itself: docs, configs, CI, containers and code checked against each other, offline.
Author: truthlint contributors
License-Expression: MIT
Project-URL: Homepage, https://github.com/harshith7002/truthlint
Project-URL: Issues, https://github.com/harshith7002/truthlint/issues
Keywords: static-analysis,documentation,consistency,developer-tools,linter,onboarding,repository,drift
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Quality Assurance
Classifier: Topic :: Software Development :: Documentation
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: typer>=0.12
Requires-Dist: rich>=13.7
Requires-Dist: pydantic>=2.6
Requires-Dist: PyYAML>=6.0
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-cov>=5.0; extra == "dev"
Requires-Dist: ruff>=0.6; extra == "dev"
Dynamic: license-file

<div align="center">

# truthlint

### Your repository is lying. truthlint finds where.

**Find every place where your repository's docs, configs, CI, containers and code contradict each other.**

[![CI](https://github.com/harshith7002/truthlint/actions/workflows/ci.yml/badge.svg)](https://github.com/harshith7002/truthlint/actions/workflows/ci.yml)
![Python](https://img.shields.io/badge/python-3.11%2B-blue)
![License](https://img.shields.io/badge/license-MIT-green)
![Offline](https://img.shields.io/badge/works-offline-success)

</div>

---

<!-- truthlint-ignore TL101: the next line is an illustration, not a requirement -->
Your README says **Python 3.8+**. `pyproject.toml` says `>=3.10`. The Dockerfile runs **3.9**. CI tests a version
users can't even install on. The setup guide tells newcomers to run `make lint` — a target that was renamed a year
ago. The code reads `JWT_SECRET`, which is nowhere in `.env.example`.

Every one of those files is individually "fine". Together they lie.

**truthlint** reads a repository the way a careful new contributor would — then cross-examines every file against every
other file and reports, with evidence, where the project's **claims** and its **reality** disagree.

```text
 HIGH     TL101-406201  Python version contradiction  high confidence
   README.md says Python 3.8+ is enough, but the project requires >=3.10
   (minimum 3.10). Users following the docs on Python 3.8 will be refused by pip.
   ↳ README.md:8        claims Python 3.8+
   ↳ pyproject.toml:9   declares requires-python '>=3.10'
   Fix: State Python 3.10+ in README.md, or raise the constraint.  [auto-fix available]

 HIGH     TL202-6F78F4  Command runs an undefined make target  high confidence
   `README.md` documents `make lint`, but target `lint` is not defined in
   `Makefile` (defined: format, serve, test).
   ↳ README.md:28  documents `make lint`
   ↳ Makefile      defines targets: format, serve, test
```

## Why truthlint?

Linters check one file at a time. Type checkers check code. Link checkers check URLs. **Nothing checks that the
files agree with each other** — and that is exactly where onboarding breaks, deployments drift and "works on my
machine" is born.

| | |
|---|---|
| 🔎 **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. |
| 🧾 **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. |
| 🧭 **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? |
| 📊 **Explainable scores** | A 0–100 **Repository Truth Score** and an **Onboarding Reliability Score**, with every deduction itemised. |
| 🩹 **Reviewable fixes** | Suggested fix for every finding; many come with a unified-diff patch. Nothing is written without your explicit confirmation. |
| 🔒 **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). |

## Installation

Requires Python **3.11+**.

```bash
pipx install truthlint        # recommended: isolated CLI
# or
pip install truthlint
```

From source:

```bash
git clone https://github.com/harshith7002/truthlint.git
cd truthlint
pip install -e ".[dev]"
```

This installs the `truthlint` command (`python -m truthlint` works too).

## Quick start

```bash
truthlint scan .                         # investigate the current repository
truthlint scan . --deep                  # + heuristic checks (unused deps/env vars, stale doc paths, CI coverage)
truthlint explain TL202-6F78F4           # evidence in context, rationale and the proposed patch
truthlint fix                            # preview every available patch (dry run)
truthlint fix --interactive              # review and apply patches one by one
truthlint report --format markdown -o truthlint-report.md
truthlint report --format json -o truthlint.json
```

Try it on the bundled sample repositories:

```bash
truthlint scan examples/drifting-python-api
truthlint scan examples/drifting-node-service --deep
truthlint scan examples/healthy-library          # 100/100, zero findings
```

## What truthlint detects

Run `truthlint rules` for the full list, or `truthlint explain TL201` for details on any rule. See [docs/rules.md](docs/rules.md).

| Area | Examples |
|---|---|
| **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 |
| **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 |
| **Documentation** | Broken relative links and heading anchors (case-exact, like GitHub) · outdated project-structure trees · inline references to files that no longer exist |
| **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 |
| **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 |
| **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 |
| **CI** | Workflow `run:` steps calling missing scripts/targets/files · missing `working-directory` · missing local actions |
| **Build metadata** | Malformed TOML/JSON/YAML · console scripts pointing to missing modules/functions · `readme`, `main`, `bin` files that don't exist |
| **Onboarding** | No setup instructions · required runtimes/package managers never mentioned · env configuration never explained |

## Scores

**Repository Truth Score (0–100).** Each finding contributes `severity weight × confidence`
(critical 20 · high 10 · medium 5 · low 1.5; confidence 1.0 / 0.7 / 0.4). Repeats of the same rule count half as
much each time and each rule is capped at 25 points, so one noisy area can't dominate. The score is
`100 × e^(−points/90)`. Every deduction is listed in the Markdown/JSON report.

**Onboarding Reliability Score (0–100).** Starts at 100: −40 if there are no runnable setup instructions, −15 per
documented step that fails verification (max −60), −8 per required tool the docs never mention, −5 if
environment configuration is never explained. Steps that would need execution to verify (e.g. `git clone`,
`pytest`) are reported as *unverified* and never counted as passing **or** failing.

## Using truthlint in CI

```yaml
# .github/workflows/truthlint.yml
name: truthlint
on: [pull_request]
jobs:
  truthlint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - run: pip install truthlint
      - run: truthlint scan . --fail-on high              # fail on high/critical contradictions
      - run: truthlint report --format markdown -o truthlint-report.md
        if: always()
```

Exit codes: `0` success · `1` a `--fail-under` / `--fail-on` gate tripped · `2` usage error.

## Configuration

Add a `[tool.truthlint]` table to `pyproject.toml` or create `.truthlint.toml`:

```toml
[tool.truthlint]
exclude = ["examples/**", "vendor/"]   # gitignore syntax, on top of .gitignore
disable = ["TL402"]                     # rule IDs to skip
ignore_env = ["MY_CI_ONLY_VAR"]         # env vars that never need documenting
min_severity = "low"                    # hide findings below this severity
```

Suppress a single finding inline with a comment on the same line (or a comment line directly above):

```markdown
<!-- truthlint-ignore TL302 -->
Generated files land in `build/output/report.html`.
```

```bash
make docs  # truthlint-ignore TL202, TL203
```

## Accuracy principles

truthlint is built to be trusted in CI, so it prefers silence to guessing:

- **Evidence or nothing.** A finding can't be constructed without at least one file/line citation (enforced in code).
- **Unverifiable ≠ broken.** Anything that depends on the network or installed tools is reported as *unverified*.
- **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*.
- **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.
- **Heuristic rules are opt-in.** Rules with a higher false-positive risk run only with `--deep`.
- **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".

The bundled [`examples/healthy-library`](examples/healthy-library) must always score 100 with zero findings — it's part of the test suite.

## Architecture

```text
                ┌──────────────────────────────────────────────────────────┐
  repository ─▶ │ discovery   own .gitignore engine, no git, no symlink     │
                │             escapes, size/binary limits                   │
                └───────────────────────┬──────────────────────────────────┘
                                        ▼
                ┌──────────────────────────────────────────────────────────┐
                │ RepoContext (lazy, cached)                                │
                │   parsers: markdown · shell · pyproject/requirements ·    │
                │   package.json · Dockerfile · compose · GH Actions ·      │
                │   GitLab CI · Makefile · .env · Python AST · JS/TS        │
                └───────┬───────────────────────┬──────────────────────────┘
                        ▼                       ▼
         ┌──────────────────────┐   ┌──────────────────────────────────────┐
         │ intel: profile       │   │ checks → Findings (pydantic models)   │
         │ languages, tooling   │   │ runtime · commands · docs · env ·     │
         └──────────┬───────────┘   │ deps · containers · CI · build        │
                    │               └──────────────┬───────────────────────┘
                    │                              ▼
                    │               onboarding audit · suppression · scoring
                    ▼                              ▼
              ┌──────────────────────────────────────────────────────────┐
              │ ScanResult → terminal (Rich) · Markdown · JSON · patches  │
              └──────────────────────────────────────────────────────────┘
```

| Module | Responsibility |
|---|---|
| `src/truthlint/context.py` | File discovery and the lazily-parsed, cached view every rule reads from |
| `src/truthlint/ignore.py` | `.gitignore` / `.dockerignore` matcher (git semantics, no subprocess) |
| `src/truthlint/parsers/` | One parser per format; all line-number aware; none execute code |
| `src/truthlint/intel.py` | Repository intelligence: languages, frameworks, package managers, CI, key files |
| `src/truthlint/rules/` | The checks. `commands.py` hosts the static command verifier shared by docs, CI, Makefiles and npm scripts |
| `src/truthlint/onboarding.py` | Setup-instruction audit and onboarding score |
| `src/truthlint/scoring.py` | Explainable Truth Score |
| `src/truthlint/fixes.py` | Edit validation, unified diffs, consent-gated application |
| `src/truthlint/render/` | Rich terminal UI, Markdown and JSON reports |
| `src/truthlint/cli.py` | Typer commands |

More detail in [docs/architecture.md](docs/architecture.md), including how to write a new rule.

## Contributing

Contributions are very welcome — especially new rules, parsers for more ecosystems, and **false-positive
reports** (there's a dedicated issue template). See [CONTRIBUTING.md](CONTRIBUTING.md).

```bash
pip install -e ".[dev]"
pytest
ruff check src tests
ruff format --check src tests
truthlint scan .          # truthlint dogfoods itself
```

## Roadmap

- More ecosystems: Go modules, Cargo, Gemfile, Gradle/Maven version checks
- `truthlint scan --diff origin/main` to report only findings introduced by a change
- SARIF output for GitHub code scanning
- Verifying CLI sub-commands documented in the README against argparse/Typer/Click definitions

## License

[MIT](LICENSE)
