- Python 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
All checks were successful
CI / check (push) Successful in 1m35s
Record what this project is for, which until now existed only in conversation. fname is a spiritual prequel to archivist: point it at a file or a directory, and it derives a name from the file's own contents and renames in place. No repository, no database, nothing to migrate away from. Naming runs in four stages — identify by magic value, extract with a per-filetype plugin, fill remaining gaps with a local LLM, then apply a per-type template. Deterministic extraction always runs first; a PDF with an embedded title never triggers an inference. The LLM configuration follows the homelab paperless-ngx precedent, including the constraints that make it work: qwen3:4b-instruct because 4b fits a shared 6 GB card and the -instruct suffix avoids a <think> block that breaks JSON parsing, and an 8192 context that must match the server or get truncated silently. Per-type templates give consistency within a filetype without forcing PDFs and MP3s to agree with each other. The convention's grammar stays fixed; only the description field varies by type. Scope is stated as commitments rather than ambitions, so the document can settle an argument later: one user, no repository, no database of record, no hosted inference, no plugin may rewrite the convention, no metadata written into files, no destructive rename. v1.0 is PDFs and ID3-tagged audio, both as plugins — two types because one could be hard-coded and would teach nothing about whether the plugin boundary sits in the right place. The plugin architecture document records the boundaries the existing code already enforces, including why each rule exists rather than only what it does. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018XhA69mkFRwmkhrDvRsQLW |
||
| .forgejo | ||
| blueprints | ||
| src/fname | ||
| tests | ||
| .gitignore | ||
| .license-header.txt | ||
| .pre-commit-config.yaml | ||
| .python-version | ||
| CLAUDE.md | ||
| LICENSE | ||
| pyproject.toml | ||
| README.md | ||
| uv.lock | ||
fname
Manage files whose metadata lives in their own names.
fname implements the filename-as-database convention documented by
Karl Voit. Voit's stack is
four separate tools glued by convention (date2name, appendfilename,
filetags, move2archive); fname is one command with subcommands covering
the same ground.
2014-05-09T22.19.58 Susan presenting her new shoes -- family clothing.jpg
└──────── timestamp ────────┘ └───── description ─────┘ └──── tags ────┘
| Part | Rule |
|---|---|
| Timestamp | ISO 8601, colons replaced with dots so the name stays valid on Windows. Date-only is fine |
| Description | Free text, spaces allowed |
| Separator | -- (space, two hyphens, space) |
| Tags | Lowercase, single words, space-separated, before the extension |
Why the filename
Filenames survive every tool, backup, and platform migration. Embedded metadata
such as EXIF gets stripped by tools that do not know to preserve it, and an
external database dies with the application that owns it. The convention is the
artifact: the files stay usable if fname disappears entirely.
Commands
| Command | Does |
|---|---|
fname stamp |
Prepend a timestamp, from EXIF where available, else mtime |
fname append |
Insert descriptive text between the timestamp and the tags |
fname tag |
Add or remove tags, completing from a controlled vocabulary |
fname move |
Move into the dated archive folder, prompting for an event name |
fname find |
Filter by tag; materialize matches as a symlink view |
find never mutates. Write commands are idempotent, so running one twice leaves
the name unchanged, and they never destroy an existing description or tag set.
Status: early. The command surface is defined and the CLI parses, but the subcommands are not implemented yet.
Install
Requires Python 3.14 and uv.
$ git clone ssh://git@git.clowderlabs.com/justine/fname.git
$ cd fname
$ uv sync
$ uv run fname --help
Development
tox is the entry point; every gate CI runs is a tox environment, so a green run locally means a green build.
$ uv run tox # license, lint, format, typecheck, pyright, test
$ uv run tox -e fix # autofix lint findings, then format
$ uv run tox -e cov # tests with the 95% coverage gate
$ uv run tox -e testmon # only the tests affected by your change
Work starts as an issue, and every pull request links back to one with
Closes #<n> or Ref #<n>. CI enforces the link.
Documentation
blueprints/ is an
OKF v0.2
bundle holding the source material this project derives from: the workflow, the
folder scheme, the tag rules, the tool-selection method, and the argument that
motivates the whole approach. Start at
blueprints/index.md.
License
GNU Affero General Public License v3.0 or later.
Copyright (c) 2026 Justine Akehurst.