WatchDock
WatchDock is a review-first file organizer for Windows, macOS, and Linux. It can watch folders, ask a cloud or local model for a proposed category and filename, and keep the proposal in a durable approval queue. If no model is available, it falls back to deterministic extension rules and still leaves the source file in place until you approve the action.
WatchDock is alpha software that can rename or move files. Start with a test
folder, keep a backup, and stay in the default hitl mode until you have reviewed
its proposals.
What it does
- Watches one or more folders for stable new or changed files.
- Creates a proposed category, filename, tags, and destination.
- Queues every proposal in the default human-in-the-loop (
hitl) mode. - Can automatically apply only a validated, high-confidence provider result in
automode. - Uses a durable SQLite queue shared by the CLI and GUI.
- Sanitizes path components and Windows reserved filenames, keeps the original extension, and avoids overwriting an existing file.
- Writes portable tag metadata in a JSON sidecar next to the organized file.
- Falls back to offline rules when a provider is unavailable or returns invalid
output. A fallback result is always review-only, even in
automode.
Requirements and installation
WatchDock requires Python 3.10 or newer. A base install includes the CLI, folder watching, GUI code, and offline rules, but not the optional model-provider SDKs.
In PowerShell or Command Prompt:
python -m pip install --upgrade pip
python -m pip install watchdock
Choose an extra when you want an AI provider:
# OpenAI, or an Ollama server with an OpenAI-compatible endpoint
python -m pip install "watchdock[openai]"
# Anthropic
python -m pip install "watchdock[anthropic]"
# Both provider SDKs
python -m pip install "watchdock[ai]"
PowerShell needs the quotes around a requirement containing brackets. On Windows,
py -m pip is also valid if the Python launcher is installed. Tkinter is supplied
by most standard Python installers; watchdock gui reports an error if it is not
available.
The base install can produce review-only rules proposals. Because the generated
configuration names OpenAI as its provider, watchdock doctor reports a missing
OpenAI SDK as a warning in safe hitl mode and as an error in auto mode. If you
change provider, install its required extra; Ollama also needs the openai extra,
even though its current doctor check only displays the configured endpoint.
Install from source
git clone https://github.com/Z-MarkUs/watchdock.git
cd watchdock
python -m venv .venv
.\.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install -e ".[dev]"
If PowerShell blocks activation, use .venv\Scripts\activate.bat from Command
Prompt, or run .venv\Scripts\python.exe directly. See CONTRIBUTING.md
for checks used by the project.
Quick start
Initialization and monitoring are explicit; running watchdock with no command
shows help and does not start a watcher.
watchdock config init
watchdock config validate
watchdock doctor
watchdock start
watchdock config init creates a review-first configuration. Inspect it before
starting. The default watches your Downloads folder non-recursively, archives
under Documents\Archive, and uses hitl mode. watchdock start runs in the
foreground until you press Ctrl+C or close the terminal.
For OpenAI or Anthropic, prefer an environment variable instead of putting a key in JSON. These PowerShell assignments last for the current terminal only:
$env:WATCHDOCK_OPENAI_API_KEY = "<your OpenAI API key>"
# Or, when ai_config.provider is "anthropic":
$env:WATCHDOCK_ANTHROPIC_API_KEY = "<your Anthropic API key>"
Standard OPENAI_API_KEY and ANTHROPIC_API_KEY variables are also recognized.
The WatchDock-prefixed variable takes precedence. Open a new terminal or remove
the variable after testing if you do not want the key to remain in that process.
A safe Windows sandbox walkthrough
This walkthrough isolates both the files and WatchDock state under a temporary
directory. Install watchdock[openai] but do not set an API key; this deliberately
exercises the offline, review-only fallback without making a cloud request.
python -m pip install "watchdock[openai]"
$Sandbox = Join-Path $env:TEMP "watchdock-sandbox"
$Inbox = Join-Path $Sandbox "inbox"
$Archive = Join-Path $Sandbox "archive"
$env:WATCHDOCK_HOME = Join-Path $Sandbox "state"
New-Item -ItemType Directory -Force $Inbox, $Archive | Out-Null
watchdock config init
$ConfigPath = Join-Path $env:WATCHDOCK_HOME "config.json"
$Config = Get-Content -Raw $ConfigPath | ConvertFrom-Json
$Config.watched_folders[0].path = $Inbox
$Config.archive_config.base_path = $Archive
$Config.mode = "hitl"
$Json = $Config | ConvertTo-Json -Depth 6
$Utf8NoBom = New-Object System.Text.UTF8Encoding($false)
[IO.File]::WriteAllText($ConfigPath, $Json + [Environment]::NewLine, $Utf8NoBom)
watchdock config validate
watchdock doctor
"Quarterly planning notes" | Set-Content (Join-Path $Inbox "sample notes.txt")
watchdock process (Join-Path $Inbox "sample notes.txt")
The final command is a dry run and prints an analysis and proposal. With no API
key, the output has "analysis_source": "rules" and
"requires_review": true; the file remains in the inbox. Queue and review that
same proposal:
watchdock process (Join-Path $Inbox "sample notes.txt") --queue
watchdock list-pending
watchdock approve ACTION_ID
Approval moves the file under the sandbox archive. To test continuous watching,
run watchdock start in that PowerShell window, create another file from a second
window using the same WATCHDOCK_HOME, then return to the first window and press
Ctrl+C. Environment variables are per-process, so set WATCHDOCK_HOME again in
each new terminal or pass the configuration explicitly:
watchdock --config "$Sandbox\state\config.json" status
Remove the sandbox when you have finished and confirmed it contains nothing you need.
Review modes
Human-in-the-loop (hitl, default)
Every watcher result is stored as pending. Nothing is moved or renamed until
you approve it with the CLI or GUI. Rejection records the decision without
touching the source.
Automatic (auto)
A provider result is applied automatically only after it passes WatchDock's
structured validation and is marked high confidence. Missing credentials, a
missing SDK, a provider error, or invalid provider output produces a low-confidence
rules result with requires_review=true; WatchDock queues it and does not move the
source. Auto mode is not recommended until you have tested your exact folders,
provider, model, and archive rules.
CLI reference
Both watchdock and wd invoke the same CLI.
watchdock --help
watchdock --version
watchdock version
watchdock version --check
watchdock update
watchdock config init
watchdock config init --force
watchdock config validate
watchdock config show
watchdock status
watchdock status --json
watchdock doctor
watchdock gui
watchdock start
watchdock process FILE
watchdock process FILE --queue
watchdock process FILE --apply
watchdock list-pending
watchdock list-pending --all
watchdock approve ACTION_ID
watchdock reject ACTION_ID
watchdock retry ACTION_ID
watchdock recover-stale
watchdock recover-stale --older-than SECONDS
config init --forcereplaces an existing configuration; use it deliberately.config showredacts a non-empty inline API key.doctorchecks configuration, watched folders, archive writability, provider package/credentials, and the SQLite queue. For Ollama it reports the configured endpoint but does not perform a network health check.process FILEis a dry run.--queuestores the proposal.--applyperforms a fresh analysis and immediately applies it if it is high confidence; it refuses fallback results but is independent of the configured HITL mode. Dry-run first, then use the queue when you need approval of one frozen proposal.list-pendingincludes pending and failed actions;--allalso includes processing, completed, and rejected history.retrychanges a failed action back to pending; it does not execute it. Runapproveafter reviewing it again.recover-stalemarks oldprocessingclaims as failed for manual reconciliation. Startup does this conservatively for claims older than 24 hours. Because a crashed process may have moved a file before recording completion, inspect both source and destination before retrying.updateupgrades a pip installation after checking PyPI. A standalone build cannot self-update.--config PATHis accepted before or after a subcommand and uses the selected configuration's parent directory for its queue, examples, and logs.
Configuration
The generated JSON is intentionally complete and can be edited directly. A portable sample is in config.example.json.
{
"watched_folders": [
{
"path": "~/Downloads",
"enabled": true,
"recursive": false,
"file_extensions": null
}
],
"ai_config": {
"provider": "openai",
"api_key": null,
"model": "gpt-5.6-luna",
"base_url": null,
"temperature": 0.3
},
"archive_config": {
"base_path": "~/Documents/Archive",
"create_date_folders": true,
"create_category_folders": true,
"move_files": true
},
"log_level": "INFO",
"check_interval": 1.0,
"mode": "hitl"
}
~ expands to the current user's home directory on Windows, macOS, and Linux.
file_extensions may be null for all files or a list such as
[".pdf", ".txt"]. Validation rejects duplicate watched paths and any enabled
watched folder that contains, equals, or is contained by the archive path; this
prevents archive feedback loops.
When move_files is true, the default layout is
Archive/YYYY-MM/Category/filename.ext. When false, WatchDock only renames the
file in its source folder. If a new automatic proposal collides with an existing
name, WatchDock adds _1, _2, and so on. A reviewed action instead fails when
its exact destination has become occupied, because silently changing an approved
destination would invalidate the review.
For Ollama, install the openai or ai extra, set provider to ollama, choose
a model available on your server, and set an OpenAI-compatible URL such as
http://localhost:11434/v1. Provider/model availability is deployment-specific;
the default model shown above may need to be changed for your account or server.
State, logs, and tags
By default WatchDock stores its local state in %USERPROFILE%\.watchdock on
Windows and ~/.watchdock elsewhere:
| Purpose | Default path |
|---|---|
| Configuration | ~/.watchdock/config.json |
| Review queue and history | ~/.watchdock/pending_actions.sqlite3 |
| Few-shot examples | ~/.watchdock/few_shot_examples.json |
| Active log | ~/.watchdock/logs/watchdock.log |
Logs rotate at about 2 MiB and retain three backups. SQLite may create temporary
-wal and -shm companion files while WatchDock is running.
Set WATCHDOCK_HOME before running a command to relocate all default state:
$env:WATCHDOCK_HOME = "D:\WatchDockState"
watchdock config init
Each non-empty tag list is stored next to the final file as
filename.ext.watchdock.json. This is a portable JSON sidecar, not an NTFS,
Finder, or Linux extended-attribute tag. The watcher ignores these sidecars.
Content analysis and privacy
For supported text files, WatchDock reads at most 5,000 UTF-8 characters locally
and includes at most the first 2,000 characters in a provider prompt. Supported
preview extensions are .txt, .md, .rst, .py, .js, .ts, .json,
.xml, .csv, .log, .yaml, .yml, and .toml; files identified by the
operating system as text/* may also be previewed. Invalid UTF-8 bytes are
replaced for the preview.
PDF, Office, image, audio, video, archive, and other binary contents are not parsed, OCR'd, or transcribed. Those files are classified from filename, extension, size, and MIME type only.
When OpenAI or Anthropic is configured and available, WatchDock sends that
metadata, the optional text excerpt, and up to five sanitized few-shot examples
to the selected provider. The OpenAI integration uses the Responses API with a
strict structured-output schema and sends store=false. That request setting is
not a promise of zero retention or zero transient processing; review the
provider's current terms, account controls, and logging policy before sending
sensitive material. Anthropic data handling likewise depends on your provider
agreement. Avoid sensitive watched folders unless you have assessed those terms.
Ollama requests go to the configured endpoint. Whether that endpoint is local and what it records depends on your Ollama deployment. With no usable client, WatchDock's deterministic rules fallback does not make a provider request.
More detail is in docs/SECURITY.md.
GUI and foreground monitoring
watchdock gui opens the Tk desktop UI for configuration, examples, monitoring,
and review actions. Monitoring started by the GUI runs only while that GUI process
is open. Start Monitor first validates and saves the current settings, then
runs the monitor on a background thread within that process. The GUI blocks Save
and Reload while monitoring so review actions cannot diverge from the running
configuration; stop the monitor before changing settings. watchdock start
likewise runs in the current terminal. WatchDock does not currently install a
Windows service, a macOS LaunchAgent, a Linux systemd unit, a tray process, or an
automatic-start task.
The GUI needs a working graphical display and Tk. The CLI is the supported option for headless sessions. Standalone application archives may be produced for tagged releases, but they are unsigned and cannot self-update; verify release checksums and expect operating-system warnings where applicable.
Recovery and current limitations
- There is no undo command. To reverse an approved move, stop WatchDock, inspect
the completed action with
watchdock list-pending --all, and move the file back manually. Move or remove its.watchdock.jsonsidecar with it. - Before backing up state, stop the CLI watcher and GUI, then copy the entire WatchDock state directory so the SQLite database and any companion files remain consistent.
- If a queued source changes after review, approval fails and retains the action
as
failed; re-analyze the file rather than trusting the stale proposal. - If an action fails, inspect its error with
watchdock list-pending, correct the cause, runwatchdock retry ACTION_ID, review again, and then approve it. - If WatchDock exits during approval, use
watchdock recover-staleonly after checking whether the source or destination already reflects the action. The command intentionally records an uncertain claim as failed rather than guessing that it completed. - A file move/rename and sidecar write are not one transaction. A sidecar failure is logged as a warning after the file operation may already have succeeded.
- File arrival detection is best-effort and depends on the operating system and filesystem. WatchDock debounces events, waits for a file to stabilize, ignores common partial-download suffixes, and retries transient failures, but you should still test applications that write files in unusual ways.
- There is no built-in archive browser, duplicate-content detection, rollback, encryption, provider billing control, or binary-document understanding.
See docs/ARCHITECTURE.md for the processing flow and trust boundaries.
License
Metadata
Release files for watchdock 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| watchdock-0.2.0.tar.gz | 91.0 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| watchdock-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 155.1 kB
Release files / watchdock-0.2.0.tar.gz
| Download URL | watchdock-0.2.0.tar.gz |
|---|---|
| Size | 91.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f3f900d331ce2d3956e61b1389ecf01d57063337c8083b5c4b9c8022f19ac41a
|
|
BLAKE2b-256 checksum How to use checksums |
ec94de3e50281f54b3241628ccb3abcb0256dcd9c34dcfbce8bc2773a5c4ac5b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|
Release files / watchdock-0.2.0-py3-none-any.whl
| Download URL | watchdock-0.2.0-py3-none-any.whl |
|---|---|
| Size | 64.1 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
77111264f5254bf0ccd66330b60994344adbd687b2e71b4480558ffd3f91abe5
|
|
BLAKE2b-256 checksum How to use checksums |
2d4602abe41f23e3a33ba77a7a2ca1f4c052f041111f296ead0cc55999928e48
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.14
|