Filez4Eva
⚠️ DISCLAIMER: This is a hobby/personal project. Not a commercial product. Not for production use.
Shift scans, photos, and other files into structured folders for permanent safekeeping
Filez4Eva is a command-line tool that helps you organize files by naming them correctly and placing them in the right directory structure. It's especially useful for managing account documents, scanned files, and other personal records.
Installation
It's best to install using pipx:
pipx install filez4eva
If you don't have pipx installed, visit the pipx site for installation instructions.
Usage
Stowing Individual Files
To organize a single file (like a downloaded PDF):
filez4eva stow-file ~/Desktop/123456789SomeFileIDownloaded.pdf
Filez4Eva will interactively prompt for:
- Cabinet, only if several are configured (with tab-completion of cabinet names; see Cabinets)
- Date in YYYYMMDD format
- Account name (with tab-completion from existing accounts)
- Part name (with tab-completion from existing files for that account)
Values can also be piped in as a YAML mapping on stdin, which is handy for other tools that produce a record for the file:
printf 'date: 20240213\naccount: acct\npart: statement\n' \
| filez4eva stow-file ~/Desktop/statement.pdf
- Recognised keys are
cabinet,date,accountandpart; any other keys are ignored. - Command-line flags override values from stdin.
- Anything still missing is prompted for as usual.
datemay be20240213,'20240213'or2024-02-13. If it isn't a valid date,stow-fileexits with an error.- If stdin is empty, isn't valid YAML, or isn't a mapping, it is ignored.
- Values are read as plain text, so
0123stays0123andyesstaysyes. ~andnullare treated as missing, so that value is prompted for.- Stdin is only read by
stow-fileitself.scan-dirignores it.
However the values are supplied (flags, stdin or prompts), account must be a
single directory name (not empty, . or .., and without / or \), and
part may contain only letters, digits and hyphens. An invalid value from a
flag or stdin makes stow-file exit with an error. An invalid value typed at
a prompt is rejected and the prompt asks again.
Processing Multiple Files
To process all files in a directory:
filez4eva scan-dir ~/Desktop
This interactive process lets you:
- Skip a file (x)
- Preview a file (p)
- Stow a file (s)
- Delete a file (d)
- Quit processing (q)
Stowing a file works like process: the file
is transcribed (reusing an up-to-date transcript) and classified, then the
summary and doctype are shown and the Date:, Account: and Part: prompts
are pre-filled with the proposed values. Press Enter to accept a value, or
type a new one. With several cabinets, the cabinet is asked for once, before
anything is sent to the model.
- If transcription or classification fails (for example, no API key or an unsupported file type), the error is shown and the prompts start empty, as they would without AI.
- Empty input at any prompt returns to the action choice for the same file, without moving anything.
Transcripts written by transcribe are not offered as files of their own:
X.md is skipped when X is in the same directory (for example
scan.pdf.md next to scan.pdf). A .md file with no such companion is
offered as usual. Deleting a file also deletes its transcript. Stowing a file
moves its transcript with it (see stow-file).
Transcribing Files
To transcribe a file to Markdown using an AI model:
filez4eva transcribe ~/Desktop/scan.pdf
This writes scan.pdf.md next to the file (the full file name plus .md)
and prints its path on stdout. The transcript looks like:
---
source: scan.pdf
sha256: 3a7bd3e2360a3d29eea436fcfb7e44c735d117c42d1c1835420b6b9942dd4f1b
model: claude-opus-4-6
transcribed: '2026-10-10T17:42:05+00:00'
---
<!-- Generated by an AI process from a binary file. ... -->
...Markdown transcription...
- Supported file types are PDF, PNG, JPEG, GIF, WebP, plain text, Markdown and CSV.
- If the transcript already exists and its
sha256matches the file, nothing is sent to the model and the existing path is printed (statusUnchangedon stderr). If the file has changed, or the transcript has no readable front matter, it is transcribed again and overwritten. --force(or-f) transcribes again even if the transcript is up to date.- If transcription fails (e.g. an unsupported file type or an API error),
transcribeexits with an error and any existing transcript is left as it was.
See Transcription settings for the model and API key.
Classifying Files
To have an AI model propose where a file should be filed:
filez4eva classify ~/Desktop/scan.pdf
classify transcribes the file first if needed (exactly as transcribe
does, reusing an up-to-date transcript), then asks the model to classify the
transcript and prints a YAML record on stdout:
file: /Users/me/Desktop/scan.pdf
transcript: /Users/me/Desktop/scan.pdf.md
cabinet: accounts
date: '20261002'
account: amazon
part: receipt
doctype: receipt
summary: Amazon.ca order receipt for a USB hub
dateis the document's own date, as YYYYMMDD.doctypeis one of the configured doctypes, orother.summaryis a single line.- The model is told the cabinet's
description, the accounts already in the cabinet with the parts already used for each (the same ones offered by tab completion, so only with the default pattern), and asked to reuse them when they fit. It is also given the doctype names and descriptions. - The cabinet is chosen as for
stow-file:--cabinet NAME, the only cabinet, or a prompt. - If transcription or classification fails, or the model's answer isn't a
valid date, account, part or doctype,
classifyexits with an error and prints nothing on stdout.
Nothing is moved. To file the document as proposed, pipe the record into
stow-file, which reads cabinet, date, account and part and ignores
the other keys:
filez4eva classify ~/Desktop/scan.pdf | filez4eva stow-file ~/Desktop/scan.pdf
Processing a File with AI
To transcribe, classify, confirm and stow a file in one go:
filez4eva process ~/Desktop/scan.pdf
process:
- transcribes the file if needed (as
transcribedoes, reusing an up-to-date transcript) - classifies it (as
classifydoes) - shows the summary and doctype, then prompts for
Cabinet:(only if several cabinets are configured and it wasn't just chosen),Date:,Account:andPart:, each pre-filled with the proposed value. Press Enter to accept it, or type a new value (backspace clears the pre-filled value first). Tab completion works as forstow-file. - stows the file and its transcript, exactly as
stow-filedoes, and prints the destination path on stdout
- Confirmation is always interactive.
- Empty input at any prompt cancels without moving anything. An invalid value is rejected and the prompt asks again.
- The cabinet is chosen as for
classify(--cabinet NAME, the only cabinet, or a prompt) before anything is sent to the model, and the whole configuration is checked first. --dry-runtranscribes, classifies and prompts as usual, then prints where the file (and its transcript) would go, without moving anything.
Command Line Options
Global options:
--config PATH: Specify path to config file--debug: Enable debug output
stow-file command:
--cabinet, -c NAME: Specify the cabinet (destination) to stow into--date, -d DATE: Specify date in YYYYMMDD format--account, -a ACCOUNT: Specify account name--part, -p PART: Specify part namefile: Path to the file to stow
After a successful move, stow-file prints the absolute destination path on
stdout (the Done status goes to stderr), so scripts can capture it, e.g.
dest=$(filez4eva stow-file -d 20240213 -a acct -p part file.pdf).
If the file has a transcript (FILE.md, written by transcribe), stow-file
moves it too, named after the stowed file. By default it goes adjacent to the
stowed file: .../20261002-receipt.pdf gets .../20261002-receipt.pdf.md.
It can instead go into a parallel tree; see
Transcript location. If a transcript already exists at
its destination, stow-file fails before moving anything.
scan-dir command:
dir: Optional path to directory to scan (defaults to configured source)
transcribe command:
--force, -f: Transcribe again even if the transcript is up to datefile: Path to the file to transcribe
classify command:
--cabinet, -c NAME: Specify the cabinet to classify forfile: Path to the file to classify
process command:
--cabinet, -c NAME: Specify the cabinet to classify for and stow into--dry-run: Show where the file would go without moving anythingfile: Path to the file to process
Configuration
Create a filez4eva.yml file with:
filez4eva:
source: '~/Desktop' # Default source directory
target: '~/Dropbox/accounts' # Target directory for stowed files
Files will be organized in this pattern:
~/Dropbox/accounts/<year>/<account>/<date>-<part>.<extension>
Destination pattern
The layout under target can be changed with the optional pattern key:
filez4eva:
target: '~/Dropbox/accounts'
pattern: '{account}/{year}/{part}-{date}{ext}'
The default is '{year}/{account}/{date}-{part}{ext}', which gives the layout
shown above. Quote the pattern in YAML, since it starts with {.
Placeholders:
{year}: four-digit year, e.g.2024{date}: date as YYYYMMDD, e.g.20240213{account}: account name{part}: part name{ext}: the source file's extension, including the dot (e.g..pdf), or nothing if the file has no extension
Use / to create subdirectories; missing directories are created. Any other
text is used literally. A pattern is a configuration error, and stow-file
exits before moving anything or prompting, if it:
- uses a placeholder not listed above, or a positional one like
{}or{0} - uses format specs, conversions, attributes or indexes (e.g.
{year:>4},{account!r},{year.x},{account[0]}) - has unbalanced braces
- is absolute, starts with
~, or contains..
Tab completion of accounts and parts reads existing files in the default layout, so it only works with the default pattern. With any other pattern the prompts still accept typed values but offer no completion.
Cabinets
To file into more than one destination, configure named cabinets:
filez4eva:
cabinets:
accounts:
target: ~/Dropbox/accounts
pattern: '{year}/{account}/{date}-{part}{ext}'
description: Personal account records
memories:
target: ~/Dropbox/memories
pattern: '{account}/{year}/{part}-{date}{ext}'
Each cabinet has:
target(required): the directory files are stowed underpattern(optional): the layout under the target, as described above. Defaults to the top-levelpatternif set, otherwise'{year}/{account}/{date}-{part}{ext}'description(optional): a note on what the cabinet holdstranscripts(optional): where transcripts of files stowed in this cabinet go, withlocationandrootkeys. Each key defaults to the top-leveltranscriptsvalue; see Transcript location
Choose the cabinet with --cabinet NAME (or -c NAME), or with a cabinet
key on stdin; the flag wins if both are given. If neither is given and there
is more than one cabinet, stow-file prompts for one, with tab-completion of
the names. With only one cabinet it is used without asking. An unknown name
from the flag or stdin is an error; an unknown name typed at the prompt is
rejected and the prompt asks again. scan-dir asks for a cabinet for each
file it stows (once per file, before classifying it).
Tab completion of accounts and parts reads the chosen cabinet's target.
If there is no cabinets section (or it is empty), the top-level target and
pattern form a single cabinet named default, so existing configurations
work unchanged. If cabinets is present, the top-level target is ignored.
The whole configuration is checked before any prompt: a cabinet with no
target, or with an invalid pattern or transcripts setting, is an error even
if it isn't the one being used.
Transcript location
When stow-file stows a file that has a transcript, the transcript goes
next to the stowed file by default (adjacent). To keep transcripts in a
parallel tree instead:
filez4eva:
transcripts:
location: parallel # or 'adjacent' (the default)
root: ~/Dropbox/transcripts
With location: parallel, the transcript goes to
<root>/<cabinet>/<path under the cabinet target>.md. For example, stowing
into the accounts cabinet at 2026/amazon/20261002-receipt.pdf puts the
transcript at ~/Dropbox/transcripts/accounts/2026/amazon/20261002-receipt.pdf.md.
Missing directories are created, and the root may be on another filesystem.
Without a cabinets section the single cabinet is named default, so
transcripts go under <root>/default/.
A cabinet can override either key with its own transcripts block; a key it
doesn't set falls back to the top-level value:
filez4eva:
transcripts:
location: parallel
root: ~/Dropbox/transcripts
cabinets:
accounts:
target: ~/Dropbox/accounts # transcripts in the parallel tree
memories:
target: ~/Dropbox/memories
transcripts:
location: adjacent # transcripts next to the files
A location other than adjacent or parallel, parallel without a
root, or a transcripts value (top-level or in a cabinet) that isn't a
mapping is a configuration error, and stow-file exits before prompting or
moving anything. An error in a top-level setting names the top level rather
than a cabinet. An empty transcripts: block, or an empty location, counts
as unset, but other values such as no or 0 are rejected as invalid
locations.
This only affects stow-file. transcribe and scan-dir always write the
transcript of an incoming file next to it, as FILE.md.
Transcription settings
transcribe uses these optional keys:
filez4eva:
transcribe:
model: claude-opus-4-6 # Anthropic model ID
anthropic:
key: $(op read op://vault/anthropic/credential)
transcribe: model:defaults toclaude-opus-4-6.anthropic: key:is the Anthropic API key. Like other values it may use$(...)to run a command, such as a password manager lookup. It can also be set with theFILEZ4EVA_ANTHROPIC_KEYenvironment variable, which takes precedence over the config file. If neither is set, the Anthropic SDK's ownANTHROPIC_API_KEYenvironment variable is used.
Filez4Eva only reads its own configuration; Kwark's configuration is never used.
Classification settings
classify uses these optional keys, as well as the transcribe model and
the anthropic key above:
filez4eva:
classify:
model: claude-haiku-4-5 # Anthropic model ID (the default)
doctypes:
receipt:
description: Proof of purchase or payment
bill:
description: A request for payment, usually with a due date
classify: model:defaults toclaude-haiku-4-5. Transcription still usestranscribe: model:.doctypesmaps each document type to an optionaldescription, which is shown to the model. Names must be single words (letters, digits,-and_).otheris always available; configure it only to change its description. With nodoctypes, every document's doctype isother.- A
doctypesvalue that isn't a mapping, a name that isn't a single word, or a description that isn't text is a configuration error, andclassifyexits before calling the model.
Metadata
Release files for filez4eva 5.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| filez4eva-5.1.0.tar.gz | 25.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| filez4eva-5.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 50.2 kB
Release files / filez4eva-5.1.0.tar.gz
| Download URL | filez4eva-5.1.0.tar.gz |
|---|---|
| Size | 25.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
e0481dbb33958e9b58104eaee8a41fd178c9ffb8fabf99915aacf4b4df044002
|
|
BLAKE2b-256 checksum How to use checksums |
1f2e09f0c2e64828d654249fed63fd7f0477d2de86264b49cda015b10329fec1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.
Transparency logRelease files / filez4eva-5.1.0-py3-none-any.whl
| Download URL | filez4eva-5.1.0-py3-none-any.whl |
|---|---|
| Size | 24.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f0b265deeaae9466f7823bc4822da02a8058243b62ec57ccaa1bdb79ea025eff
|
|
BLAKE2b-256 checksum How to use checksums |
937095862ed62db2d7161274204173d619bc1b7fcee6bea2e8654cfc51de790d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 11, 2026.
Transparency log