Metadata-Version: 2.4
Name: tidydisk
Version: 0.3.0
Summary: Safety-first media/personal-data consolidation engine with an agent layer designed for local models
Author: Surakshith Sampath
License-Expression: AGPL-3.0-or-later
Project-URL: Repository, https://github.com/msampath/tidydisk
Project-URL: Issues, https://github.com/msampath/tidydisk/issues
Project-URL: Changelog, https://github.com/msampath/tidydisk/blob/master/CHANGELOG.md
Keywords: media-library,deduplication,file-organization,agent,llm,safety
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Operating System :: Microsoft :: Windows
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: System :: Archiving
Classifier: Topic :: System :: Filesystems
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: hypothesis>=6; extra == "dev"
Requires-Dist: ruff<0.16,>=0.6; extra == "dev"
Requires-Dist: mypy>=1.11; extra == "dev"
Provides-Extra: enrich
Requires-Dist: mutagen>=1.47; extra == "enrich"
Requires-Dist: Pillow>=10; extra == "enrich"
Requires-Dist: thefuzz>=0.22; extra == "enrich"
Dynamic: license-file

# tidydisk — media library organizer

[![ci](https://github.com/msampath/tidydisk/actions/workflows/ci.yml/badge.svg)](https://github.com/msampath/tidydisk/actions/workflows/ci.yml)
[![license: AGPL-3.0-or-later](https://img.shields.io/badge/license-AGPL--3.0--or--later-blue.svg)](LICENSE)
[![python: 3.11+](https://img.shields.io/badge/python-3.11%2B-blue.svg)](pyproject.toml)

**A safety-first consolidation engine for personal data hoards, with an agent layer
designed so even a small local model does trustworthy work.**

`tidydisk` consolidates years of scattered files — old drives, phone backups, NAS dumps,
recovery-tool output — into one organized, deduplicated library. It was distilled from a
real 2 TB / 388,609-file consolidation whose first pipeline needed five rewrites; this
engine is the re-architecture that makes each of those failure classes *structurally
impossible*, not just guarded by discipline.

> Status: v0.3 (unreleased) — the engine, the agent layer, content-derived organization
> (Jellyfin-style `Movies/Title (Year)`, `Series/Season NN`, photo year folders), in-place
> library repair (`plan reorganize`), the guided 2-pass web UI (`tidydisk serve`), opt-in
> enrichment connectors (TMDb/OpenSubtitles/ID3/SearXNG), and undo/dispose/doctor are all in.
> See [docs/roadmap.md](docs/roadmap.md).

## The trust model

Most "AI file organizers" put the model in charge and hope. `tidydisk` inverts it:

1. **Code enforces safety.** Every filesystem mutation flows through one small kernel
   that knows protected paths, same-drive staging, never-overwrite — and there is **no
   delete API anywhere in the codebase** (AST-enforced in CI). The one delete-adjacent
   capability, `tidydisk dispose` (C68), hands staging-only, journal-verified files to the
   **OS's own Recycle Bin / trash** — recoverable, staging-scoped, and gated behind a
   typed row-count confirmation. The engine cannot destroy your data, no matter what
   the model says.
2. **The model supplies judgment, inside a protocol.** Classification and triage happen
   through bounded tasks: schema-validated outputs, enumerated choices, abstention
   (`UNSURE`) as a first-class answer, escalation local → cloud → human. Designed and
   evaluated for **gpt-oss-20b running on your own machine** — your filenames never have
   to leave your house.
3. **Humans gate irreversibility.** Staging is reversible by design; disposal of staged
   duplicates happens only through `tidydisk dispose` — a plan you review, executed only with
   an explicit `--confirm-dispose <row count>`, into the OS trash (never a true delete).

## Why another organizer

Because the five-rewrite pipeline this replaces kept a defect ledger, and every entry is
now a mechanism with a named regression test — see
[docs/defect-ledger.md](docs/defect-ledger.md):

- a phase that ran twice created 165K duplicate files → **journaled, content-addressed
  operations; re-running anything is a no-op**
- one missing keyword silently misrouted an entire language → **classifiers must prove
  coverage; unmatched files block the plan and name themselves**
- stale scan CSVs were consulted as truth, twice → **derived artifacts carry freshness
  stamps; the engine refuses stale inputs and never reads CSVs back**
- files manually moved mid-pipeline stranded 3,858 unique files in a staging folder →
  **plans re-verify every row's preconditions at execute time; drift is a report, not a loss**
- `shutil.copy2` was trusted blindly → **the kernel re-hashes every copy before journaling it**

## Quickstart

**The 2-pass cleanup** (the front door). Pass 1 analyzes everything read-only and
rehearsed into ONE sealed proposal; Pass 2 is a single review-and-execute sitting in a
localhost web UI. Approvals are hash-bound to exactly what you reviewed; execution
auto-converges (bounded) and verifies; the engine still deletes nothing — staged
duplicates leave only via `tidydisk dispose` into the OS Recycle Bin / trash, behind a typed
row-count confirmation.

```console
$ tidydisk pilot                       # Pass 1: analyze everything -> proposal.json
$ tidydisk serve                       # Pass 2: review clusters -> approve -> Execute
                                  #   (http://127.0.0.1:8765, Ctrl-C to stop)
```

Headless Pass 2: `tidydisk pilot --execute --proposal <proposal.json> --approve-all`.

`tidydisk serve` also keeps the guided single-source stepper (scan → sort → structure
preview → dry-run → organize) for first contact with one messy folder.

Or the granular CLI. `pip install tidydisk` isn't live on PyPI yet — until then, install from
source:

```console
$ git clone https://github.com/msampath/tidydisk.git
$ cd tidydisk
$ pip install -e .                     # zero runtime deps — stdlib only
```

```console
$ tidydisk init                        # writes an annotated tidydisk.toml
$ tidydisk check                       # validates config + root reachability
$ tidydisk scan library                # fingerprint your library into the index
$ tidydisk scan old-drive              # fingerprint a source
$ tidydisk verdicts old-drive          # ORGANIZED / JUNK / UNIQUE / REVIEW
$ tidydisk plan organize old-drive     # uniques -> library (copy-before-stage is enforced)
$ tidydisk apply ".tidydisk/plans/plan-organize-old-drive-ab12cd34.jsonl"            # rehearse
$ tidydisk apply ".tidydisk/plans/plan-organize-old-drive-ab12cd34.jsonl" --execute
$ tidydisk plan dedup old-drive        # duplicates + junk -> staging (same-drive, reversible)
$ tidydisk apply ".tidydisk/plans/plan-dedup-old-drive-9f00aa11.jsonl" --execute
$ tidydisk verify library
$ tidydisk plan reorganize --under Video/old-drive --exif   # repair flat dumps in place;
                                                       # correct trees never move
```

Agent layer (local model via Ollama, or any OpenAI-compatible endpoint):

```console
$ tidydisk agent triage old-drive     # LLM recommends dispositions for the REVIEW pile
$ tidydisk agent classify old-drive   # labels the tail your rules didn't cover
$ tidydisk agent eval --mock          # harness self-check, no model needed
$ tidydisk agent eval                 # measures the configured chain on the golden sets
```

A fully-annotated example config — the exact text `tidydisk init` writes — is browsable at
[examples/tidydisk.toml](examples/tidydisk.toml).

## Design documents

| Doc | What it covers |
|---|---|
| [docs/architecture.md](docs/architecture.md) | SQLite-as-truth store, safety kernel, plan/apply contract, freshness |
| [docs/defect-ledger.md](docs/defect-ledger.md) | every real-world failure → the mechanism that kills it → its regression test |
| [docs/agent-design.md](docs/agent-design.md) | the small-model-first protocol, fallback chain, eval results |
| [docs/formats.md](docs/formats.md) | plan.jsonl / summary.json schemas, exit codes |
| [docs/runbook.md](docs/runbook.md) | end-to-end consolidation walkthrough |
| [docs/web-ui.md](docs/web-ui.md) | the `tidydisk serve` 2-pass UI — what shipped, and what stays CLI/human |

## License

AGPL-3.0-or-later. See [LICENSE](LICENSE).
