Metadata-Version: 2.4
Name: orphan
Version: 0.2.0
Summary: Find the files whose knowledge sits with a single person — the bus factor, made concrete.
Author: Feareis
License: MIT
Project-URL: Homepage, https://github.com/Feareis/Orphan
Project-URL: Issues, https://github.com/Feareis/Orphan/issues
Keywords: git,bus-factor,code-ownership,knowledge-silo,devops
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Version Control :: Git
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Dynamic: license-file

<div align="center">

# orphan

**The bus factor, made concrete.**

Not an abstract score — the *named list* of files whose knowledge sits with a single person, sorted by how much is actually at risk.

[![PyPI version](https://img.shields.io/pypi/v/orphan.svg)](https://pypi.org/project/orphan/)
[![Python versions](https://img.shields.io/pypi/pyversions/orphan.svg)](https://pypi.org/project/orphan/)
[![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
[![No dependencies](https://img.shields.io/badge/dependencies-none-brightgreen.svg)](#why-youll-like-it)

</div>

<!--
  SCREENSHOT — hero.
  Capture a run on a real repo (colored terminal output), save as docs/hero.png,
  ~1200px wide. A repo with a few silos/orphans reads best.
-->
![orphan in action](docs/hero.png)

---

## What it does

Every mature codebase has files only one person has ever really touched. Some are harmless. Some are a `terraform` module or an `nginx.conf` holding up production, understood by exactly one engineer — who may have left months ago.

`orphan` reads your git history and surfaces those files by name, with the owner, the age of the last commit, and a risk category. It reads **history only, never file contents**, so it works on any repository regardless of language — application code and infrastructure alike.

## The three categories

Age alone is misleading: a `LICENSE` written once and never touched looks exactly like a dangerous orphan, but it's the healthiest file in the repo. `orphan` separates *stable* from *risky* using two axes — how much a file has evolved (commit count, a proxy for accumulated knowledge) and whether its sole owner is still active.

| Category | Meaning | Signal |
|---|---|---|
| ◆ **silo** | Many commits, one owner, **still active** | Live, concentrated knowledge. One brain holds a growing area. |
| ✖ **orphan** | Many commits, one owner, **now inactive** | The knowledge has likely already walked out the door. |
| · **stable** | Few commits (a base or config file) | Low risk. Shown for completeness, not as an alarm. |

The `stable` bucket exists on purpose: it's where the classic false positives land (base files an author wrote once before moving on), so they stop drowning out the real risks.

<!--
  SCREENSHOT — optional, localized output.
  A second capture with `--lang fr` (or de/es/it) shows the i18n off nicely.
  Save as docs/lang.png.
-->

## Why you'll like it

- **Zero dependencies.** Pure Python standard library, 3.8+.
- **Portable.** A single script; drop `orphan.py` anywhere and run it.
- **Language-agnostic.** Works on Terraform, Ansible, shell, YAML — anything git tracks, not just source code.
- **Localized output.** English, French, German, Spanish, Italian (see [Languages](#languages)).
- **Scriptable.** `--json` for pipelines and dashboards.

## Installation

### From PyPI

```bash
pip install orphan
```

Then, in any repository:

```bash
orphan
```

### From source (no install)

The tool is a single file. Grab `orphan.py` (and `locales.json` if you want non-English output), then:

```bash
python3 orphan.py
```

That's it — nothing to build, nothing to install. `orphan.py` alone runs in English; add `locales.json` beside it to unlock the other languages.

## Usage

```bash
orphan                     # analyze the current repository
orphan --lang fr           # localized output (en, fr, de, es, it)
orphan --dense-commits 8   # raise the silo/orphan vs stable threshold
orphan --active-months 6   # "inactive owner" cutoff (default: 12 months)
orphan --threshold 2       # also consider files with up to 2 authors
orphan --top 40            # cap the displayed rows (0 = all)
orphan --path infra/       # restrict to a subdirectory
orphan --json              # machine-readable output (never localized)
```

### Options

| Option | Default | Description |
|---|---|---|
| `--lang` | `en` | Output language: `en`, `fr`, `de`, `es`, `it`. |
| `--path` | *(repo root)* | Restrict analysis to a subdirectory. |
| `--threshold` | `1` | Max distinct authors for a file to be considered single-owned. |
| `--dense-commits` | `5` | Commits from which a file counts as knowledge-dense (silo/orphan vs stable). |
| `--active-months` | `12` | An author with no commit for N months counts as inactive. |
| `--top` | `25` | Show only the N riskiest files (`0` for all). |
| `--json` | *(off)* | Emit raw JSON instead of the terminal report. |
| `--no-color` | *(off)* | Disable ANSI colors. |

### JSON output

`--json` emits stable, language-neutral data — ideal for CI gates or a dashboard:

```json
{
  "summary": { "total": 12, "silo": 3, "orphan": 4, "stable": 5, "tracked_files": 210, "dense_commits": 5 },
  "files": [
    {
      "path": "infra/pipeline.tf",
      "kind": "infra",
      "category": "orphan",
      "owners": ["Bob"],
      "owner_count": 1,
      "commits": 8,
      "last_commit": "2024-05-01T10:00:00+00:00",
      "age_days": 800,
      "departed": true
    }
  ]
}
```

## Languages

Switch the display language with `--lang`. English is built into the script; the other languages live in `locales.json` next to it. If that file is missing or a string is absent, the output falls back to English.

<details>
<summary><strong>Adding a language</strong> (no code required)</summary>

<br>

Translations are pure data. To add, say, Portuguese:

1. Open `locales.json`.
2. Copy the `"en"` block — it's the reference and lists every key.
3. Rename it `"pt"` and translate the values. Keep the `{placeholders}` intact.
4. For the age units, note the `_one` / `_other` pairs — they exist so plurals read correctly (e.g. `1 mês` vs `6 meses`).
5. Add `"pt"` to the `--lang` choices in `orphan.py` (the `LANGUAGES` tuple).
6. Open a pull request. 🎉

</details>

## How it works

A single `git log` pass over the whole repository collects, per file, the set of authors and the date of the last commit, plus each author's most recent activity anywhere in the repo. `%aN` applies your `.mailmap`, so one person committing under several emails counts once. Files are then bucketed into the three categories and sorted so the densest live silos rise to the top.

One git invocation for the whole repo — fast even on large histories.

## Limitations

Honest by design:

- **Commit count is a proxy for knowledge, not size.** A large file written in a single commit reads as `stable` even if it holds a lot.
- **Renames aren't followed** in a repo-wide log, so a freshly renamed file may look single-owner even if it had history under its old name.
- **Ownership is per commit, not per line.** "Who has ever touched this file" is the right orphan signal and far faster than running `git blame` per file.
- **"Inactive" is inferred from git activity**, not HR data. Someone working on another repo for a year will read as inactive. Tune `--active-months` to your team's rhythm.

## Contributing

Issues and pull requests welcome — a new translation, a smarter category heuristic, an extra infra file type. Adding a language needs no Python (see [above](#languages)).

## License

[MIT](LICENSE) — do what you like, no warranty.
