Metadata-Version: 2.4
Name: filesorty
Version: 0.6.1
Summary: Safety-first, AI-assisted file organizer: understands what files contain, previews every move, and can undo.
Author: Jayanth Thalla
License-Expression: MIT
Project-URL: Homepage, https://github.com/jayanththalla/filesorty
Project-URL: Issues, https://github.com/jayanththalla/filesorty/issues
Project-URL: Changelog, https://github.com/jayanththalla/filesorty/blob/main/CHANGELOG.md
Keywords: file organizer,downloads,semantic,llm,claude,openai,gemini,grok,groq,ollama,undo,cli
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: End Users/Desktop
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
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 :: Filesystems
Classifier: Topic :: Utilities
Classifier: Typing :: Typed
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: click>=8.1
Provides-Extra: pdf
Requires-Dist: pypdf>=4; extra == "pdf"
Provides-Extra: pymupdf
Requires-Dist: pymupdf>=1.24; extra == "pymupdf"
Provides-Extra: docs
Requires-Dist: python-docx>=1.0; extra == "docs"
Requires-Dist: openpyxl>=3.1; extra == "docs"
Requires-Dist: python-pptx>=0.6.23; extra == "docs"
Provides-Extra: watch
Requires-Dist: watchdog>=4.0; extra == "watch"
Provides-Extra: keyring
Requires-Dist: keyring>=24; extra == "keyring"
Provides-Extra: ocr
Requires-Dist: rapidocr-onnxruntime>=1.3; python_version < "3.13" and extra == "ocr"
Requires-Dist: pypdfium2>=4; extra == "ocr"
Requires-Dist: pillow>=10; extra == "ocr"
Provides-Extra: all
Requires-Dist: pypdf>=4; extra == "all"
Requires-Dist: python-docx>=1.0; extra == "all"
Requires-Dist: openpyxl>=3.1; extra == "all"
Requires-Dist: python-pptx>=0.6.23; extra == "all"
Requires-Dist: watchdog>=4.0; extra == "all"
Requires-Dist: keyring>=24; extra == "all"
Provides-Extra: dev
Requires-Dist: pytest>=8; extra == "dev"
Requires-Dist: reportlab>=4; extra == "dev"
Requires-Dist: pypdf>=4; extra == "dev"
Requires-Dist: python-docx>=1.0; extra == "dev"
Requires-Dist: openpyxl>=3.1; extra == "dev"
Requires-Dist: python-pptx>=0.6.23; extra == "dev"
Requires-Dist: rapidocr-onnxruntime>=1.3; python_version < "3.13" and extra == "dev"
Requires-Dist: pypdfium2>=4; extra == "dev"
Requires-Dist: pillow>=10; extra == "dev"
Requires-Dist: ruff; extra == "dev"
Requires-Dist: build; extra == "dev"
Requires-Dist: twine; extra == "dev"
Dynamic: license-file

# filesorty

**Tidy a messy folder in two steps – safely.** It reads what your files are *about* (text, titles, even
scanned documents), proposes a folder structure, shows it to you grouped by folder, and only moves files when
you say so. Every run can be undone.

```bash
pip install "filesorty[all]"
filesorty
```

That's it. The first run asks three quick questions (AI or no AI, API key, how careful). Then:

```
$ filesorty
Organize C:\Users\me\Downloads? [Y/n]:
Analyzing  [####################]  84/84

Ready to move: 61 file(s)    Held back (low confidence): 6

  Career\Interview Preparation\Infosys\    4 file(s)   90–93%
      infosys.pdf, infosys_dsa.pdf, infosys_interview.pdf …
  Finance\Invoices\                        5 file(s)   85–90%
  Projects\AI-ML\                          3 file(s)   88%
  Study\DBMS\                              6 file(s)   90%

Move 61 file(s)?  [y]es / [n]o / [r]eview by folder / [d]etails:
```

Changed your mind? `filesorty undo` puts everything back and removes the folders it created.

> Status: **alpha (0.3)**. It is careful by design, but always read the plan before saying yes.

## Why you can trust it

* **Nothing moves without your approval**, and files below the confidence threshold never move on their own.
* **Never overwrites, never deletes.** Name clashes get a numbered name. Files changed after analysis aren't moved.
* **Always undoable** (history in SQLite). Undo refuses to overwrite and detects edited files.
* **Refuses dangerous targets:** drive roots, your home folder itself, OS/program folders.
* **Leaves code projects alone** (folders with `.git`, `package.json`, `pyproject.toml`, …).
* **AI is optional.** Without it, built-in rules still organize common documents.
* **The AI can't make a mess:** it must pick from a fixed folder taxonomy (plus *your* existing folders); one-off
  topics don't get their own folders; every proposed path is validated and confined to the target folder.

## Accuracy features

| Feature | What it does |
|---|---|
| Content + context | file name, title/headings, text, neighbouring files, existing folders |
| Local OCR (`[ocr]`) | reads scanned PDFs and photos of documents on your machine (Python 3.11/3.12) |
| Stable taxonomy | `Work/HR`, `Study/Marksheets`, … instead of a new folder name per file |
| Topic grouping | `Work/HR/TCS/` only appears when ≥ 2 files share the topic (`min_topic_group`) |
| Rule + AI hybrid | confident rules skip the AI; AI answers are validated and cached per file hash |
| Calibrated confidence | name-only guesses are capped at 80%; generic "Documents" answers at 69% |

## Privacy

Choose during setup: **rules only** (nothing leaves your computer), **Ollama** (local AI), or a **cloud**
provider (Groq / OpenAI-compatible).

* Cloud mode sends only extracted text (≤ 4,000 characters), never files. Structured identifiers
  (Aadhaar/UAN-style numbers, PAN, cards, phones, emails, IFSC) are removed first; this is best-effort and
  **does not remove names or addresses** – use Ollama if text must never leave your machine.
* Identity, medical and finance files, source code, and files without text are never sent to a cloud AI.
* API keys live in your OS keychain (or a private file / environment variable) – never in the config file or logs.
* OCR and PDF reading are always local.

## API keys & AI providers

Pick a provider once (`filesorty setup`, or `use <provider>` any time). The wizard asks for your key
(hidden), stores it safely, tests it, and lets you choose a model from the provider's **live** list.

| Provider | `use …` | Key from | Notes |
|---|---|---|---|
| Claude (Anthropic) | `use claude` | console.anthropic.com/settings/keys | Haiku is the cheapest |
| OpenAI | `use openai` | platform.openai.com/api-keys | |
| Google Gemini | `use gemini` | aistudio.google.com/apikey | generous free tier |
| Groq | `use groq` | console.groq.com/keys | very fast; free tier is rate-limited |
| xAI Grok | `use grok` | console.x.ai | (`groq` ≠ `grok`: two different companies) |
| Mistral · DeepSeek · OpenRouter · Together | `use mistral` … | their consoles | |
| **Ollama** (local) | `use ollama` | no key | private and free |
| **LM Studio** (local) | `use lmstudio` | no key | start its local server first |
| Any OpenAI-compatible server | `setup` → *Other* | your base URL | vLLM, LiteLLM, gateways … |

```bash
filesorty keys set claude     # paste key → tested first → saved only if it works
filesorty keys set claude --visible    # terminal won't paste into hidden input? show what you type
filesorty keys set claude --clipboard  # or copy the key and just press Enter
filesorty keys               # which providers have a key, and where it's stored
filesorty use groq           # switch provider in one command (asks for a key if missing)
filesorty models             # what can I use right now?
filesorty doctor             # check key + connection + model
```

**Where keys live** (looked up in this order): a key you saved with `filesorty keys set` – in your **OS keychain**
(Windows Credential Manager / macOS Keychain / Linux Secret Service) or, only if *you* agree, a private
credentials file (e.g. on a headless server) → an environment variable (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`,
`GEMINI_API_KEY`, `GROQ_API_KEY`, `XAI_API_KEY`, `MISTRAL_API_KEY`, `DEEPSEEK_API_KEY`, `OPENROUTER_API_KEY`,
`TOGETHER_API_KEY`). A saved key wins, so an old variable left in a terminal can't override the key you just
entered; `filesorty keys list` and `doctor` tell you when that happens. Every provider keeps its own key, so you
can store several and switch with `filesorty use <provider>`.
Keys are never written to the config file, logs or error messages. Pasted keys are cleaned up automatically
(quotes, `Bearer `, `KEY=`), and a gentle warning appears if the prefix doesn't match the provider.

**It checks before it works:** every run tests the connection first. A rejected key shows up in a second, and you can
type a new key, continue with rules only, or quit – instead of failing halfway through.

**Fast by design:** files the rules are already sure about (≥ 90%) skip the AI; AI answers are cached; requests run
in parallel at a rate suited to each provider (`ai_concurrency` to override); rate limits are retried automatically.

## Commands

| Command | What it does |
|---|---|
| `filesorty` | guided mode (setup on first run → analyze → review → move) |
| `organize [PATH] [--review] [--dry-run] [--yes]` | same, for a folder; default `~/Downloads` |
| `preview [PATH] [--all] [--json]` | every proposed move in detail; changes nothing |
| `analyze [PATH]` | folder-tree summary and confidence counts |
| `undo` | restore the last batch |
| `setup` / `doctor` | 3-step setup / check configuration, API key, connection and optional features |
| `keys [set\|test\|delete] [PROVIDER]` | manage API keys (OS keychain) |
| `use PROVIDER [--model M]` · `models` | switch AI provider in one command · list available models |
| `duplicates PATH` · `search PATH QUERY` · `scan PATH` | find duplicates · keyword search · list files |
| `config [--set KEY=VALUE]` · `cache [--clear]` | settings · AI-answer cache |

Upgrading from `fiesorty` or the earlier `smart-file-organizer`? Filesorty migrates existing settings and undo
history from `~/.fiesorty` or `~/.smart_file_organizer` to `~/.filesorty` automatically. Existing `FIESORTY_*`
environment variables and saved API keys continue to work.

## Python API

```python
from filesorty import Organizer

org = Organizer("~/Downloads")                # rules only; or ai_provider="ollama", model="qwen2.5:3b"
plan = org.preview()                          # read-only
for op in plan:
    print(op.source.name, "→", op.destination, f"{op.confidence:.0%}", op.reason)

org.organize()                                # dry_run=True by default: simulation only
org.organize(dry_run=False, auto_approve=True, min_confidence=0.85)   # moves confident files
org.undo()
```

`Organizer(...)` never modifies the `Config` you pass in. Extension points: `ContentExtractor`, `AIProvider` /
`LLMProvider`, `Rule`. The test-suite uses `MockAIProvider` and never calls a real AI service.

## Install options

| Extra | Adds |
|---|---|
| `all` | PDF (pypdf), Word/Excel/PowerPoint, OS keychain, watchdog |
| `ocr` | read scanned documents locally (RapidOCR; Python < 3.13) |
| `pymupdf` | faster PDF reading; **AGPL-3.0 / commercial** licensed, so it's opt-in |

Python 3.11+ on Windows, macOS and Linux.

## Troubleshooting

Run `filesorty doctor`. Common messages:

| Message | Fix |
|---|---|
| `HTTP 429 … rate limited` | free tier limit: it retries automatically; lower `max_chars_sent` to use fewer tokens |
| `access denied – check your API key` | `filesorty keys set <provider>` |
| `model '…' not offered` | `filesorty models`, then `use <provider> --model <name>` |
| `cannot reach Ollama` | start Ollama and `ollama pull <model>` |
| `AI disabled for the rest of this run` | 3 failures in a row; remaining files used rules. Run `doctor` |
| `will not move` / "Held back" | below the confidence threshold; `--min-confidence 0.7` or `--review` |

## Development

```bash
pip install -e ".[all,dev]"
pytest
ruff check src tests --select F,E9
```

## Known limitations

* Alpha: keyword rules + an optional AI model; always review the plan.
* Keyword search only (no embeddings yet); no file watcher yet; related files are only linked within one folder.
* OCR needs the `ocr` extra, adds seconds per scanned page (results are cached), and isn't available on Python 3.13.

## Roadmap

Embeddings & semantic search · watcher with suggest-only mode · learning from your edits · image understanding.

## License

MIT
