Repository files navigation

Image Autonamer logo

Image Autonamer

Useful filenames. Local vision. Zero cloud uploads.

A sandboxed macOS menu bar app that watches Downloads, understands new images and PDFs with a local vision-language model, and safely renames them.

GitHub releasemacOS 13 or newerSwift 6Ollama local inferenceMIT license

Real Image Autonamer app processing synthetic PDFs with local Ollama inference

Watch the 46-second real-app demo in HD · Download v0.6.1

Install in one command

On an Apple Silicon Mac running macOS 13 or newer:

curl -fsSL https://raw.githubusercontent.com/DavidVaness/image-autonamer/main/scripts/install.sh | sh

The installer downloads the checksum-verified app, installs Ollama through Homebrew if needed, pulls qwen3-vl:4b, and opens Image Autonamer. If you prefer to inspect code before running it, clone the repository and use ./scripts/install-macos-app.sh.

On first launch, the Settings window opens automatically. Click Grant Access, choose Downloads, and click Allow Downloads. That one explicit choice creates a persistent security-scoped bookmark inside the app sandbox. Full Disk Access is not required.

Note

The v0.6.1 binary is ad-hoc signed and supports Apple Silicon. A Developer ID certificate and Apple notarization are the remaining steps for conventional consumer distribution. Intel Macs can build from source.

See it work

This sample is original vector artwork created for this repository and released under the same MIT license as the code. It is safe to reuse in articles, screenshots, and demos.

Original vector illustration of mountains, a sunset, a lake, and pine trees

The checked-in sample was passed through the real local model during release validation:

sample-landscape.png -> mountains-sunset-lake-trees.png

Model wording can vary slightly between Ollama and model versions. Representative everyday results look like this:

BeforeAfterUseful signal captured
Screenshot 2026-08-11.pngpurple-sales-dashboard-bar-chart.pngInterface, color, and primary chart
IMG_8472.JPGred-coffee-mug-beside-laptop.jpgMain objects and their relationship
download.webporange-cat-sleeping-on-sofa.webpSubject, action, and setting
north-star-invoice.pdf2026-08-15-north-star-studio-invoice-inv-2048-invoice-summary-and-payment-notes.pdfDate, visible correspondent, document type, and reference across three pages

The original extension is normalized and preserved. If a filename already exists, Image Autonamer adds -2, -3, and so on without overwriting anything. For PDFs, the existing filename is part of the decision instead of being discarded blindly.

Naming modes

Open Image Autonamer from Finder, Spotlight, or your app switcher to show Settings, then choose the Naming tab. You can also select Configure… from the Naming status row in the menu bar app. The preview button analyzes a selected image or PDF locally and shows the proposed filename without renaming or moving the file.

ModeExample resultBest for
Descriptivered-coffee-mug-beside-laptop.jpgGeneral screenshots, photos, and downloaded images
Date + descriptive2026-08-11-red-coffee-mug-beside-laptop.jpgChronological sorting and camera imports
Documents2026-08-01-example-co-invoice-inv-1042-annual-renewal.pngScans and screenshots of invoices, receipts, statements, contracts, letters, reports, certificates, and tax documents

Date-prefixed names use the image capture date when metadata provides one, then fall back to the file creation or modification date. Documents mode infers the correspondent from the visible issuer, sender, merchant, or wordmark in each file. It classifies the document type and then applies a deterministic type-specific recipe. Invoices can include a visible invoice reference, statements prefer a visible statement period, and receipts omit reference numbers to stay concise. Missing or ambiguous metadata is omitted instead of guessed. An optional Naming context field accepts up to 500 characters of workflow guidance, such as the type of business or image collection. The app normalizes that text and instructs the local model to treat it as reference data without overriding visible-evidence rules. There is intentionally no unrestricted system-prompt editor.

Review Inbox

Enable Review suggestions before renaming in General Settings when you want approval instead of immediate automation. New suggestions then wait in Review Inbox with a thumbnail preview, the original filename, an editable proposed filename, and the visible evidence used to produce it. Approve one suggestion, keep the original name, or approve the whole queue after reviewing edits. Ambiguous PDF improvements are queued even when global review is disabled. PDFs with useful existing names are kept automatically, while generic names are renamed only when the proposal adds visible document information.

Every completed rename appears in History, including renames performed automatically. Undo restores the original filename only when that path is free, so it never overwrites a newer file. The pending queue and the latest 100 rename records survive app restarts in the same atomic state file as scan fingerprints.

Measured locally

The repository includes eight original, MIT-licensed evaluation images plus one synthetic three-page PDF across illustrations, screenshots, objects, documents, diagrams, interfaces, and indoor and outdoor scenes. The harness records the proposed filename, concept coverage, forbidden-concept hits, and latency without renaming the fixtures.

The checked-in qwen3-vl:4b baseline produced useful filenames for all 8 fixtures with a warm-model median local inference time of approximately 0.8 seconds on the release machine. Cold-model latency was higher and varies substantially with hardware and Ollama state. This is a small transparent baseline, not a claim of general model accuracy.

python3 -m pip install -e '.[dev]'
./scripts/build-eval-fixtures.sh
./eval/run.py --model qwen3-vl:4b --output eval/results/qwen3-vl-4b.json

See eval/manifest.json for the explicit rubric and eval/results/qwen3-vl-4b.json for the complete result.

Why this exists

Downloads folders quickly fill with UUIDs, camera counters, and names such as image (12).png. Cloud vision APIs can fix that, but uploading private screenshots and photos solely to rename them is an uncomfortable trade. Image Autonamer keeps inference on the Mac and limits automatic filesystem access to the Downloads folder the user explicitly selects.

What it does

  • Runs image understanding locally through Ollama and qwen3-vl:4b.
  • Watches new top-level images and PDFs in Downloads every 15 seconds.
  • Starts automatically through the native macOS login-item API.
  • Uses the App Sandbox with only user-selected read/write and outbound client entitlements.
  • Shows whether Downloads access is granted and keeps recovery one click away.
  • Protects supported files that existed before first-run setup.
  • Waits for downloads to settle and verifies that a file did not change during analysis.
  • Treats model output as untrusted input and reduces it to a safe lowercase ASCII slug.
  • Extracts embedded PDF text first and uses local Apple Vision OCR only when a scan has no useful text layer.
  • Compares existing and proposed PDF names, then chooses keep, rename, or review conservatively.
  • Offers three focused naming modes, dynamic document recipes, and a no-rename preview instead of an open-ended prompt surface.
  • Offers an opt-in Review Inbox with editable suggestions, visible evidence, bulk approval, and rename history.
  • Makes automatic and approved renames reversible without overwriting files created later.
  • Uses collision-safe filesystem operations that never overwrite another file.
  • Includes a standalone Python CLI for dry runs, one-off images, recursive batches, and Linux.

The native app recognizes AVIF, BMP, GIF, HEIC, HEIF, JPEG, PDF, PNG, TIFF, and WebP by extension. AppKit converts each image to PNG before inference, which gives Ollama a consistent input format. PDFKit renders up to the first three PDF pages to PNG locally before inference. The same pages provide bounded embedded text or an on-device Vision OCR fallback for filename-quality checks. The PDF itself never leaves the Mac and its original .pdf extension is preserved.

How it works

flowchart LR
A["Downloads folder"] --> B["15-second scan"]
B --> C["Settle and fingerprint checks"]
C --> D{"Input type"}
D -->|Image| E["AppKit PNG conversion"]
D -->|PDF| P["PDFKit text + first 3 pages"]
P --> Q["Vision OCR fallback"]
Q --> E
E --> F["Local Ollama VLM"]
F --> G["Structured visible facts"]
G --> H["Deterministic composer and filename quality gate"]
H --> I{"Filename decision"}
I -->|Keep| S["Keep existing name"]
I -->|Review| J["Persisted Review Inbox"]
I -->|Rename| R{"Global review enabled?"}
R -->|Yes| J
J --> K["Approve or keep original"]
R -->|No| L["Automatic approval"]
K --> M["Hard-link-first safe rename"]
L --> M
M --> N["History and collision-safe undo"]
S --> O["Atomic state file"]
N --> O["Atomic state file"]
Loading

ImageProcessor is a Swift actor, so scans cannot mutate state concurrently. Ollama is asked for schema-constrained visible facts at a low temperature. Deterministic code applies the selected naming mode, and the result is still sanitized before touching the filesystem. The source is fingerprinted before and after inference to catch partial or changing downloads.

Security model

The app accepts only the current user's actual Downloads folder in its picker. macOS supplies a security-scoped capability for that folder, and the app persists the capability as a bookmark in its private container. It has no Full Disk Access, no automation entitlement, no telemetry, and no third-party API.

Image bytes are sent to the configured Ollama endpoint, which is hard-coded by default to http://127.0.0.1:11434. The sandbox entitlement technically permits outbound client connections because macOS does not offer a localhost-only network entitlement. The shipped code uses no remote endpoint. Optional naming context is included only in requests to that same local endpoint and is stored in the app's private preferences. PDF pages are rasterized in memory by PDFKit and only those page images are sent to local Ollama. Embedded text and Vision OCR output are bounded, held in memory, and sent only to that same local endpoint. The existing filename is JSON-escaped and explicitly treated as untrusted reference data.

Filesystem handling is defensive:

  • Unsupported files, hidden files, subfolders, and symbolic links are ignored.
  • Existing supported files are baselined during setup instead of unexpectedly renamed.
  • Existing PDFs are baselined once when upgrading from a version that did not support PDFs.
  • A second fingerprint check rejects files modified during model inference.
  • A hard-link-first operation claims the destination atomically before removing the source name.
  • Destination collisions select a numbered suffix instead of overwriting.
  • Undo refuses to run when the original path has been reused, preserving both files.
  • Processing state is written atomically inside the sandbox container.

Engineering decisions

DecisionAlternative consideredWhy this design won
Sandboxed native menu appPython daemon or launchd jobFolder authorization is explicit, persistent, and scoped to one folder instead of inheriting broad terminal or interpreter permissions.
15-second pollingFSEventsA small top-level directory scan is predictable, easy to test, and naturally pairs with the settle window for partially downloaded files.
Local Ollama inferenceHosted vision APIPrivacy and offline operation matter more here than model startup time and disk usage.
JSON schema plus sanitizerFree-form model textModel output remains untrusted even when structured generation succeeds.
Three modes plus deterministic compositionRaw custom promptsCommon workflows stay predictable, testable, and resistant to prompt mistakes while the tool remains focused.
Bounded reference contextAn unrestricted system-prompt editorUsers can add domain vocabulary without weakening visible-evidence rules or turning the app into a prompt workbench.
Dynamic visible correspondentsA manually maintained company listEach document can name its actual issuer while the visible-evidence gate prevents unsupported brand guesses.
Type-specific document recipesOne generic business filenameInvoices, receipts, statements, and other documents include only metadata that is useful for that type.
First three PDF pagesFull-document rendering or OCR storageThree pages usually cover identity, continuation context, and totals while bounding memory, latency, and model context.
Embedded text, then Vision OCROCR every document unconditionallySearchable PDFs stay fast and exact, while scanned documents still receive a fully local fallback.
Keep / rename / review gateRename every supported PDFPreserving a merely adequate name is cheaper than destroying useful dates, references, or source-language meaning.
Debug-only isolated capture harnessRecording against a real Downloads folderThe public demo uses the real app and local model with synthetic PDFs in a temporary folder, so it is authentic without exposing or mutating personal files.
One persisted review pipelineA separate manual renamerAutomatic and reviewed operation share the same sanitizer, collision handling, state, history, and recovery behavior.
Opt-in review for existing usersChanging automation after an updateExisting behavior remains stable while users who want control can enable review explicitly.
Bounded local historyAn unlimited activity databaseThe latest 100 renames provide practical recovery without turning a small utility into a document-management system.
Hard link, then unlinkmoveItem after an existence checkClaiming the destination atomically removes the check-then-write race that could overwrite a file.
Ad-hoc signed release artifactUnsigned bundle or premature App Store packagingThe artifact is reproducible and sandboxed today, while notarization remains an explicit production-distribution follow-up.

Manual CLI

The image-only Python 3.11+ CLI is useful for dry runs, individual files, recursive directories, or non-macOS systems. It is not sandboxed, so the native app is the safer choice for continuous macOS automation.

Preview without changing files:

./bin/image-autonamer --dry-run ~/Downloads

Rename one image:

./bin/image-autonamer ~/Downloads/IMG_8472.PNG

Process a directory tree:

./bin/image-autonamer --recursive /path/to/images

Run ./bin/image-autonamer --help for the complete command reference.

Build and test

Requirements for source builds are macOS 13+, Swift 6, Xcode Command Line Tools, Python 3.11+, and Ollama.

# Native unit tests
swift test# Python unit and contract tests
PYTHONPATH=src python3 -m unittest discover -s tests -v
# Release app and checksum manifest
./scripts/package-release.sh 0.6.1
# Rebuild the 46-second MP4 and GIF from real app-window recordings and original title cards
./scripts/build-demo.sh
# Rebuild the native icon and GitHub social preview
./scripts/build-brand-assets.py

Native tests cover sanitization, first-run protection, PDF text extraction, page rendering, filename decisions, PDF discovery, review queuing, edited approval, rejection, safe renaming, collision handling, history, and undo. Python tests additionally cover the CLI, state database, file settling, discovery, and Ollama request/response contract. CI intentionally mocks Ollama because downloading a multi-gigabyte model on every run would make the suite slow and wasteful.

Project landmarks:

Sources/ImageAutonamerKit/ Local inference and safe processing actor
Sources/ImageAutonamerMac/ Menu bar lifecycle, bookmark, and login item
src/image_autonamer/ Portable Python CLI
tests/ Swift and Python test suites
scripts/ Build, install, package, and demo tooling
docs/ Original demo sources and generated media

See CONTRIBUTING.md for contribution guidelines. See ROADMAP.md for the deliberately narrow product direction and SECURITY.md for private vulnerability reporting.

Troubleshooting

Ollama is unavailable

Open Ollama and verify the local endpoint:

curl http://127.0.0.1:11434/api/tags
ollama pull qwen3-vl:4b

Folder access was cancelled

Open Image Autonamer and choose Grant Access or Reauthorize… in General Settings. Select Downloads and click Allow Downloads. There is no + button to find in System Settings.

The menu bar icon is hidden

macOS may place extra status items behind Control Center when menu bar space is limited. Open Image Autonamer from Finder, Spotlight, or your app switcher to use the full Settings window without the menu bar icon.

The build uses the wrong Swift toolchain

Point the installer at a specific Swift binary:

SWIFT_BIN=/path/to/swift ./scripts/install-macos-app.sh

Uninstall

Open the menu bar icon, turn off Launch at Login, and quit the app. Move ~/Applications/Image Autonamer.app to Trash.

The sandbox container remains at ~/Library/Containers/com.davidvaness.image-autonamer so reinstalling preserves folder authorization and processing state. Remove that container manually if you also want to erase the saved state and bookmark.

License

Image Autonamer, its logo, and the original demo artwork are available under the MIT License.

About

A sandboxed macOS menu bar app that uses a local Ollama vision model to safely rename downloaded images.

Topics

Resources

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

Image Autonamer logo

Image Autonamer

Useful filenames. Local vision. Zero cloud uploads.

A sandboxed macOS menu bar app that watches Downloads, understands new images and PDFs with a local vision-language model, and safely renames them.

GitHub releasemacOS 13 or newerSwift 6Ollama local inferenceMIT license

Real Image Autonamer app processing synthetic PDFs with local Ollama inference

Watch the 46-second real-app demo in HD · Download v0.6.1

Install in one command

On an Apple Silicon Mac running macOS 13 or newer:

curl -fsSL https://raw.githubusercontent.com/DavidVaness/image-autonamer/main/scripts/install.sh | sh

The installer downloads the checksum-verified app, installs Ollama through Homebrew if needed, pulls qwen3-vl:4b, and opens Image Autonamer. If you prefer to inspect code before running it, clone the repository and use ./scripts/install-macos-app.sh.

On first launch, the Settings window opens automatically. Click Grant Access, choose Downloads, and click Allow Downloads. That one explicit choice creates a persistent security-scoped bookmark inside the app sandbox. Full Disk Access is not required.

Note

The v0.6.1 binary is ad-hoc signed and supports Apple Silicon. A Developer ID certificate and Apple notarization are the remaining steps for conventional consumer distribution. Intel Macs can build from source.

See it work

This sample is original vector artwork created for this repository and released under the same MIT license as the code. It is safe to reuse in articles, screenshots, and demos.

Original vector illustration of mountains, a sunset, a lake, and pine trees

The checked-in sample was passed through the real local model during release validation:

sample-landscape.png -> mountains-sunset-lake-trees.png

Model wording can vary slightly between Ollama and model versions. Representative everyday results look like this:

BeforeAfterUseful signal captured
Screenshot 2026-08-11.pngpurple-sales-dashboard-bar-chart.pngInterface, color, and primary chart
IMG_8472.JPGred-coffee-mug-beside-laptop.jpgMain objects and their relationship
download.webporange-cat-sleeping-on-sofa.webpSubject, action, and setting
north-star-invoice.pdf2026-08-15-north-star-studio-invoice-inv-2048-invoice-summary-and-payment-notes.pdfDate, visible correspondent, document type, and reference across three pages

The original extension is normalized and preserved. If a filename already exists, Image Autonamer adds -2, -3, and so on without overwriting anything. For PDFs, the existing filename is part of the decision instead of being discarded blindly.

Naming modes

Open Image Autonamer from Finder, Spotlight, or your app switcher to show Settings, then choose the Naming tab. You can also select Configure… from the Naming status row in the menu bar app. The preview button analyzes a selected image or PDF locally and shows the proposed filename without renaming or moving the file.

ModeExample resultBest for
Descriptivered-coffee-mug-beside-laptop.jpgGeneral screenshots, photos, and downloaded images
Date + descriptive2026-08-11-red-coffee-mug-beside-laptop.jpgChronological sorting and camera imports
Documents2026-08-01-example-co-invoice-inv-1042-annual-renewal.pngScans and screenshots of invoices, receipts, statements, contracts, letters, reports, certificates, and tax documents

Date-prefixed names use the image capture date when metadata provides one, then fall back to the file creation or modification date. Documents mode infers the correspondent from the visible issuer, sender, merchant, or wordmark in each file. It classifies the document type and then applies a deterministic type-specific recipe. Invoices can include a visible invoice reference, statements prefer a visible statement period, and receipts omit reference numbers to stay concise. Missing or ambiguous metadata is omitted instead of guessed. An optional Naming context field accepts up to 500 characters of workflow guidance, such as the type of business or image collection. The app normalizes that text and instructs the local model to treat it as reference data without overriding visible-evidence rules. There is intentionally no unrestricted system-prompt editor.

Review Inbox

Enable Review suggestions before renaming in General Settings when you want approval instead of immediate automation. New suggestions then wait in Review Inbox with a thumbnail preview, the original filename, an editable proposed filename, and the visible evidence used to produce it. Approve one suggestion, keep the original name, or approve the whole queue after reviewing edits. Ambiguous PDF improvements are queued even when global review is disabled. PDFs with useful existing names are kept automatically, while generic names are renamed only when the proposal adds visible document information.

Every completed rename appears in History, including renames performed automatically. Undo restores the original filename only when that path is free, so it never overwrites a newer file. The pending queue and the latest 100 rename records survive app restarts in the same atomic state file as scan fingerprints.

Measured locally

The repository includes eight original, MIT-licensed evaluation images plus one synthetic three-page PDF across illustrations, screenshots, objects, documents, diagrams, interfaces, and indoor and outdoor scenes. The harness records the proposed filename, concept coverage, forbidden-concept hits, and latency without renaming the fixtures.

The checked-in qwen3-vl:4b baseline produced useful filenames for all 8 fixtures with a warm-model median local inference time of approximately 0.8 seconds on the release machine. Cold-model latency was higher and varies substantially with hardware and Ollama state. This is a small transparent baseline, not a claim of general model accuracy.

python3 -m pip install -e '.[dev]'
./scripts/build-eval-fixtures.sh
./eval/run.py --model qwen3-vl:4b --output eval/results/qwen3-vl-4b.json

See eval/manifest.json for the explicit rubric and eval/results/qwen3-vl-4b.json for the complete result.

Why this exists

Downloads folders quickly fill with UUIDs, camera counters, and names such as image (12).png. Cloud vision APIs can fix that, but uploading private screenshots and photos solely to rename them is an uncomfortable trade. Image Autonamer keeps inference on the Mac and limits automatic filesystem access to the Downloads folder the user explicitly selects.

What it does

  • Runs image understanding locally through Ollama and qwen3-vl:4b.
  • Watches new top-level images and PDFs in Downloads every 15 seconds.
  • Starts automatically through the native macOS login-item API.
  • Uses the App Sandbox with only user-selected read/write and outbound client entitlements.
  • Shows whether Downloads access is granted and keeps recovery one click away.
  • Protects supported files that existed before first-run setup.
  • Waits for downloads to settle and verifies that a file did not change during analysis.
  • Treats model output as untrusted input and reduces it to a safe lowercase ASCII slug.
  • Extracts embedded PDF text first and uses local Apple Vision OCR only when a scan has no useful text layer.
  • Compares existing and proposed PDF names, then chooses keep, rename, or review conservatively.
  • Offers three focused naming modes, dynamic document recipes, and a no-rename preview instead of an open-ended prompt surface.
  • Offers an opt-in Review Inbox with editable suggestions, visible evidence, bulk approval, and rename history.
  • Makes automatic and approved renames reversible without overwriting files created later.
  • Uses collision-safe filesystem operations that never overwrite another file.
  • Includes a standalone Python CLI for dry runs, one-off images, recursive batches, and Linux.

The native app recognizes AVIF, BMP, GIF, HEIC, HEIF, JPEG, PDF, PNG, TIFF, and WebP by extension. AppKit converts each image to PNG before inference, which gives Ollama a consistent input format. PDFKit renders up to the first three PDF pages to PNG locally before inference. The same pages provide bounded embedded text or an on-device Vision OCR fallback for filename-quality checks. The PDF itself never leaves the Mac and its original .pdf extension is preserved.

How it works

flowchart LR
A["Downloads folder"] --> B["15-second scan"]
B --> C["Settle and fingerprint checks"]
C --> D{"Input type"}
D -->|Image| E["AppKit PNG conversion"]
D -->|PDF| P["PDFKit text + first 3 pages"]
P --> Q["Vision OCR fallback"]
Q --> E
E --> F["Local Ollama VLM"]
F --> G["Structured visible facts"]
G --> H["Deterministic composer and filename quality gate"]
H --> I{"Filename decision"}
I -->|Keep| S["Keep existing name"]
I -->|Review| J["Persisted Review Inbox"]
I -->|Rename| R{"Global review enabled?"}
R -->|Yes| J
J --> K["Approve or keep original"]
R -->|No| L["Automatic approval"]
K --> M["Hard-link-first safe rename"]
L --> M
M --> N["History and collision-safe undo"]
S --> O["Atomic state file"]
N --> O["Atomic state file"]
Loading

ImageProcessor is a Swift actor, so scans cannot mutate state concurrently. Ollama is asked for schema-constrained visible facts at a low temperature. Deterministic code applies the selected naming mode, and the result is still sanitized before touching the filesystem. The source is fingerprinted before and after inference to catch partial or changing downloads.

Security model

The app accepts only the current user's actual Downloads folder in its picker. macOS supplies a security-scoped capability for that folder, and the app persists the capability as a bookmark in its private container. It has no Full Disk Access, no automation entitlement, no telemetry, and no third-party API.

Image bytes are sent to the configured Ollama endpoint, which is hard-coded by default to http://127.0.0.1:11434. The sandbox entitlement technically permits outbound client connections because macOS does not offer a localhost-only network entitlement. The shipped code uses no remote endpoint. Optional naming context is included only in requests to that same local endpoint and is stored in the app's private preferences. PDF pages are rasterized in memory by PDFKit and only those page images are sent to local Ollama. Embedded text and Vision OCR output are bounded, held in memory, and sent only to that same local endpoint. The existing filename is JSON-escaped and explicitly treated as untrusted reference data.

Filesystem handling is defensive:

  • Unsupported files, hidden files, subfolders, and symbolic links are ignored.
  • Existing supported files are baselined during setup instead of unexpectedly renamed.
  • Existing PDFs are baselined once when upgrading from a version that did not support PDFs.
  • A second fingerprint check rejects files modified during model inference.
  • A hard-link-first operation claims the destination atomically before removing the source name.
  • Destination collisions select a numbered suffix instead of overwriting.
  • Undo refuses to run when the original path has been reused, preserving both files.
  • Processing state is written atomically inside the sandbox container.

Engineering decisions

DecisionAlternative consideredWhy this design won
Sandboxed native menu appPython daemon or launchd jobFolder authorization is explicit, persistent, and scoped to one folder instead of inheriting broad terminal or interpreter permissions.
15-second pollingFSEventsA small top-level directory scan is predictable, easy to test, and naturally pairs with the settle window for partially downloaded files.
Local Ollama inferenceHosted vision APIPrivacy and offline operation matter more here than model startup time and disk usage.
JSON schema plus sanitizerFree-form model textModel output remains untrusted even when structured generation succeeds.
Three modes plus deterministic compositionRaw custom promptsCommon workflows stay predictable, testable, and resistant to prompt mistakes while the tool remains focused.
Bounded reference contextAn unrestricted system-prompt editorUsers can add domain vocabulary without weakening visible-evidence rules or turning the app into a prompt workbench.
Dynamic visible correspondentsA manually maintained company listEach document can name its actual issuer while the visible-evidence gate prevents unsupported brand guesses.
Type-specific document recipesOne generic business filenameInvoices, receipts, statements, and other documents include only metadata that is useful for that type.
First three PDF pagesFull-document rendering or OCR storageThree pages usually cover identity, continuation context, and totals while bounding memory, latency, and model context.
Embedded text, then Vision OCROCR every document unconditionallySearchable PDFs stay fast and exact, while scanned documents still receive a fully local fallback.
Keep / rename / review gateRename every supported PDFPreserving a merely adequate name is cheaper than destroying useful dates, references, or source-language meaning.
Debug-only isolated capture harnessRecording against a real Downloads folderThe public demo uses the real app and local model with synthetic PDFs in a temporary folder, so it is authentic without exposing or mutating personal files.
One persisted review pipelineA separate manual renamerAutomatic and reviewed operation share the same sanitizer, collision handling, state, history, and recovery behavior.
Opt-in review for existing usersChanging automation after an updateExisting behavior remains stable while users who want control can enable review explicitly.
Bounded local historyAn unlimited activity databaseThe latest 100 renames provide practical recovery without turning a small utility into a document-management system.
Hard link, then unlinkmoveItem after an existence checkClaiming the destination atomically removes the check-then-write race that could overwrite a file.
Ad-hoc signed release artifactUnsigned bundle or premature App Store packagingThe artifact is reproducible and sandboxed today, while notarization remains an explicit production-distribution follow-up.

Manual CLI

The image-only Python 3.11+ CLI is useful for dry runs, individual files, recursive directories, or non-macOS systems. It is not sandboxed, so the native app is the safer choice for continuous macOS automation.

Preview without changing files:

./bin/image-autonamer --dry-run ~/Downloads

Rename one image:

./bin/image-autonamer ~/Downloads/IMG_8472.PNG

Process a directory tree:

./bin/image-autonamer --recursive /path/to/images

Run ./bin/image-autonamer --help for the complete command reference.

Build and test

Requirements for source builds are macOS 13+, Swift 6, Xcode Command Line Tools, Python 3.11+, and Ollama.

# Native unit tests
swift test# Python unit and contract tests
PYTHONPATH=src python3 -m unittest discover -s tests -v
# Release app and checksum manifest
./scripts/package-release.sh 0.6.1
# Rebuild the 46-second MP4 and GIF from real app-window recordings and original title cards
./scripts/build-demo.sh
# Rebuild the native icon and GitHub social preview
./scripts/build-brand-assets.py

Native tests cover sanitization, first-run protection, PDF text extraction, page rendering, filename decisions, PDF discovery, review queuing, edited approval, rejection, safe renaming, collision handling, history, and undo. Python tests additionally cover the CLI, state database, file settling, discovery, and Ollama request/response contract. CI intentionally mocks Ollama because downloading a multi-gigabyte model on every run would make the suite slow and wasteful.

Project landmarks:

Sources/ImageAutonamerKit/ Local inference and safe processing actor
Sources/ImageAutonamerMac/ Menu bar lifecycle, bookmark, and login item
src/image_autonamer/ Portable Python CLI
tests/ Swift and Python test suites
scripts/ Build, install, package, and demo tooling
docs/ Original demo sources and generated media

See CONTRIBUTING.md for contribution guidelines. See ROADMAP.md for the deliberately narrow product direction and SECURITY.md for private vulnerability reporting.

Troubleshooting

Ollama is unavailable

Open Ollama and verify the local endpoint:

curl http://127.0.0.1:11434/api/tags
ollama pull qwen3-vl:4b

Folder access was cancelled

Open Image Autonamer and choose Grant Access or Reauthorize… in General Settings. Select Downloads and click Allow Downloads. There is no + button to find in System Settings.

The menu bar icon is hidden

macOS may place extra status items behind Control Center when menu bar space is limited. Open Image Autonamer from Finder, Spotlight, or your app switcher to use the full Settings window without the menu bar icon.

The build uses the wrong Swift toolchain

Point the installer at a specific Swift binary:

SWIFT_BIN=/path/to/swift ./scripts/install-macos-app.sh

Uninstall

Open the menu bar icon, turn off Launch at Login, and quit the app. Move ~/Applications/Image Autonamer.app to Trash.

The sandbox container remains at ~/Library/Containers/com.davidvaness.image-autonamer so reinstalling preserves folder authorization and processing state. Remove that container manually if you also want to erase the saved state and bookmark.

License

Image Autonamer, its logo, and the original demo artwork are available under the MIT License.

About

A sandboxed macOS menu bar app that uses a local Ollama vision model to safely rename downloaded images.

Topics

Resources

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Image Autonamer logo

Image Autonamer

Useful filenames. Local vision. Zero cloud uploads.

A sandboxed macOS menu bar app that watches Downloads, understands new images and PDFs with a local vision-language model, and safely renames them.

GitHub releasemacOS 13 or newerSwift 6Ollama local inferenceMIT license

Real Image Autonamer app processing synthetic PDFs with local Ollama inference

Watch the 46-second real-app demo in HD · Download v0.6.1

Install in one command

On an Apple Silicon Mac running macOS 13 or newer:

curl -fsSL https://raw.githubusercontent.com/DavidVaness/image-autonamer/main/scripts/install.sh | sh

The installer downloads the checksum-verified app, installs Ollama through Homebrew if needed, pulls qwen3-vl:4b, and opens Image Autonamer. If you prefer to inspect code before running it, clone the repository and use ./scripts/install-macos-app.sh.

On first launch, the Settings window opens automatically. Click Grant Access, choose Downloads, and click Allow Downloads. That one explicit choice creates a persistent security-scoped bookmark inside the app sandbox. Full Disk Access is not required.

Note

The v0.6.1 binary is ad-hoc signed and supports Apple Silicon. A Developer ID certificate and Apple notarization are the remaining steps for conventional consumer distribution. Intel Macs can build from source.

See it work

This sample is original vector artwork created for this repository and released under the same MIT license as the code. It is safe to reuse in articles, screenshots, and demos.

Original vector illustration of mountains, a sunset, a lake, and pine trees

The checked-in sample was passed through the real local model during release validation:

sample-landscape.png -> mountains-sunset-lake-trees.png

Model wording can vary slightly between Ollama and model versions. Representative everyday results look like this:

BeforeAfterUseful signal captured
Screenshot 2026-08-11.pngpurple-sales-dashboard-bar-chart.pngInterface, color, and primary chart
IMG_8472.JPGred-coffee-mug-beside-laptop.jpgMain objects and their relationship
download.webporange-cat-sleeping-on-sofa.webpSubject, action, and setting
north-star-invoice.pdf2026-08-15-north-star-studio-invoice-inv-2048-invoice-summary-and-payment-notes.pdfDate, visible correspondent, document type, and reference across three pages

The original extension is normalized and preserved. If a filename already exists, Image Autonamer adds -2, -3, and so on without overwriting anything. For PDFs, the existing filename is part of the decision instead of being discarded blindly.

Naming modes

Open Image Autonamer from Finder, Spotlight, or your app switcher to show Settings, then choose the Naming tab. You can also select Configure… from the Naming status row in the menu bar app. The preview button analyzes a selected image or PDF locally and shows the proposed filename without renaming or moving the file.

ModeExample resultBest for
Descriptivered-coffee-mug-beside-laptop.jpgGeneral screenshots, photos, and downloaded images
Date + descriptive2026-08-11-red-coffee-mug-beside-laptop.jpgChronological sorting and camera imports
Documents2026-08-01-example-co-invoice-inv-1042-annual-renewal.pngScans and screenshots of invoices, receipts, statements, contracts, letters, reports, certificates, and tax documents

Date-prefixed names use the image capture date when metadata provides one, then fall back to the file creation or modification date. Documents mode infers the correspondent from the visible issuer, sender, merchant, or wordmark in each file. It classifies the document type and then applies a deterministic type-specific recipe. Invoices can include a visible invoice reference, statements prefer a visible statement period, and receipts omit reference numbers to stay concise. Missing or ambiguous metadata is omitted instead of guessed. An optional Naming context field accepts up to 500 characters of workflow guidance, such as the type of business or image collection. The app normalizes that text and instructs the local model to treat it as reference data without overriding visible-evidence rules. There is intentionally no unrestricted system-prompt editor.

Review Inbox

Enable Review suggestions before renaming in General Settings when you want approval instead of immediate automation. New suggestions then wait in Review Inbox with a thumbnail preview, the original filename, an editable proposed filename, and the visible evidence used to produce it. Approve one suggestion, keep the original name, or approve the whole queue after reviewing edits. Ambiguous PDF improvements are queued even when global review is disabled. PDFs with useful existing names are kept automatically, while generic names are renamed only when the proposal adds visible document information.

Every completed rename appears in History, including renames performed automatically. Undo restores the original filename only when that path is free, so it never overwrites a newer file. The pending queue and the latest 100 rename records survive app restarts in the same atomic state file as scan fingerprints.

Measured locally

The repository includes eight original, MIT-licensed evaluation images plus one synthetic three-page PDF across illustrations, screenshots, objects, documents, diagrams, interfaces, and indoor and outdoor scenes. The harness records the proposed filename, concept coverage, forbidden-concept hits, and latency without renaming the fixtures.

The checked-in qwen3-vl:4b baseline produced useful filenames for all 8 fixtures with a warm-model median local inference time of approximately 0.8 seconds on the release machine. Cold-model latency was higher and varies substantially with hardware and Ollama state. This is a small transparent baseline, not a claim of general model accuracy.

python3 -m pip install -e '.[dev]'
./scripts/build-eval-fixtures.sh
./eval/run.py --model qwen3-vl:4b --output eval/results/qwen3-vl-4b.json

See eval/manifest.json for the explicit rubric and eval/results/qwen3-vl-4b.json for the complete result.

Why this exists

Downloads folders quickly fill with UUIDs, camera counters, and names such as image (12).png. Cloud vision APIs can fix that, but uploading private screenshots and photos solely to rename them is an uncomfortable trade. Image Autonamer keeps inference on the Mac and limits automatic filesystem access to the Downloads folder the user explicitly selects.

What it does

  • Runs image understanding locally through Ollama and qwen3-vl:4b.
  • Watches new top-level images and PDFs in Downloads every 15 seconds.
  • Starts automatically through the native macOS login-item API.
  • Uses the App Sandbox with only user-selected read/write and outbound client entitlements.
  • Shows whether Downloads access is granted and keeps recovery one click away.
  • Protects supported files that existed before first-run setup.
  • Waits for downloads to settle and verifies that a file did not change during analysis.
  • Treats model output as untrusted input and reduces it to a safe lowercase ASCII slug.
  • Extracts embedded PDF text first and uses local Apple Vision OCR only when a scan has no useful text layer.
  • Compares existing and proposed PDF names, then chooses keep, rename, or review conservatively.
  • Offers three focused naming modes, dynamic document recipes, and a no-rename preview instead of an open-ended prompt surface.
  • Offers an opt-in Review Inbox with editable suggestions, visible evidence, bulk approval, and rename history.
  • Makes automatic and approved renames reversible without overwriting files created later.
  • Uses collision-safe filesystem operations that never overwrite another file.
  • Includes a standalone Python CLI for dry runs, one-off images, recursive batches, and Linux.

The native app recognizes AVIF, BMP, GIF, HEIC, HEIF, JPEG, PDF, PNG, TIFF, and WebP by extension. AppKit converts each image to PNG before inference, which gives Ollama a consistent input format. PDFKit renders up to the first three PDF pages to PNG locally before inference. The same pages provide bounded embedded text or an on-device Vision OCR fallback for filename-quality checks. The PDF itself never leaves the Mac and its original .pdf extension is preserved.

How it works

flowchart LR
A["Downloads folder"] --> B["15-second scan"]
B --> C["Settle and fingerprint checks"]
C --> D{"Input type"}
D -->|Image| E["AppKit PNG conversion"]
D -->|PDF| P["PDFKit text + first 3 pages"]
P --> Q["Vision OCR fallback"]
Q --> E
E --> F["Local Ollama VLM"]
F --> G["Structured visible facts"]
G --> H["Deterministic composer and filename quality gate"]
H --> I{"Filename decision"}
I -->|Keep| S["Keep existing name"]
I -->|Review| J["Persisted Review Inbox"]
I -->|Rename| R{"Global review enabled?"}
R -->|Yes| J
J --> K["Approve or keep original"]
R -->|No| L["Automatic approval"]
K --> M["Hard-link-first safe rename"]
L --> M
M --> N["History and collision-safe undo"]
S --> O["Atomic state file"]
N --> O["Atomic state file"]
Loading

ImageProcessor is a Swift actor, so scans cannot mutate state concurrently. Ollama is asked for schema-constrained visible facts at a low temperature. Deterministic code applies the selected naming mode, and the result is still sanitized before touching the filesystem. The source is fingerprinted before and after inference to catch partial or changing downloads.

Security model

The app accepts only the current user's actual Downloads folder in its picker. macOS supplies a security-scoped capability for that folder, and the app persists the capability as a bookmark in its private container. It has no Full Disk Access, no automation entitlement, no telemetry, and no third-party API.

Image bytes are sent to the configured Ollama endpoint, which is hard-coded by default to http://127.0.0.1:11434. The sandbox entitlement technically permits outbound client connections because macOS does not offer a localhost-only network entitlement. The shipped code uses no remote endpoint. Optional naming context is included only in requests to that same local endpoint and is stored in the app's private preferences. PDF pages are rasterized in memory by PDFKit and only those page images are sent to local Ollama. Embedded text and Vision OCR output are bounded, held in memory, and sent only to that same local endpoint. The existing filename is JSON-escaped and explicitly treated as untrusted reference data.

Filesystem handling is defensive:

  • Unsupported files, hidden files, subfolders, and symbolic links are ignored.
  • Existing supported files are baselined during setup instead of unexpectedly renamed.
  • Existing PDFs are baselined once when upgrading from a version that did not support PDFs.
  • A second fingerprint check rejects files modified during model inference.
  • A hard-link-first operation claims the destination atomically before removing the source name.
  • Destination collisions select a numbered suffix instead of overwriting.
  • Undo refuses to run when the original path has been reused, preserving both files.
  • Processing state is written atomically inside the sandbox container.

Engineering decisions

DecisionAlternative consideredWhy this design won
Sandboxed native menu appPython daemon or launchd jobFolder authorization is explicit, persistent, and scoped to one folder instead of inheriting broad terminal or interpreter permissions.
15-second pollingFSEventsA small top-level directory scan is predictable, easy to test, and naturally pairs with the settle window for partially downloaded files.
Local Ollama inferenceHosted vision APIPrivacy and offline operation matter more here than model startup time and disk usage.
JSON schema plus sanitizerFree-form model textModel output remains untrusted even when structured generation succeeds.
Three modes plus deterministic compositionRaw custom promptsCommon workflows stay predictable, testable, and resistant to prompt mistakes while the tool remains focused.
Bounded reference contextAn unrestricted system-prompt editorUsers can add domain vocabulary without weakening visible-evidence rules or turning the app into a prompt workbench.
Dynamic visible correspondentsA manually maintained company listEach document can name its actual issuer while the visible-evidence gate prevents unsupported brand guesses.
Type-specific document recipesOne generic business filenameInvoices, receipts, statements, and other documents include only metadata that is useful for that type.
First three PDF pagesFull-document rendering or OCR storageThree pages usually cover identity, continuation context, and totals while bounding memory, latency, and model context.
Embedded text, then Vision OCROCR every document unconditionallySearchable PDFs stay fast and exact, while scanned documents still receive a fully local fallback.
Keep / rename / review gateRename every supported PDFPreserving a merely adequate name is cheaper than destroying useful dates, references, or source-language meaning.
Debug-only isolated capture harnessRecording against a real Downloads folderThe public demo uses the real app and local model with synthetic PDFs in a temporary folder, so it is authentic without exposing or mutating personal files.
One persisted review pipelineA separate manual renamerAutomatic and reviewed operation share the same sanitizer, collision handling, state, history, and recovery behavior.
Opt-in review for existing usersChanging automation after an updateExisting behavior remains stable while users who want control can enable review explicitly.
Bounded local historyAn unlimited activity databaseThe latest 100 renames provide practical recovery without turning a small utility into a document-management system.
Hard link, then unlinkmoveItem after an existence checkClaiming the destination atomically removes the check-then-write race that could overwrite a file.
Ad-hoc signed release artifactUnsigned bundle or premature App Store packagingThe artifact is reproducible and sandboxed today, while notarization remains an explicit production-distribution follow-up.

Manual CLI

The image-only Python 3.11+ CLI is useful for dry runs, individual files, recursive directories, or non-macOS systems. It is not sandboxed, so the native app is the safer choice for continuous macOS automation.

Preview without changing files:

./bin/image-autonamer --dry-run ~/Downloads

Rename one image:

./bin/image-autonamer ~/Downloads/IMG_8472.PNG

Process a directory tree:

./bin/image-autonamer --recursive /path/to/images

Run ./bin/image-autonamer --help for the complete command reference.

Build and test

Requirements for source builds are macOS 13+, Swift 6, Xcode Command Line Tools, Python 3.11+, and Ollama.

# Native unit tests
swift test# Python unit and contract tests
PYTHONPATH=src python3 -m unittest discover -s tests -v
# Release app and checksum manifest
./scripts/package-release.sh 0.6.1
# Rebuild the 46-second MP4 and GIF from real app-window recordings and original title cards
./scripts/build-demo.sh
# Rebuild the native icon and GitHub social preview
./scripts/build-brand-assets.py

Native tests cover sanitization, first-run protection, PDF text extraction, page rendering, filename decisions, PDF discovery, review queuing, edited approval, rejection, safe renaming, collision handling, history, and undo. Python tests additionally cover the CLI, state database, file settling, discovery, and Ollama request/response contract. CI intentionally mocks Ollama because downloading a multi-gigabyte model on every run would make the suite slow and wasteful.

Project landmarks:

Sources/ImageAutonamerKit/ Local inference and safe processing actor
Sources/ImageAutonamerMac/ Menu bar lifecycle, bookmark, and login item
src/image_autonamer/ Portable Python CLI
tests/ Swift and Python test suites
scripts/ Build, install, package, and demo tooling
docs/ Original demo sources and generated media

See CONTRIBUTING.md for contribution guidelines. See ROADMAP.md for the deliberately narrow product direction and SECURITY.md for private vulnerability reporting.

Troubleshooting

Ollama is unavailable

Open Ollama and verify the local endpoint:

curl http://127.0.0.1:11434/api/tags
ollama pull qwen3-vl:4b

Folder access was cancelled

Open Image Autonamer and choose Grant Access or Reauthorize… in General Settings. Select Downloads and click Allow Downloads. There is no + button to find in System Settings.

The menu bar icon is hidden

macOS may place extra status items behind Control Center when menu bar space is limited. Open Image Autonamer from Finder, Spotlight, or your app switcher to use the full Settings window without the menu bar icon.

The build uses the wrong Swift toolchain

Point the installer at a specific Swift binary:

SWIFT_BIN=/path/to/swift ./scripts/install-macos-app.sh

Uninstall

Open the menu bar icon, turn off Launch at Login, and quit the app. Move ~/Applications/Image Autonamer.app to Trash.

The sandbox container remains at ~/Library/Containers/com.davidvaness.image-autonamer so reinstalling preserves folder authorization and processing state. Remove that container manually if you also want to erase the saved state and bookmark.

License

Image Autonamer, its logo, and the original demo artwork are available under the MIT License.

About

A sandboxed macOS menu bar app that uses a local Ollama vision model to safely rename downloaded images.

Topics

Resources

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Image Autonamer logo

Image Autonamer

Useful filenames. Local vision. Zero cloud uploads.

A sandboxed macOS menu bar app that watches Downloads, understands new images and PDFs with a local vision-language model, and safely renames them.

GitHub releasemacOS 13 or newerSwift 6Ollama local inferenceMIT license

Real Image Autonamer app processing synthetic PDFs with local Ollama inference

Watch the 46-second real-app demo in HD · Download v0.6.1

Install in one command

On an Apple Silicon Mac running macOS 13 or newer:

curl -fsSL https://raw.githubusercontent.com/DavidVaness/image-autonamer/main/scripts/install.sh | sh

The installer downloads the checksum-verified app, installs Ollama through Homebrew if needed, pulls qwen3-vl:4b, and opens Image Autonamer. If you prefer to inspect code before running it, clone the repository and use ./scripts/install-macos-app.sh.

On first launch, the Settings window opens automatically. Click Grant Access, choose Downloads, and click Allow Downloads. That one explicit choice creates a persistent security-scoped bookmark inside the app sandbox. Full Disk Access is not required.

Note

The v0.6.1 binary is ad-hoc signed and supports Apple Silicon. A Developer ID certificate and Apple notarization are the remaining steps for conventional consumer distribution. Intel Macs can build from source.

See it work

This sample is original vector artwork created for this repository and released under the same MIT license as the code. It is safe to reuse in articles, screenshots, and demos.

Original vector illustration of mountains, a sunset, a lake, and pine trees

The checked-in sample was passed through the real local model during release validation:

sample-landscape.png -> mountains-sunset-lake-trees.png

Model wording can vary slightly between Ollama and model versions. Representative everyday results look like this:

BeforeAfterUseful signal captured
Screenshot 2026-08-11.pngpurple-sales-dashboard-bar-chart.pngInterface, color, and primary chart
IMG_8472.JPGred-coffee-mug-beside-laptop.jpgMain objects and their relationship
download.webporange-cat-sleeping-on-sofa.webpSubject, action, and setting
north-star-invoice.pdf2026-08-15-north-star-studio-invoice-inv-2048-invoice-summary-and-payment-notes.pdfDate, visible correspondent, document type, and reference across three pages

The original extension is normalized and preserved. If a filename already exists, Image Autonamer adds -2, -3, and so on without overwriting anything. For PDFs, the existing filename is part of the decision instead of being discarded blindly.

Naming modes

Open Image Autonamer from Finder, Spotlight, or your app switcher to show Settings, then choose the Naming tab. You can also select Configure… from the Naming status row in the menu bar app. The preview button analyzes a selected image or PDF locally and shows the proposed filename without renaming or moving the file.

ModeExample resultBest for
Descriptivered-coffee-mug-beside-laptop.jpgGeneral screenshots, photos, and downloaded images
Date + descriptive2026-08-11-red-coffee-mug-beside-laptop.jpgChronological sorting and camera imports
Documents2026-08-01-example-co-invoice-inv-1042-annual-renewal.pngScans and screenshots of invoices, receipts, statements, contracts, letters, reports, certificates, and tax documents

Date-prefixed names use the image capture date when metadata provides one, then fall back to the file creation or modification date. Documents mode infers the correspondent from the visible issuer, sender, merchant, or wordmark in each file. It classifies the document type and then applies a deterministic type-specific recipe. Invoices can include a visible invoice reference, statements prefer a visible statement period, and receipts omit reference numbers to stay concise. Missing or ambiguous metadata is omitted instead of guessed. An optional Naming context field accepts up to 500 characters of workflow guidance, such as the type of business or image collection. The app normalizes that text and instructs the local model to treat it as reference data without overriding visible-evidence rules. There is intentionally no unrestricted system-prompt editor.

Review Inbox

Enable Review suggestions before renaming in General Settings when you want approval instead of immediate automation. New suggestions then wait in Review Inbox with a thumbnail preview, the original filename, an editable proposed filename, and the visible evidence used to produce it. Approve one suggestion, keep the original name, or approve the whole queue after reviewing edits. Ambiguous PDF improvements are queued even when global review is disabled. PDFs with useful existing names are kept automatically, while generic names are renamed only when the proposal adds visible document information.

Every completed rename appears in History, including renames performed automatically. Undo restores the original filename only when that path is free, so it never overwrites a newer file. The pending queue and the latest 100 rename records survive app restarts in the same atomic state file as scan fingerprints.

Measured locally

The repository includes eight original, MIT-licensed evaluation images plus one synthetic three-page PDF across illustrations, screenshots, objects, documents, diagrams, interfaces, and indoor and outdoor scenes. The harness records the proposed filename, concept coverage, forbidden-concept hits, and latency without renaming the fixtures.

The checked-in qwen3-vl:4b baseline produced useful filenames for all 8 fixtures with a warm-model median local inference time of approximately 0.8 seconds on the release machine. Cold-model latency was higher and varies substantially with hardware and Ollama state. This is a small transparent baseline, not a claim of general model accuracy.

python3 -m pip install -e '.[dev]'
./scripts/build-eval-fixtures.sh
./eval/run.py --model qwen3-vl:4b --output eval/results/qwen3-vl-4b.json

See eval/manifest.json for the explicit rubric and eval/results/qwen3-vl-4b.json for the complete result.

Why this exists

Downloads folders quickly fill with UUIDs, camera counters, and names such as image (12).png. Cloud vision APIs can fix that, but uploading private screenshots and photos solely to rename them is an uncomfortable trade. Image Autonamer keeps inference on the Mac and limits automatic filesystem access to the Downloads folder the user explicitly selects.

What it does

  • Runs image understanding locally through Ollama and qwen3-vl:4b.
  • Watches new top-level images and PDFs in Downloads every 15 seconds.
  • Starts automatically through the native macOS login-item API.
  • Uses the App Sandbox with only user-selected read/write and outbound client entitlements.
  • Shows whether Downloads access is granted and keeps recovery one click away.
  • Protects supported files that existed before first-run setup.
  • Waits for downloads to settle and verifies that a file did not change during analysis.
  • Treats model output as untrusted input and reduces it to a safe lowercase ASCII slug.
  • Extracts embedded PDF text first and uses local Apple Vision OCR only when a scan has no useful text layer.
  • Compares existing and proposed PDF names, then chooses keep, rename, or review conservatively.
  • Offers three focused naming modes, dynamic document recipes, and a no-rename preview instead of an open-ended prompt surface.
  • Offers an opt-in Review Inbox with editable suggestions, visible evidence, bulk approval, and rename history.
  • Makes automatic and approved renames reversible without overwriting files created later.
  • Uses collision-safe filesystem operations that never overwrite another file.
  • Includes a standalone Python CLI for dry runs, one-off images, recursive batches, and Linux.

The native app recognizes AVIF, BMP, GIF, HEIC, HEIF, JPEG, PDF, PNG, TIFF, and WebP by extension. AppKit converts each image to PNG before inference, which gives Ollama a consistent input format. PDFKit renders up to the first three PDF pages to PNG locally before inference. The same pages provide bounded embedded text or an on-device Vision OCR fallback for filename-quality checks. The PDF itself never leaves the Mac and its original .pdf extension is preserved.

How it works

flowchart LR
A["Downloads folder"] --> B["15-second scan"]
B --> C["Settle and fingerprint checks"]
C --> D{"Input type"}
D -->|Image| E["AppKit PNG conversion"]
D -->|PDF| P["PDFKit text + first 3 pages"]
P --> Q["Vision OCR fallback"]
Q --> E
E --> F["Local Ollama VLM"]
F --> G["Structured visible facts"]
G --> H["Deterministic composer and filename quality gate"]
H --> I{"Filename decision"}
I -->|Keep| S["Keep existing name"]
I -->|Review| J["Persisted Review Inbox"]
I -->|Rename| R{"Global review enabled?"}
R -->|Yes| J
J --> K["Approve or keep original"]
R -->|No| L["Automatic approval"]
K --> M["Hard-link-first safe rename"]
L --> M
M --> N["History and collision-safe undo"]
S --> O["Atomic state file"]
N --> O["Atomic state file"]
Loading

ImageProcessor is a Swift actor, so scans cannot mutate state concurrently. Ollama is asked for schema-constrained visible facts at a low temperature. Deterministic code applies the selected naming mode, and the result is still sanitized before touching the filesystem. The source is fingerprinted before and after inference to catch partial or changing downloads.

Security model

The app accepts only the current user's actual Downloads folder in its picker. macOS supplies a security-scoped capability for that folder, and the app persists the capability as a bookmark in its private container. It has no Full Disk Access, no automation entitlement, no telemetry, and no third-party API.

Image bytes are sent to the configured Ollama endpoint, which is hard-coded by default to http://127.0.0.1:11434. The sandbox entitlement technically permits outbound client connections because macOS does not offer a localhost-only network entitlement. The shipped code uses no remote endpoint. Optional naming context is included only in requests to that same local endpoint and is stored in the app's private preferences. PDF pages are rasterized in memory by PDFKit and only those page images are sent to local Ollama. Embedded text and Vision OCR output are bounded, held in memory, and sent only to that same local endpoint. The existing filename is JSON-escaped and explicitly treated as untrusted reference data.

Filesystem handling is defensive:

  • Unsupported files, hidden files, subfolders, and symbolic links are ignored.
  • Existing supported files are baselined during setup instead of unexpectedly renamed.
  • Existing PDFs are baselined once when upgrading from a version that did not support PDFs.
  • A second fingerprint check rejects files modified during model inference.
  • A hard-link-first operation claims the destination atomically before removing the source name.
  • Destination collisions select a numbered suffix instead of overwriting.
  • Undo refuses to run when the original path has been reused, preserving both files.
  • Processing state is written atomically inside the sandbox container.

Engineering decisions

DecisionAlternative consideredWhy this design won
Sandboxed native menu appPython daemon or launchd jobFolder authorization is explicit, persistent, and scoped to one folder instead of inheriting broad terminal or interpreter permissions.
15-second pollingFSEventsA small top-level directory scan is predictable, easy to test, and naturally pairs with the settle window for partially downloaded files.
Local Ollama inferenceHosted vision APIPrivacy and offline operation matter more here than model startup time and disk usage.
JSON schema plus sanitizerFree-form model textModel output remains untrusted even when structured generation succeeds.
Three modes plus deterministic compositionRaw custom promptsCommon workflows stay predictable, testable, and resistant to prompt mistakes while the tool remains focused.
Bounded reference contextAn unrestricted system-prompt editorUsers can add domain vocabulary without weakening visible-evidence rules or turning the app into a prompt workbench.
Dynamic visible correspondentsA manually maintained company listEach document can name its actual issuer while the visible-evidence gate prevents unsupported brand guesses.
Type-specific document recipesOne generic business filenameInvoices, receipts, statements, and other documents include only metadata that is useful for that type.
First three PDF pagesFull-document rendering or OCR storageThree pages usually cover identity, continuation context, and totals while bounding memory, latency, and model context.
Embedded text, then Vision OCROCR every document unconditionallySearchable PDFs stay fast and exact, while scanned documents still receive a fully local fallback.
Keep / rename / review gateRename every supported PDFPreserving a merely adequate name is cheaper than destroying useful dates, references, or source-language meaning.
Debug-only isolated capture harnessRecording against a real Downloads folderThe public demo uses the real app and local model with synthetic PDFs in a temporary folder, so it is authentic without exposing or mutating personal files.
One persisted review pipelineA separate manual renamerAutomatic and reviewed operation share the same sanitizer, collision handling, state, history, and recovery behavior.
Opt-in review for existing usersChanging automation after an updateExisting behavior remains stable while users who want control can enable review explicitly.
Bounded local historyAn unlimited activity databaseThe latest 100 renames provide practical recovery without turning a small utility into a document-management system.
Hard link, then unlinkmoveItem after an existence checkClaiming the destination atomically removes the check-then-write race that could overwrite a file.
Ad-hoc signed release artifactUnsigned bundle or premature App Store packagingThe artifact is reproducible and sandboxed today, while notarization remains an explicit production-distribution follow-up.

Manual CLI

The image-only Python 3.11+ CLI is useful for dry runs, individual files, recursive directories, or non-macOS systems. It is not sandboxed, so the native app is the safer choice for continuous macOS automation.

Preview without changing files:

./bin/image-autonamer --dry-run ~/Downloads

Rename one image:

./bin/image-autonamer ~/Downloads/IMG_8472.PNG

Process a directory tree:

./bin/image-autonamer --recursive /path/to/images

Run ./bin/image-autonamer --help for the complete command reference.

Build and test

Requirements for source builds are macOS 13+, Swift 6, Xcode Command Line Tools, Python 3.11+, and Ollama.

# Native unit tests
swift test# Python unit and contract tests
PYTHONPATH=src python3 -m unittest discover -s tests -v
# Release app and checksum manifest
./scripts/package-release.sh 0.6.1
# Rebuild the 46-second MP4 and GIF from real app-window recordings and original title cards
./scripts/build-demo.sh
# Rebuild the native icon and GitHub social preview
./scripts/build-brand-assets.py

Native tests cover sanitization, first-run protection, PDF text extraction, page rendering, filename decisions, PDF discovery, review queuing, edited approval, rejection, safe renaming, collision handling, history, and undo. Python tests additionally cover the CLI, state database, file settling, discovery, and Ollama request/response contract. CI intentionally mocks Ollama because downloading a multi-gigabyte model on every run would make the suite slow and wasteful.

Project landmarks:

Sources/ImageAutonamerKit/ Local inference and safe processing actor
Sources/ImageAutonamerMac/ Menu bar lifecycle, bookmark, and login item
src/image_autonamer/ Portable Python CLI
tests/ Swift and Python test suites
scripts/ Build, install, package, and demo tooling
docs/ Original demo sources and generated media

See CONTRIBUTING.md for contribution guidelines. See ROADMAP.md for the deliberately narrow product direction and SECURITY.md for private vulnerability reporting.

Troubleshooting

Ollama is unavailable

Open Ollama and verify the local endpoint:

curl http://127.0.0.1:11434/api/tags
ollama pull qwen3-vl:4b

Folder access was cancelled

Open Image Autonamer and choose Grant Access or Reauthorize… in General Settings. Select Downloads and click Allow Downloads. There is no + button to find in System Settings.

The menu bar icon is hidden

macOS may place extra status items behind Control Center when menu bar space is limited. Open Image Autonamer from Finder, Spotlight, or your app switcher to use the full Settings window without the menu bar icon.

The build uses the wrong Swift toolchain

Point the installer at a specific Swift binary:

SWIFT_BIN=/path/to/swift ./scripts/install-macos-app.sh

Uninstall

Open the menu bar icon, turn off Launch at Login, and quit the app. Move ~/Applications/Image Autonamer.app to Trash.

The sandbox container remains at ~/Library/Containers/com.davidvaness.image-autonamer so reinstalling preserves folder authorization and processing state. Remove that container manually if you also want to erase the saved state and bookmark.

License

Image Autonamer, its logo, and the original demo artwork are available under the MIT License.

About

A sandboxed macOS menu bar app that uses a local Ollama vision model to safely rename downloaded images.

Topics

Resources

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

Image Autonamer logo

Image Autonamer

Useful filenames. Local vision. Zero cloud uploads.

A sandboxed macOS menu bar app that watches Downloads, understands new images and PDFs with a local vision-language model, and safely renames them.

GitHub releasemacOS 13 or newerSwift 6Ollama local inferenceMIT license

Real Image Autonamer app processing synthetic PDFs with local Ollama inference

Watch the 46-second real-app demo in HD · Download v0.6.1

Install in one command

On an Apple Silicon Mac running macOS 13 or newer:

curl -fsSL https://raw.githubusercontent.com/DavidVaness/image-autonamer/main/scripts/install.sh | sh

The installer downloads the checksum-verified app, installs Ollama through Homebrew if needed, pulls qwen3-vl:4b, and opens Image Autonamer. If you prefer to inspect code before running it, clone the repository and use ./scripts/install-macos-app.sh.

On first launch, the Settings window opens automatically. Click Grant Access, choose Downloads, and click Allow Downloads. That one explicit choice creates a persistent security-scoped bookmark inside the app sandbox. Full Disk Access is not required.

Note

The v0.6.1 binary is ad-hoc signed and supports Apple Silicon. A Developer ID certificate and Apple notarization are the remaining steps for conventional consumer distribution. Intel Macs can build from source.

See it work

This sample is original vector artwork created for this repository and released under the same MIT license as the code. It is safe to reuse in articles, screenshots, and demos.

Original vector illustration of mountains, a sunset, a lake, and pine trees

The checked-in sample was passed through the real local model during release validation:

sample-landscape.png -> mountains-sunset-lake-trees.png

Model wording can vary slightly between Ollama and model versions. Representative everyday results look like this:

BeforeAfterUseful signal captured
Screenshot 2026-08-11.pngpurple-sales-dashboard-bar-chart.pngInterface, color, and primary chart
IMG_8472.JPGred-coffee-mug-beside-laptop.jpgMain objects and their relationship
download.webporange-cat-sleeping-on-sofa.webpSubject, action, and setting
north-star-invoice.pdf2026-08-15-north-star-studio-invoice-inv-2048-invoice-summary-and-payment-notes.pdfDate, visible correspondent, document type, and reference across three pages

The original extension is normalized and preserved. If a filename already exists, Image Autonamer adds -2, -3, and so on without overwriting anything. For PDFs, the existing filename is part of the decision instead of being discarded blindly.

Naming modes

Open Image Autonamer from Finder, Spotlight, or your app switcher to show Settings, then choose the Naming tab. You can also select Configure… from the Naming status row in the menu bar app. The preview button analyzes a selected image or PDF locally and shows the proposed filename without renaming or moving the file.

ModeExample resultBest for
Descriptivered-coffee-mug-beside-laptop.jpgGeneral screenshots, photos, and downloaded images
Date + descriptive2026-08-11-red-coffee-mug-beside-laptop.jpgChronological sorting and camera imports
Documents2026-08-01-example-co-invoice-inv-1042-annual-renewal.pngScans and screenshots of invoices, receipts, statements, contracts, letters, reports, certificates, and tax documents

Date-prefixed names use the image capture date when metadata provides one, then fall back to the file creation or modification date. Documents mode infers the correspondent from the visible issuer, sender, merchant, or wordmark in each file. It classifies the document type and then applies a deterministic type-specific recipe. Invoices can include a visible invoice reference, statements prefer a visible statement period, and receipts omit reference numbers to stay concise. Missing or ambiguous metadata is omitted instead of guessed. An optional Naming context field accepts up to 500 characters of workflow guidance, such as the type of business or image collection. The app normalizes that text and instructs the local model to treat it as reference data without overriding visible-evidence rules. There is intentionally no unrestricted system-prompt editor.

Review Inbox

Enable Review suggestions before renaming in General Settings when you want approval instead of immediate automation. New suggestions then wait in Review Inbox with a thumbnail preview, the original filename, an editable proposed filename, and the visible evidence used to produce it. Approve one suggestion, keep the original name, or approve the whole queue after reviewing edits. Ambiguous PDF improvements are queued even when global review is disabled. PDFs with useful existing names are kept automatically, while generic names are renamed only when the proposal adds visible document information.

Every completed rename appears in History, including renames performed automatically. Undo restores the original filename only when that path is free, so it never overwrites a newer file. The pending queue and the latest 100 rename records survive app restarts in the same atomic state file as scan fingerprints.

Measured locally

The repository includes eight original, MIT-licensed evaluation images plus one synthetic three-page PDF across illustrations, screenshots, objects, documents, diagrams, interfaces, and indoor and outdoor scenes. The harness records the proposed filename, concept coverage, forbidden-concept hits, and latency without renaming the fixtures.

The checked-in qwen3-vl:4b baseline produced useful filenames for all 8 fixtures with a warm-model median local inference time of approximately 0.8 seconds on the release machine. Cold-model latency was higher and varies substantially with hardware and Ollama state. This is a small transparent baseline, not a claim of general model accuracy.

python3 -m pip install -e '.[dev]'
./scripts/build-eval-fixtures.sh
./eval/run.py --model qwen3-vl:4b --output eval/results/qwen3-vl-4b.json

See eval/manifest.json for the explicit rubric and eval/results/qwen3-vl-4b.json for the complete result.

Why this exists

Downloads folders quickly fill with UUIDs, camera counters, and names such as image (12).png. Cloud vision APIs can fix that, but uploading private screenshots and photos solely to rename them is an uncomfortable trade. Image Autonamer keeps inference on the Mac and limits automatic filesystem access to the Downloads folder the user explicitly selects.

What it does

  • Runs image understanding locally through Ollama and qwen3-vl:4b.
  • Watches new top-level images and PDFs in Downloads every 15 seconds.
  • Starts automatically through the native macOS login-item API.
  • Uses the App Sandbox with only user-selected read/write and outbound client entitlements.
  • Shows whether Downloads access is granted and keeps recovery one click away.
  • Protects supported files that existed before first-run setup.
  • Waits for downloads to settle and verifies that a file did not change during analysis.
  • Treats model output as untrusted input and reduces it to a safe lowercase ASCII slug.
  • Extracts embedded PDF text first and uses local Apple Vision OCR only when a scan has no useful text layer.
  • Compares existing and proposed PDF names, then chooses keep, rename, or review conservatively.
  • Offers three focused naming modes, dynamic document recipes, and a no-rename preview instead of an open-ended prompt surface.
  • Offers an opt-in Review Inbox with editable suggestions, visible evidence, bulk approval, and rename history.
  • Makes automatic and approved renames reversible without overwriting files created later.
  • Uses collision-safe filesystem operations that never overwrite another file.
  • Includes a standalone Python CLI for dry runs, one-off images, recursive batches, and Linux.

The native app recognizes AVIF, BMP, GIF, HEIC, HEIF, JPEG, PDF, PNG, TIFF, and WebP by extension. AppKit converts each image to PNG before inference, which gives Ollama a consistent input format. PDFKit renders up to the first three PDF pages to PNG locally before inference. The same pages provide bounded embedded text or an on-device Vision OCR fallback for filename-quality checks. The PDF itself never leaves the Mac and its original .pdf extension is preserved.

How it works

flowchart LR
A["Downloads folder"] --> B["15-second scan"]
B --> C["Settle and fingerprint checks"]
C --> D{"Input type"}
D -->|Image| E["AppKit PNG conversion"]
D -->|PDF| P["PDFKit text + first 3 pages"]
P --> Q["Vision OCR fallback"]
Q --> E
E --> F["Local Ollama VLM"]
F --> G["Structured visible facts"]
G --> H["Deterministic composer and filename quality gate"]
H --> I{"Filename decision"}
I -->|Keep| S["Keep existing name"]
I -->|Review| J["Persisted Review Inbox"]
I -->|Rename| R{"Global review enabled?"}
R -->|Yes| J
J --> K["Approve or keep original"]
R -->|No| L["Automatic approval"]
K --> M["Hard-link-first safe rename"]
L --> M
M --> N["History and collision-safe undo"]
S --> O["Atomic state file"]
N --> O["Atomic state file"]
Loading

ImageProcessor is a Swift actor, so scans cannot mutate state concurrently. Ollama is asked for schema-constrained visible facts at a low temperature. Deterministic code applies the selected naming mode, and the result is still sanitized before touching the filesystem. The source is fingerprinted before and after inference to catch partial or changing downloads.

Security model

The app accepts only the current user's actual Downloads folder in its picker. macOS supplies a security-scoped capability for that folder, and the app persists the capability as a bookmark in its private container. It has no Full Disk Access, no automation entitlement, no telemetry, and no third-party API.

Image bytes are sent to the configured Ollama endpoint, which is hard-coded by default to http://127.0.0.1:11434. The sandbox entitlement technically permits outbound client connections because macOS does not offer a localhost-only network entitlement. The shipped code uses no remote endpoint. Optional naming context is included only in requests to that same local endpoint and is stored in the app's private preferences. PDF pages are rasterized in memory by PDFKit and only those page images are sent to local Ollama. Embedded text and Vision OCR output are bounded, held in memory, and sent only to that same local endpoint. The existing filename is JSON-escaped and explicitly treated as untrusted reference data.

Filesystem handling is defensive:

  • Unsupported files, hidden files, subfolders, and symbolic links are ignored.
  • Existing supported files are baselined during setup instead of unexpectedly renamed.
  • Existing PDFs are baselined once when upgrading from a version that did not support PDFs.
  • A second fingerprint check rejects files modified during model inference.
  • A hard-link-first operation claims the destination atomically before removing the source name.
  • Destination collisions select a numbered suffix instead of overwriting.
  • Undo refuses to run when the original path has been reused, preserving both files.
  • Processing state is written atomically inside the sandbox container.

Engineering decisions

DecisionAlternative consideredWhy this design won
Sandboxed native menu appPython daemon or launchd jobFolder authorization is explicit, persistent, and scoped to one folder instead of inheriting broad terminal or interpreter permissions.
15-second pollingFSEventsA small top-level directory scan is predictable, easy to test, and naturally pairs with the settle window for partially downloaded files.
Local Ollama inferenceHosted vision APIPrivacy and offline operation matter more here than model startup time and disk usage.
JSON schema plus sanitizerFree-form model textModel output remains untrusted even when structured generation succeeds.
Three modes plus deterministic compositionRaw custom promptsCommon workflows stay predictable, testable, and resistant to prompt mistakes while the tool remains focused.
Bounded reference contextAn unrestricted system-prompt editorUsers can add domain vocabulary without weakening visible-evidence rules or turning the app into a prompt workbench.
Dynamic visible correspondentsA manually maintained company listEach document can name its actual issuer while the visible-evidence gate prevents unsupported brand guesses.
Type-specific document recipesOne generic business filenameInvoices, receipts, statements, and other documents include only metadata that is useful for that type.
First three PDF pagesFull-document rendering or OCR storageThree pages usually cover identity, continuation context, and totals while bounding memory, latency, and model context.
Embedded text, then Vision OCROCR every document unconditionallySearchable PDFs stay fast and exact, while scanned documents still receive a fully local fallback.
Keep / rename / review gateRename every supported PDFPreserving a merely adequate name is cheaper than destroying useful dates, references, or source-language meaning.
Debug-only isolated capture harnessRecording against a real Downloads folderThe public demo uses the real app and local model with synthetic PDFs in a temporary folder, so it is authentic without exposing or mutating personal files.
One persisted review pipelineA separate manual renamerAutomatic and reviewed operation share the same sanitizer, collision handling, state, history, and recovery behavior.
Opt-in review for existing usersChanging automation after an updateExisting behavior remains stable while users who want control can enable review explicitly.
Bounded local historyAn unlimited activity databaseThe latest 100 renames provide practical recovery without turning a small utility into a document-management system.
Hard link, then unlinkmoveItem after an existence checkClaiming the destination atomically removes the check-then-write race that could overwrite a file.
Ad-hoc signed release artifactUnsigned bundle or premature App Store packagingThe artifact is reproducible and sandboxed today, while notarization remains an explicit production-distribution follow-up.

Manual CLI

The image-only Python 3.11+ CLI is useful for dry runs, individual files, recursive directories, or non-macOS systems. It is not sandboxed, so the native app is the safer choice for continuous macOS automation.

Preview without changing files:

./bin/image-autonamer --dry-run ~/Downloads

Rename one image:

./bin/image-autonamer ~/Downloads/IMG_8472.PNG

Process a directory tree:

./bin/image-autonamer --recursive /path/to/images

Run ./bin/image-autonamer --help for the complete command reference.

Build and test

Requirements for source builds are macOS 13+, Swift 6, Xcode Command Line Tools, Python 3.11+, and Ollama.

# Native unit tests
swift test# Python unit and contract tests
PYTHONPATH=src python3 -m unittest discover -s tests -v
# Release app and checksum manifest
./scripts/package-release.sh 0.6.1
# Rebuild the 46-second MP4 and GIF from real app-window recordings and original title cards
./scripts/build-demo.sh
# Rebuild the native icon and GitHub social preview
./scripts/build-brand-assets.py

Native tests cover sanitization, first-run protection, PDF text extraction, page rendering, filename decisions, PDF discovery, review queuing, edited approval, rejection, safe renaming, collision handling, history, and undo. Python tests additionally cover the CLI, state database, file settling, discovery, and Ollama request/response contract. CI intentionally mocks Ollama because downloading a multi-gigabyte model on every run would make the suite slow and wasteful.

Project landmarks:

Sources/ImageAutonamerKit/ Local inference and safe processing actor
Sources/ImageAutonamerMac/ Menu bar lifecycle, bookmark, and login item
src/image_autonamer/ Portable Python CLI
tests/ Swift and Python test suites
scripts/ Build, install, package, and demo tooling
docs/ Original demo sources and generated media

See CONTRIBUTING.md for contribution guidelines. See ROADMAP.md for the deliberately narrow product direction and SECURITY.md for private vulnerability reporting.

Troubleshooting

Ollama is unavailable

Open Ollama and verify the local endpoint:

curl http://127.0.0.1:11434/api/tags
ollama pull qwen3-vl:4b

Folder access was cancelled

Open Image Autonamer and choose Grant Access or Reauthorize… in General Settings. Select Downloads and click Allow Downloads. There is no + button to find in System Settings.

The menu bar icon is hidden

macOS may place extra status items behind Control Center when menu bar space is limited. Open Image Autonamer from Finder, Spotlight, or your app switcher to use the full Settings window without the menu bar icon.

The build uses the wrong Swift toolchain

Point the installer at a specific Swift binary:

SWIFT_BIN=/path/to/swift ./scripts/install-macos-app.sh

Uninstall

Open the menu bar icon, turn off Launch at Login, and quit the app. Move ~/Applications/Image Autonamer.app to Trash.

The sandbox container remains at ~/Library/Containers/com.davidvaness.image-autonamer so reinstalling preserves folder authorization and processing state. Remove that container manually if you also want to erase the saved state and bookmark.

License

Image Autonamer, its logo, and the original demo artwork are available under the MIT License.

About

A sandboxed macOS menu bar app that uses a local Ollama vision model to safely rename downloaded images.

Topics

Resources

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Image Autonamer logo

Image Autonamer

Useful filenames. Local vision. Zero cloud uploads.

A sandboxed macOS menu bar app that watches Downloads, understands new images and PDFs with a local vision-language model, and safely renames them.

GitHub releasemacOS 13 or newerSwift 6Ollama local inferenceMIT license

Real Image Autonamer app processing synthetic PDFs with local Ollama inference

Watch the 46-second real-app demo in HD · Download v0.6.1

Install in one command

On an Apple Silicon Mac running macOS 13 or newer:

curl -fsSL https://raw.githubusercontent.com/DavidVaness/image-autonamer/main/scripts/install.sh | sh

The installer downloads the checksum-verified app, installs Ollama through Homebrew if needed, pulls qwen3-vl:4b, and opens Image Autonamer. If you prefer to inspect code before running it, clone the repository and use ./scripts/install-macos-app.sh.

On first launch, the Settings window opens automatically. Click Grant Access, choose Downloads, and click Allow Downloads. That one explicit choice creates a persistent security-scoped bookmark inside the app sandbox. Full Disk Access is not required.

Note

The v0.6.1 binary is ad-hoc signed and supports Apple Silicon. A Developer ID certificate and Apple notarization are the remaining steps for conventional consumer distribution. Intel Macs can build from source.

See it work

This sample is original vector artwork created for this repository and released under the same MIT license as the code. It is safe to reuse in articles, screenshots, and demos.

Original vector illustration of mountains, a sunset, a lake, and pine trees

The checked-in sample was passed through the real local model during release validation:

sample-landscape.png -> mountains-sunset-lake-trees.png

Model wording can vary slightly between Ollama and model versions. Representative everyday results look like this:

BeforeAfterUseful signal captured
Screenshot 2026-08-11.pngpurple-sales-dashboard-bar-chart.pngInterface, color, and primary chart
IMG_8472.JPGred-coffee-mug-beside-laptop.jpgMain objects and their relationship
download.webporange-cat-sleeping-on-sofa.webpSubject, action, and setting
north-star-invoice.pdf2026-08-15-north-star-studio-invoice-inv-2048-invoice-summary-and-payment-notes.pdfDate, visible correspondent, document type, and reference across three pages

The original extension is normalized and preserved. If a filename already exists, Image Autonamer adds -2, -3, and so on without overwriting anything. For PDFs, the existing filename is part of the decision instead of being discarded blindly.

Naming modes

Open Image Autonamer from Finder, Spotlight, or your app switcher to show Settings, then choose the Naming tab. You can also select Configure… from the Naming status row in the menu bar app. The preview button analyzes a selected image or PDF locally and shows the proposed filename without renaming or moving the file.

ModeExample resultBest for
Descriptivered-coffee-mug-beside-laptop.jpgGeneral screenshots, photos, and downloaded images
Date + descriptive2026-08-11-red-coffee-mug-beside-laptop.jpgChronological sorting and camera imports
Documents2026-08-01-example-co-invoice-inv-1042-annual-renewal.pngScans and screenshots of invoices, receipts, statements, contracts, letters, reports, certificates, and tax documents

Date-prefixed names use the image capture date when metadata provides one, then fall back to the file creation or modification date. Documents mode infers the correspondent from the visible issuer, sender, merchant, or wordmark in each file. It classifies the document type and then applies a deterministic type-specific recipe. Invoices can include a visible invoice reference, statements prefer a visible statement period, and receipts omit reference numbers to stay concise. Missing or ambiguous metadata is omitted instead of guessed. An optional Naming context field accepts up to 500 characters of workflow guidance, such as the type of business or image collection. The app normalizes that text and instructs the local model to treat it as reference data without overriding visible-evidence rules. There is intentionally no unrestricted system-prompt editor.

Review Inbox

Enable Review suggestions before renaming in General Settings when you want approval instead of immediate automation. New suggestions then wait in Review Inbox with a thumbnail preview, the original filename, an editable proposed filename, and the visible evidence used to produce it. Approve one suggestion, keep the original name, or approve the whole queue after reviewing edits. Ambiguous PDF improvements are queued even when global review is disabled. PDFs with useful existing names are kept automatically, while generic names are renamed only when the proposal adds visible document information.

Every completed rename appears in History, including renames performed automatically. Undo restores the original filename only when that path is free, so it never overwrites a newer file. The pending queue and the latest 100 rename records survive app restarts in the same atomic state file as scan fingerprints.

Measured locally

The repository includes eight original, MIT-licensed evaluation images plus one synthetic three-page PDF across illustrations, screenshots, objects, documents, diagrams, interfaces, and indoor and outdoor scenes. The harness records the proposed filename, concept coverage, forbidden-concept hits, and latency without renaming the fixtures.

The checked-in qwen3-vl:4b baseline produced useful filenames for all 8 fixtures with a warm-model median local inference time of approximately 0.8 seconds on the release machine. Cold-model latency was higher and varies substantially with hardware and Ollama state. This is a small transparent baseline, not a claim of general model accuracy.

python3 -m pip install -e '.[dev]'
./scripts/build-eval-fixtures.sh
./eval/run.py --model qwen3-vl:4b --output eval/results/qwen3-vl-4b.json

See eval/manifest.json for the explicit rubric and eval/results/qwen3-vl-4b.json for the complete result.

Why this exists

Downloads folders quickly fill with UUIDs, camera counters, and names such as image (12).png. Cloud vision APIs can fix that, but uploading private screenshots and photos solely to rename them is an uncomfortable trade. Image Autonamer keeps inference on the Mac and limits automatic filesystem access to the Downloads folder the user explicitly selects.

What it does

  • Runs image understanding locally through Ollama and qwen3-vl:4b.
  • Watches new top-level images and PDFs in Downloads every 15 seconds.
  • Starts automatically through the native macOS login-item API.
  • Uses the App Sandbox with only user-selected read/write and outbound client entitlements.
  • Shows whether Downloads access is granted and keeps recovery one click away.
  • Protects supported files that existed before first-run setup.
  • Waits for downloads to settle and verifies that a file did not change during analysis.
  • Treats model output as untrusted input and reduces it to a safe lowercase ASCII slug.
  • Extracts embedded PDF text first and uses local Apple Vision OCR only when a scan has no useful text layer.
  • Compares existing and proposed PDF names, then chooses keep, rename, or review conservatively.
  • Offers three focused naming modes, dynamic document recipes, and a no-rename preview instead of an open-ended prompt surface.
  • Offers an opt-in Review Inbox with editable suggestions, visible evidence, bulk approval, and rename history.
  • Makes automatic and approved renames reversible without overwriting files created later.
  • Uses collision-safe filesystem operations that never overwrite another file.
  • Includes a standalone Python CLI for dry runs, one-off images, recursive batches, and Linux.

The native app recognizes AVIF, BMP, GIF, HEIC, HEIF, JPEG, PDF, PNG, TIFF, and WebP by extension. AppKit converts each image to PNG before inference, which gives Ollama a consistent input format. PDFKit renders up to the first three PDF pages to PNG locally before inference. The same pages provide bounded embedded text or an on-device Vision OCR fallback for filename-quality checks. The PDF itself never leaves the Mac and its original .pdf extension is preserved.

How it works

flowchart LR
A["Downloads folder"] --> B["15-second scan"]
B --> C["Settle and fingerprint checks"]
C --> D{"Input type"}
D -->|Image| E["AppKit PNG conversion"]
D -->|PDF| P["PDFKit text + first 3 pages"]
P --> Q["Vision OCR fallback"]
Q --> E
E --> F["Local Ollama VLM"]
F --> G["Structured visible facts"]
G --> H["Deterministic composer and filename quality gate"]
H --> I{"Filename decision"}
I -->|Keep| S["Keep existing name"]
I -->|Review| J["Persisted Review Inbox"]
I -->|Rename| R{"Global review enabled?"}
R -->|Yes| J
J --> K["Approve or keep original"]
R -->|No| L["Automatic approval"]
K --> M["Hard-link-first safe rename"]
L --> M
M --> N["History and collision-safe undo"]
S --> O["Atomic state file"]
N --> O["Atomic state file"]
Loading

ImageProcessor is a Swift actor, so scans cannot mutate state concurrently. Ollama is asked for schema-constrained visible facts at a low temperature. Deterministic code applies the selected naming mode, and the result is still sanitized before touching the filesystem. The source is fingerprinted before and after inference to catch partial or changing downloads.

Security model

The app accepts only the current user's actual Downloads folder in its picker. macOS supplies a security-scoped capability for that folder, and the app persists the capability as a bookmark in its private container. It has no Full Disk Access, no automation entitlement, no telemetry, and no third-party API.

Image bytes are sent to the configured Ollama endpoint, which is hard-coded by default to http://127.0.0.1:11434. The sandbox entitlement technically permits outbound client connections because macOS does not offer a localhost-only network entitlement. The shipped code uses no remote endpoint. Optional naming context is included only in requests to that same local endpoint and is stored in the app's private preferences. PDF pages are rasterized in memory by PDFKit and only those page images are sent to local Ollama. Embedded text and Vision OCR output are bounded, held in memory, and sent only to that same local endpoint. The existing filename is JSON-escaped and explicitly treated as untrusted reference data.

Filesystem handling is defensive:

  • Unsupported files, hidden files, subfolders, and symbolic links are ignored.
  • Existing supported files are baselined during setup instead of unexpectedly renamed.
  • Existing PDFs are baselined once when upgrading from a version that did not support PDFs.
  • A second fingerprint check rejects files modified during model inference.
  • A hard-link-first operation claims the destination atomically before removing the source name.
  • Destination collisions select a numbered suffix instead of overwriting.
  • Undo refuses to run when the original path has been reused, preserving both files.
  • Processing state is written atomically inside the sandbox container.

Engineering decisions

DecisionAlternative consideredWhy this design won
Sandboxed native menu appPython daemon or launchd jobFolder authorization is explicit, persistent, and scoped to one folder instead of inheriting broad terminal or interpreter permissions.
15-second pollingFSEventsA small top-level directory scan is predictable, easy to test, and naturally pairs with the settle window for partially downloaded files.
Local Ollama inferenceHosted vision APIPrivacy and offline operation matter more here than model startup time and disk usage.
JSON schema plus sanitizerFree-form model textModel output remains untrusted even when structured generation succeeds.
Three modes plus deterministic compositionRaw custom promptsCommon workflows stay predictable, testable, and resistant to prompt mistakes while the tool remains focused.
Bounded reference contextAn unrestricted system-prompt editorUsers can add domain vocabulary without weakening visible-evidence rules or turning the app into a prompt workbench.
Dynamic visible correspondentsA manually maintained company listEach document can name its actual issuer while the visible-evidence gate prevents unsupported brand guesses.
Type-specific document recipesOne generic business filenameInvoices, receipts, statements, and other documents include only metadata that is useful for that type.
First three PDF pagesFull-document rendering or OCR storageThree pages usually cover identity, continuation context, and totals while bounding memory, latency, and model context.
Embedded text, then Vision OCROCR every document unconditionallySearchable PDFs stay fast and exact, while scanned documents still receive a fully local fallback.
Keep / rename / review gateRename every supported PDFPreserving a merely adequate name is cheaper than destroying useful dates, references, or source-language meaning.
Debug-only isolated capture harnessRecording against a real Downloads folderThe public demo uses the real app and local model with synthetic PDFs in a temporary folder, so it is authentic without exposing or mutating personal files.
One persisted review pipelineA separate manual renamerAutomatic and reviewed operation share the same sanitizer, collision handling, state, history, and recovery behavior.
Opt-in review for existing usersChanging automation after an updateExisting behavior remains stable while users who want control can enable review explicitly.
Bounded local historyAn unlimited activity databaseThe latest 100 renames provide practical recovery without turning a small utility into a document-management system.
Hard link, then unlinkmoveItem after an existence checkClaiming the destination atomically removes the check-then-write race that could overwrite a file.
Ad-hoc signed release artifactUnsigned bundle or premature App Store packagingThe artifact is reproducible and sandboxed today, while notarization remains an explicit production-distribution follow-up.

Manual CLI

The image-only Python 3.11+ CLI is useful for dry runs, individual files, recursive directories, or non-macOS systems. It is not sandboxed, so the native app is the safer choice for continuous macOS automation.

Preview without changing files:

./bin/image-autonamer --dry-run ~/Downloads

Rename one image:

./bin/image-autonamer ~/Downloads/IMG_8472.PNG

Process a directory tree:

./bin/image-autonamer --recursive /path/to/images

Run ./bin/image-autonamer --help for the complete command reference.

Build and test

Requirements for source builds are macOS 13+, Swift 6, Xcode Command Line Tools, Python 3.11+, and Ollama.

# Native unit tests
swift test# Python unit and contract tests
PYTHONPATH=src python3 -m unittest discover -s tests -v
# Release app and checksum manifest
./scripts/package-release.sh 0.6.1
# Rebuild the 46-second MP4 and GIF from real app-window recordings and original title cards
./scripts/build-demo.sh
# Rebuild the native icon and GitHub social preview
./scripts/build-brand-assets.py

Native tests cover sanitization, first-run protection, PDF text extraction, page rendering, filename decisions, PDF discovery, review queuing, edited approval, rejection, safe renaming, collision handling, history, and undo. Python tests additionally cover the CLI, state database, file settling, discovery, and Ollama request/response contract. CI intentionally mocks Ollama because downloading a multi-gigabyte model on every run would make the suite slow and wasteful.

Project landmarks:

Sources/ImageAutonamerKit/ Local inference and safe processing actor
Sources/ImageAutonamerMac/ Menu bar lifecycle, bookmark, and login item
src/image_autonamer/ Portable Python CLI
tests/ Swift and Python test suites
scripts/ Build, install, package, and demo tooling
docs/ Original demo sources and generated media

See CONTRIBUTING.md for contribution guidelines. See ROADMAP.md for the deliberately narrow product direction and SECURITY.md for private vulnerability reporting.

Troubleshooting

Ollama is unavailable

Open Ollama and verify the local endpoint:

curl http://127.0.0.1:11434/api/tags
ollama pull qwen3-vl:4b

Folder access was cancelled

Open Image Autonamer and choose Grant Access or Reauthorize… in General Settings. Select Downloads and click Allow Downloads. There is no + button to find in System Settings.

The menu bar icon is hidden

macOS may place extra status items behind Control Center when menu bar space is limited. Open Image Autonamer from Finder, Spotlight, or your app switcher to use the full Settings window without the menu bar icon.

The build uses the wrong Swift toolchain

Point the installer at a specific Swift binary:

SWIFT_BIN=/path/to/swift ./scripts/install-macos-app.sh

Uninstall

Open the menu bar icon, turn off Launch at Login, and quit the app. Move ~/Applications/Image Autonamer.app to Trash.

The sandbox container remains at ~/Library/Containers/com.davidvaness.image-autonamer so reinstalling preserves folder authorization and processing state. Remove that container manually if you also want to erase the saved state and bookmark.

License

Image Autonamer, its logo, and the original demo artwork are available under the MIT License.

About

A sandboxed macOS menu bar app that uses a local Ollama vision model to safely rename downloaded images.

Topics

Resources

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Image Autonamer logo

Image Autonamer

Useful filenames. Local vision. Zero cloud uploads.

A sandboxed macOS menu bar app that watches Downloads, understands new images and PDFs with a local vision-language model, and safely renames them.

GitHub releasemacOS 13 or newerSwift 6Ollama local inferenceMIT license

Real Image Autonamer app processing synthetic PDFs with local Ollama inference

Watch the 46-second real-app demo in HD · Download v0.6.1

Install in one command

On an Apple Silicon Mac running macOS 13 or newer:

curl -fsSL https://raw.githubusercontent.com/DavidVaness/image-autonamer/main/scripts/install.sh | sh

The installer downloads the checksum-verified app, installs Ollama through Homebrew if needed, pulls qwen3-vl:4b, and opens Image Autonamer. If you prefer to inspect code before running it, clone the repository and use ./scripts/install-macos-app.sh.

On first launch, the Settings window opens automatically. Click Grant Access, choose Downloads, and click Allow Downloads. That one explicit choice creates a persistent security-scoped bookmark inside the app sandbox. Full Disk Access is not required.

Note

The v0.6.1 binary is ad-hoc signed and supports Apple Silicon. A Developer ID certificate and Apple notarization are the remaining steps for conventional consumer distribution. Intel Macs can build from source.

See it work

This sample is original vector artwork created for this repository and released under the same MIT license as the code. It is safe to reuse in articles, screenshots, and demos.

Original vector illustration of mountains, a sunset, a lake, and pine trees

The checked-in sample was passed through the real local model during release validation:

sample-landscape.png -> mountains-sunset-lake-trees.png

Model wording can vary slightly between Ollama and model versions. Representative everyday results look like this:

BeforeAfterUseful signal captured
Screenshot 2026-08-11.pngpurple-sales-dashboard-bar-chart.pngInterface, color, and primary chart
IMG_8472.JPGred-coffee-mug-beside-laptop.jpgMain objects and their relationship
download.webporange-cat-sleeping-on-sofa.webpSubject, action, and setting
north-star-invoice.pdf2026-08-15-north-star-studio-invoice-inv-2048-invoice-summary-and-payment-notes.pdfDate, visible correspondent, document type, and reference across three pages

The original extension is normalized and preserved. If a filename already exists, Image Autonamer adds -2, -3, and so on without overwriting anything. For PDFs, the existing filename is part of the decision instead of being discarded blindly.

Naming modes

Open Image Autonamer from Finder, Spotlight, or your app switcher to show Settings, then choose the Naming tab. You can also select Configure… from the Naming status row in the menu bar app. The preview button analyzes a selected image or PDF locally and shows the proposed filename without renaming or moving the file.

ModeExample resultBest for
Descriptivered-coffee-mug-beside-laptop.jpgGeneral screenshots, photos, and downloaded images
Date + descriptive2026-08-11-red-coffee-mug-beside-laptop.jpgChronological sorting and camera imports
Documents2026-08-01-example-co-invoice-inv-1042-annual-renewal.pngScans and screenshots of invoices, receipts, statements, contracts, letters, reports, certificates, and tax documents

Date-prefixed names use the image capture date when metadata provides one, then fall back to the file creation or modification date. Documents mode infers the correspondent from the visible issuer, sender, merchant, or wordmark in each file. It classifies the document type and then applies a deterministic type-specific recipe. Invoices can include a visible invoice reference, statements prefer a visible statement period, and receipts omit reference numbers to stay concise. Missing or ambiguous metadata is omitted instead of guessed. An optional Naming context field accepts up to 500 characters of workflow guidance, such as the type of business or image collection. The app normalizes that text and instructs the local model to treat it as reference data without overriding visible-evidence rules. There is intentionally no unrestricted system-prompt editor.

Review Inbox

Enable Review suggestions before renaming in General Settings when you want approval instead of immediate automation. New suggestions then wait in Review Inbox with a thumbnail preview, the original filename, an editable proposed filename, and the visible evidence used to produce it. Approve one suggestion, keep the original name, or approve the whole queue after reviewing edits. Ambiguous PDF improvements are queued even when global review is disabled. PDFs with useful existing names are kept automatically, while generic names are renamed only when the proposal adds visible document information.

Every completed rename appears in History, including renames performed automatically. Undo restores the original filename only when that path is free, so it never overwrites a newer file. The pending queue and the latest 100 rename records survive app restarts in the same atomic state file as scan fingerprints.

Measured locally

The repository includes eight original, MIT-licensed evaluation images plus one synthetic three-page PDF across illustrations, screenshots, objects, documents, diagrams, interfaces, and indoor and outdoor scenes. The harness records the proposed filename, concept coverage, forbidden-concept hits, and latency without renaming the fixtures.

The checked-in qwen3-vl:4b baseline produced useful filenames for all 8 fixtures with a warm-model median local inference time of approximately 0.8 seconds on the release machine. Cold-model latency was higher and varies substantially with hardware and Ollama state. This is a small transparent baseline, not a claim of general model accuracy.

python3 -m pip install -e '.[dev]'
./scripts/build-eval-fixtures.sh
./eval/run.py --model qwen3-vl:4b --output eval/results/qwen3-vl-4b.json

See eval/manifest.json for the explicit rubric and eval/results/qwen3-vl-4b.json for the complete result.

Why this exists

Downloads folders quickly fill with UUIDs, camera counters, and names such as image (12).png. Cloud vision APIs can fix that, but uploading private screenshots and photos solely to rename them is an uncomfortable trade. Image Autonamer keeps inference on the Mac and limits automatic filesystem access to the Downloads folder the user explicitly selects.

What it does

  • Runs image understanding locally through Ollama and qwen3-vl:4b.
  • Watches new top-level images and PDFs in Downloads every 15 seconds.
  • Starts automatically through the native macOS login-item API.
  • Uses the App Sandbox with only user-selected read/write and outbound client entitlements.
  • Shows whether Downloads access is granted and keeps recovery one click away.
  • Protects supported files that existed before first-run setup.
  • Waits for downloads to settle and verifies that a file did not change during analysis.
  • Treats model output as untrusted input and reduces it to a safe lowercase ASCII slug.
  • Extracts embedded PDF text first and uses local Apple Vision OCR only when a scan has no useful text layer.
  • Compares existing and proposed PDF names, then chooses keep, rename, or review conservatively.
  • Offers three focused naming modes, dynamic document recipes, and a no-rename preview instead of an open-ended prompt surface.
  • Offers an opt-in Review Inbox with editable suggestions, visible evidence, bulk approval, and rename history.
  • Makes automatic and approved renames reversible without overwriting files created later.
  • Uses collision-safe filesystem operations that never overwrite another file.
  • Includes a standalone Python CLI for dry runs, one-off images, recursive batches, and Linux.

The native app recognizes AVIF, BMP, GIF, HEIC, HEIF, JPEG, PDF, PNG, TIFF, and WebP by extension. AppKit converts each image to PNG before inference, which gives Ollama a consistent input format. PDFKit renders up to the first three PDF pages to PNG locally before inference. The same pages provide bounded embedded text or an on-device Vision OCR fallback for filename-quality checks. The PDF itself never leaves the Mac and its original .pdf extension is preserved.

How it works

flowchart LR
A["Downloads folder"] --> B["15-second scan"]
B --> C["Settle and fingerprint checks"]
C --> D{"Input type"}
D -->|Image| E["AppKit PNG conversion"]
D -->|PDF| P["PDFKit text + first 3 pages"]
P --> Q["Vision OCR fallback"]
Q --> E
E --> F["Local Ollama VLM"]
F --> G["Structured visible facts"]
G --> H["Deterministic composer and filename quality gate"]
H --> I{"Filename decision"}
I -->|Keep| S["Keep existing name"]
I -->|Review| J["Persisted Review Inbox"]
I -->|Rename| R{"Global review enabled?"}
R -->|Yes| J
J --> K["Approve or keep original"]
R -->|No| L["Automatic approval"]
K --> M["Hard-link-first safe rename"]
L --> M
M --> N["History and collision-safe undo"]
S --> O["Atomic state file"]
N --> O["Atomic state file"]
Loading

ImageProcessor is a Swift actor, so scans cannot mutate state concurrently. Ollama is asked for schema-constrained visible facts at a low temperature. Deterministic code applies the selected naming mode, and the result is still sanitized before touching the filesystem. The source is fingerprinted before and after inference to catch partial or changing downloads.

Security model

The app accepts only the current user's actual Downloads folder in its picker. macOS supplies a security-scoped capability for that folder, and the app persists the capability as a bookmark in its private container. It has no Full Disk Access, no automation entitlement, no telemetry, and no third-party API.

Image bytes are sent to the configured Ollama endpoint, which is hard-coded by default to http://127.0.0.1:11434. The sandbox entitlement technically permits outbound client connections because macOS does not offer a localhost-only network entitlement. The shipped code uses no remote endpoint. Optional naming context is included only in requests to that same local endpoint and is stored in the app's private preferences. PDF pages are rasterized in memory by PDFKit and only those page images are sent to local Ollama. Embedded text and Vision OCR output are bounded, held in memory, and sent only to that same local endpoint. The existing filename is JSON-escaped and explicitly treated as untrusted reference data.

Filesystem handling is defensive:

  • Unsupported files, hidden files, subfolders, and symbolic links are ignored.
  • Existing supported files are baselined during setup instead of unexpectedly renamed.
  • Existing PDFs are baselined once when upgrading from a version that did not support PDFs.
  • A second fingerprint check rejects files modified during model inference.
  • A hard-link-first operation claims the destination atomically before removing the source name.
  • Destination collisions select a numbered suffix instead of overwriting.
  • Undo refuses to run when the original path has been reused, preserving both files.
  • Processing state is written atomically inside the sandbox container.

Engineering decisions

DecisionAlternative consideredWhy this design won
Sandboxed native menu appPython daemon or launchd jobFolder authorization is explicit, persistent, and scoped to one folder instead of inheriting broad terminal or interpreter permissions.
15-second pollingFSEventsA small top-level directory scan is predictable, easy to test, and naturally pairs with the settle window for partially downloaded files.
Local Ollama inferenceHosted vision APIPrivacy and offline operation matter more here than model startup time and disk usage.
JSON schema plus sanitizerFree-form model textModel output remains untrusted even when structured generation succeeds.
Three modes plus deterministic compositionRaw custom promptsCommon workflows stay predictable, testable, and resistant to prompt mistakes while the tool remains focused.
Bounded reference contextAn unrestricted system-prompt editorUsers can add domain vocabulary without weakening visible-evidence rules or turning the app into a prompt workbench.
Dynamic visible correspondentsA manually maintained company listEach document can name its actual issuer while the visible-evidence gate prevents unsupported brand guesses.
Type-specific document recipesOne generic business filenameInvoices, receipts, statements, and other documents include only metadata that is useful for that type.
First three PDF pagesFull-document rendering or OCR storageThree pages usually cover identity, continuation context, and totals while bounding memory, latency, and model context.
Embedded text, then Vision OCROCR every document unconditionallySearchable PDFs stay fast and exact, while scanned documents still receive a fully local fallback.
Keep / rename / review gateRename every supported PDFPreserving a merely adequate name is cheaper than destroying useful dates, references, or source-language meaning.
Debug-only isolated capture harnessRecording against a real Downloads folderThe public demo uses the real app and local model with synthetic PDFs in a temporary folder, so it is authentic without exposing or mutating personal files.
One persisted review pipelineA separate manual renamerAutomatic and reviewed operation share the same sanitizer, collision handling, state, history, and recovery behavior.
Opt-in review for existing usersChanging automation after an updateExisting behavior remains stable while users who want control can enable review explicitly.
Bounded local historyAn unlimited activity databaseThe latest 100 renames provide practical recovery without turning a small utility into a document-management system.
Hard link, then unlinkmoveItem after an existence checkClaiming the destination atomically removes the check-then-write race that could overwrite a file.
Ad-hoc signed release artifactUnsigned bundle or premature App Store packagingThe artifact is reproducible and sandboxed today, while notarization remains an explicit production-distribution follow-up.

Manual CLI

The image-only Python 3.11+ CLI is useful for dry runs, individual files, recursive directories, or non-macOS systems. It is not sandboxed, so the native app is the safer choice for continuous macOS automation.

Preview without changing files:

./bin/image-autonamer --dry-run ~/Downloads

Rename one image:

./bin/image-autonamer ~/Downloads/IMG_8472.PNG

Process a directory tree:

./bin/image-autonamer --recursive /path/to/images

Run ./bin/image-autonamer --help for the complete command reference.

Build and test

Requirements for source builds are macOS 13+, Swift 6, Xcode Command Line Tools, Python 3.11+, and Ollama.

# Native unit tests
swift test# Python unit and contract tests
PYTHONPATH=src python3 -m unittest discover -s tests -v
# Release app and checksum manifest
./scripts/package-release.sh 0.6.1
# Rebuild the 46-second MP4 and GIF from real app-window recordings and original title cards
./scripts/build-demo.sh
# Rebuild the native icon and GitHub social preview
./scripts/build-brand-assets.py

Native tests cover sanitization, first-run protection, PDF text extraction, page rendering, filename decisions, PDF discovery, review queuing, edited approval, rejection, safe renaming, collision handling, history, and undo. Python tests additionally cover the CLI, state database, file settling, discovery, and Ollama request/response contract. CI intentionally mocks Ollama because downloading a multi-gigabyte model on every run would make the suite slow and wasteful.

Project landmarks:

Sources/ImageAutonamerKit/ Local inference and safe processing actor
Sources/ImageAutonamerMac/ Menu bar lifecycle, bookmark, and login item
src/image_autonamer/ Portable Python CLI
tests/ Swift and Python test suites
scripts/ Build, install, package, and demo tooling
docs/ Original demo sources and generated media

See CONTRIBUTING.md for contribution guidelines. See ROADMAP.md for the deliberately narrow product direction and SECURITY.md for private vulnerability reporting.

Troubleshooting

Ollama is unavailable

Open Ollama and verify the local endpoint:

curl http://127.0.0.1:11434/api/tags
ollama pull qwen3-vl:4b

Folder access was cancelled

Open Image Autonamer and choose Grant Access or Reauthorize… in General Settings. Select Downloads and click Allow Downloads. There is no + button to find in System Settings.

The menu bar icon is hidden

macOS may place extra status items behind Control Center when menu bar space is limited. Open Image Autonamer from Finder, Spotlight, or your app switcher to use the full Settings window without the menu bar icon.

The build uses the wrong Swift toolchain

Point the installer at a specific Swift binary:

SWIFT_BIN=/path/to/swift ./scripts/install-macos-app.sh

Uninstall

Open the menu bar icon, turn off Launch at Login, and quit the app. Move ~/Applications/Image Autonamer.app to Trash.

The sandbox container remains at ~/Library/Containers/com.davidvaness.image-autonamer so reinstalling preserves folder authorization and processing state. Remove that container manually if you also want to erase the saved state and bookmark.

License

Image Autonamer, its logo, and the original demo artwork are available under the MIT License.

About

A sandboxed macOS menu bar app that uses a local Ollama vision model to safely rename downloaded images.

Topics

Resources

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

Image Autonamer logo

Image Autonamer

Useful filenames. Local vision. Zero cloud uploads.

A sandboxed macOS menu bar app that watches Downloads, understands new images and PDFs with a local vision-language model, and safely renames them.

GitHub releasemacOS 13 or newerSwift 6Ollama local inferenceMIT license

Real Image Autonamer app processing synthetic PDFs with local Ollama inference

Watch the 46-second real-app demo in HD · Download v0.6.1

Install in one command

On an Apple Silicon Mac running macOS 13 or newer:

curl -fsSL https://raw.githubusercontent.com/DavidVaness/image-autonamer/main/scripts/install.sh | sh

The installer downloads the checksum-verified app, installs Ollama through Homebrew if needed, pulls qwen3-vl:4b, and opens Image Autonamer. If you prefer to inspect code before running it, clone the repository and use ./scripts/install-macos-app.sh.

On first launch, the Settings window opens automatically. Click Grant Access, choose Downloads, and click Allow Downloads. That one explicit choice creates a persistent security-scoped bookmark inside the app sandbox. Full Disk Access is not required.

Note

The v0.6.1 binary is ad-hoc signed and supports Apple Silicon. A Developer ID certificate and Apple notarization are the remaining steps for conventional consumer distribution. Intel Macs can build from source.

See it work

This sample is original vector artwork created for this repository and released under the same MIT license as the code. It is safe to reuse in articles, screenshots, and demos.

Original vector illustration of mountains, a sunset, a lake, and pine trees

The checked-in sample was passed through the real local model during release validation:

sample-landscape.png -> mountains-sunset-lake-trees.png

Model wording can vary slightly between Ollama and model versions. Representative everyday results look like this:

BeforeAfterUseful signal captured
Screenshot 2026-08-11.pngpurple-sales-dashboard-bar-chart.pngInterface, color, and primary chart
IMG_8472.JPGred-coffee-mug-beside-laptop.jpgMain objects and their relationship
download.webporange-cat-sleeping-on-sofa.webpSubject, action, and setting
north-star-invoice.pdf2026-08-15-north-star-studio-invoice-inv-2048-invoice-summary-and-payment-notes.pdfDate, visible correspondent, document type, and reference across three pages

The original extension is normalized and preserved. If a filename already exists, Image Autonamer adds -2, -3, and so on without overwriting anything. For PDFs, the existing filename is part of the decision instead of being discarded blindly.

Naming modes

Open Image Autonamer from Finder, Spotlight, or your app switcher to show Settings, then choose the Naming tab. You can also select Configure… from the Naming status row in the menu bar app. The preview button analyzes a selected image or PDF locally and shows the proposed filename without renaming or moving the file.

ModeExample resultBest for
Descriptivered-coffee-mug-beside-laptop.jpgGeneral screenshots, photos, and downloaded images
Date + descriptive2026-08-11-red-coffee-mug-beside-laptop.jpgChronological sorting and camera imports
Documents2026-08-01-example-co-invoice-inv-1042-annual-renewal.pngScans and screenshots of invoices, receipts, statements, contracts, letters, reports, certificates, and tax documents

Date-prefixed names use the image capture date when metadata provides one, then fall back to the file creation or modification date. Documents mode infers the correspondent from the visible issuer, sender, merchant, or wordmark in each file. It classifies the document type and then applies a deterministic type-specific recipe. Invoices can include a visible invoice reference, statements prefer a visible statement period, and receipts omit reference numbers to stay concise. Missing or ambiguous metadata is omitted instead of guessed. An optional Naming context field accepts up to 500 characters of workflow guidance, such as the type of business or image collection. The app normalizes that text and instructs the local model to treat it as reference data without overriding visible-evidence rules. There is intentionally no unrestricted system-prompt editor.

Review Inbox

Enable Review suggestions before renaming in General Settings when you want approval instead of immediate automation. New suggestions then wait in Review Inbox with a thumbnail preview, the original filename, an editable proposed filename, and the visible evidence used to produce it. Approve one suggestion, keep the original name, or approve the whole queue after reviewing edits. Ambiguous PDF improvements are queued even when global review is disabled. PDFs with useful existing names are kept automatically, while generic names are renamed only when the proposal adds visible document information.

Every completed rename appears in History, including renames performed automatically. Undo restores the original filename only when that path is free, so it never overwrites a newer file. The pending queue and the latest 100 rename records survive app restarts in the same atomic state file as scan fingerprints.

Measured locally

The repository includes eight original, MIT-licensed evaluation images plus one synthetic three-page PDF across illustrations, screenshots, objects, documents, diagrams, interfaces, and indoor and outdoor scenes. The harness records the proposed filename, concept coverage, forbidden-concept hits, and latency without renaming the fixtures.

The checked-in qwen3-vl:4b baseline produced useful filenames for all 8 fixtures with a warm-model median local inference time of approximately 0.8 seconds on the release machine. Cold-model latency was higher and varies substantially with hardware and Ollama state. This is a small transparent baseline, not a claim of general model accuracy.

python3 -m pip install -e '.[dev]'
./scripts/build-eval-fixtures.sh
./eval/run.py --model qwen3-vl:4b --output eval/results/qwen3-vl-4b.json

See eval/manifest.json for the explicit rubric and eval/results/qwen3-vl-4b.json for the complete result.

Why this exists

Downloads folders quickly fill with UUIDs, camera counters, and names such as image (12).png. Cloud vision APIs can fix that, but uploading private screenshots and photos solely to rename them is an uncomfortable trade. Image Autonamer keeps inference on the Mac and limits automatic filesystem access to the Downloads folder the user explicitly selects.

What it does

  • Runs image understanding locally through Ollama and qwen3-vl:4b.
  • Watches new top-level images and PDFs in Downloads every 15 seconds.
  • Starts automatically through the native macOS login-item API.
  • Uses the App Sandbox with only user-selected read/write and outbound client entitlements.
  • Shows whether Downloads access is granted and keeps recovery one click away.
  • Protects supported files that existed before first-run setup.
  • Waits for downloads to settle and verifies that a file did not change during analysis.
  • Treats model output as untrusted input and reduces it to a safe lowercase ASCII slug.
  • Extracts embedded PDF text first and uses local Apple Vision OCR only when a scan has no useful text layer.
  • Compares existing and proposed PDF names, then chooses keep, rename, or review conservatively.
  • Offers three focused naming modes, dynamic document recipes, and a no-rename preview instead of an open-ended prompt surface.
  • Offers an opt-in Review Inbox with editable suggestions, visible evidence, bulk approval, and rename history.
  • Makes automatic and approved renames reversible without overwriting files created later.
  • Uses collision-safe filesystem operations that never overwrite another file.
  • Includes a standalone Python CLI for dry runs, one-off images, recursive batches, and Linux.

The native app recognizes AVIF, BMP, GIF, HEIC, HEIF, JPEG, PDF, PNG, TIFF, and WebP by extension. AppKit converts each image to PNG before inference, which gives Ollama a consistent input format. PDFKit renders up to the first three PDF pages to PNG locally before inference. The same pages provide bounded embedded text or an on-device Vision OCR fallback for filename-quality checks. The PDF itself never leaves the Mac and its original .pdf extension is preserved.

How it works

flowchart LR
A["Downloads folder"] --> B["15-second scan"]
B --> C["Settle and fingerprint checks"]
C --> D{"Input type"}
D -->|Image| E["AppKit PNG conversion"]
D -->|PDF| P["PDFKit text + first 3 pages"]
P --> Q["Vision OCR fallback"]
Q --> E
E --> F["Local Ollama VLM"]
F --> G["Structured visible facts"]
G --> H["Deterministic composer and filename quality gate"]
H --> I{"Filename decision"}
I -->|Keep| S["Keep existing name"]
I -->|Review| J["Persisted Review Inbox"]
I -->|Rename| R{"Global review enabled?"}
R -->|Yes| J
J --> K["Approve or keep original"]
R -->|No| L["Automatic approval"]
K --> M["Hard-link-first safe rename"]
L --> M
M --> N["History and collision-safe undo"]
S --> O["Atomic state file"]
N --> O["Atomic state file"]
Loading

ImageProcessor is a Swift actor, so scans cannot mutate state concurrently. Ollama is asked for schema-constrained visible facts at a low temperature. Deterministic code applies the selected naming mode, and the result is still sanitized before touching the filesystem. The source is fingerprinted before and after inference to catch partial or changing downloads.

Security model

The app accepts only the current user's actual Downloads folder in its picker. macOS supplies a security-scoped capability for that folder, and the app persists the capability as a bookmark in its private container. It has no Full Disk Access, no automation entitlement, no telemetry, and no third-party API.

Image bytes are sent to the configured Ollama endpoint, which is hard-coded by default to http://127.0.0.1:11434. The sandbox entitlement technically permits outbound client connections because macOS does not offer a localhost-only network entitlement. The shipped code uses no remote endpoint. Optional naming context is included only in requests to that same local endpoint and is stored in the app's private preferences. PDF pages are rasterized in memory by PDFKit and only those page images are sent to local Ollama. Embedded text and Vision OCR output are bounded, held in memory, and sent only to that same local endpoint. The existing filename is JSON-escaped and explicitly treated as untrusted reference data.

Filesystem handling is defensive:

  • Unsupported files, hidden files, subfolders, and symbolic links are ignored.
  • Existing supported files are baselined during setup instead of unexpectedly renamed.
  • Existing PDFs are baselined once when upgrading from a version that did not support PDFs.
  • A second fingerprint check rejects files modified during model inference.
  • A hard-link-first operation claims the destination atomically before removing the source name.
  • Destination collisions select a numbered suffix instead of overwriting.
  • Undo refuses to run when the original path has been reused, preserving both files.
  • Processing state is written atomically inside the sandbox container.

Engineering decisions

DecisionAlternative consideredWhy this design won
Sandboxed native menu appPython daemon or launchd jobFolder authorization is explicit, persistent, and scoped to one folder instead of inheriting broad terminal or interpreter permissions.
15-second pollingFSEventsA small top-level directory scan is predictable, easy to test, and naturally pairs with the settle window for partially downloaded files.
Local Ollama inferenceHosted vision APIPrivacy and offline operation matter more here than model startup time and disk usage.
JSON schema plus sanitizerFree-form model textModel output remains untrusted even when structured generation succeeds.
Three modes plus deterministic compositionRaw custom promptsCommon workflows stay predictable, testable, and resistant to prompt mistakes while the tool remains focused.
Bounded reference contextAn unrestricted system-prompt editorUsers can add domain vocabulary without weakening visible-evidence rules or turning the app into a prompt workbench.
Dynamic visible correspondentsA manually maintained company listEach document can name its actual issuer while the visible-evidence gate prevents unsupported brand guesses.
Type-specific document recipesOne generic business filenameInvoices, receipts, statements, and other documents include only metadata that is useful for that type.
First three PDF pagesFull-document rendering or OCR storageThree pages usually cover identity, continuation context, and totals while bounding memory, latency, and model context.
Embedded text, then Vision OCROCR every document unconditionallySearchable PDFs stay fast and exact, while scanned documents still receive a fully local fallback.
Keep / rename / review gateRename every supported PDFPreserving a merely adequate name is cheaper than destroying useful dates, references, or source-language meaning.
Debug-only isolated capture harnessRecording against a real Downloads folderThe public demo uses the real app and local model with synthetic PDFs in a temporary folder, so it is authentic without exposing or mutating personal files.
One persisted review pipelineA separate manual renamerAutomatic and reviewed operation share the same sanitizer, collision handling, state, history, and recovery behavior.
Opt-in review for existing usersChanging automation after an updateExisting behavior remains stable while users who want control can enable review explicitly.
Bounded local historyAn unlimited activity databaseThe latest 100 renames provide practical recovery without turning a small utility into a document-management system.
Hard link, then unlinkmoveItem after an existence checkClaiming the destination atomically removes the check-then-write race that could overwrite a file.
Ad-hoc signed release artifactUnsigned bundle or premature App Store packagingThe artifact is reproducible and sandboxed today, while notarization remains an explicit production-distribution follow-up.

Manual CLI

The image-only Python 3.11+ CLI is useful for dry runs, individual files, recursive directories, or non-macOS systems. It is not sandboxed, so the native app is the safer choice for continuous macOS automation.

Preview without changing files:

./bin/image-autonamer --dry-run ~/Downloads

Rename one image:

./bin/image-autonamer ~/Downloads/IMG_8472.PNG

Process a directory tree:

./bin/image-autonamer --recursive /path/to/images

Run ./bin/image-autonamer --help for the complete command reference.

Build and test

Requirements for source builds are macOS 13+, Swift 6, Xcode Command Line Tools, Python 3.11+, and Ollama.

# Native unit tests
swift test# Python unit and contract tests
PYTHONPATH=src python3 -m unittest discover -s tests -v
# Release app and checksum manifest
./scripts/package-release.sh 0.6.1
# Rebuild the 46-second MP4 and GIF from real app-window recordings and original title cards
./scripts/build-demo.sh
# Rebuild the native icon and GitHub social preview
./scripts/build-brand-assets.py

Native tests cover sanitization, first-run protection, PDF text extraction, page rendering, filename decisions, PDF discovery, review queuing, edited approval, rejection, safe renaming, collision handling, history, and undo. Python tests additionally cover the CLI, state database, file settling, discovery, and Ollama request/response contract. CI intentionally mocks Ollama because downloading a multi-gigabyte model on every run would make the suite slow and wasteful.

Project landmarks:

Sources/ImageAutonamerKit/ Local inference and safe processing actor
Sources/ImageAutonamerMac/ Menu bar lifecycle, bookmark, and login item
src/image_autonamer/ Portable Python CLI
tests/ Swift and Python test suites
scripts/ Build, install, package, and demo tooling
docs/ Original demo sources and generated media

See CONTRIBUTING.md for contribution guidelines. See ROADMAP.md for the deliberately narrow product direction and SECURITY.md for private vulnerability reporting.

Troubleshooting

Ollama is unavailable

Open Ollama and verify the local endpoint:

curl http://127.0.0.1:11434/api/tags
ollama pull qwen3-vl:4b

Folder access was cancelled

Open Image Autonamer and choose Grant Access or Reauthorize… in General Settings. Select Downloads and click Allow Downloads. There is no + button to find in System Settings.

The menu bar icon is hidden

macOS may place extra status items behind Control Center when menu bar space is limited. Open Image Autonamer from Finder, Spotlight, or your app switcher to use the full Settings window without the menu bar icon.

The build uses the wrong Swift toolchain

Point the installer at a specific Swift binary:

SWIFT_BIN=/path/to/swift ./scripts/install-macos-app.sh

Uninstall

Open the menu bar icon, turn off Launch at Login, and quit the app. Move ~/Applications/Image Autonamer.app to Trash.

The sandbox container remains at ~/Library/Containers/com.davidvaness.image-autonamer so reinstalling preserves folder authorization and processing state. Remove that container manually if you also want to erase the saved state and bookmark.

License

Image Autonamer, its logo, and the original demo artwork are available under the MIT License.

About

A sandboxed macOS menu bar app that uses a local Ollama vision model to safely rename downloaded images.

Topics

Resources

Contributing

Security policy

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages