nomnom is an extensible file watcher for local automation. The core detects file changes and fires events. Plugins do the actual work: moving files, enriching them, or kicking off downstream workflows.
With uv:
uv tool install nomnom-pyWith pip:
pip install nomnom-pyGenerates a default config.toml in your project root.
If you use the built-in rules plugin, keep rules.toml in that same directory so nomnom can read both files together.
nomnom setupStarts watching your directories based on the configuration.
nomnom watchUse nomnom watch --once <group> to process a single watch group one time, or nomnom watch --dry to inspect behavior without applying file effects.
nomnom reads config.toml from the current project by default, or a custom path passed with --config.
When the built-in rules plugin is enabled, nomnom also reads rules.toml from that same directory.
[[watch]]
name = "inbox"
paths = ["./fixtures/inbox"]
include = ["*.pdf", "*.txt"]
[[watch]]
name = "archive"
paths = ["./fixtures/archive"]
[[plugins]]
name = "rules"
priority = 10
enabled = trueFor a fuller starter file, see config.example.toml.
A rules plugin for declarative file automation driven by rules.toml.
It ships with the main nomnom-py install and is configured as rules.
Keep config.toml and rules.toml side by side. If you run nomnom --config /path/to/config.toml, the built-in plugin will read /path/to/rules.toml automatically.
See RULES.md for the rule format and supported actions, and
rules.example.toml for a starter example.
Plugins can be installed from PyPI or directly from a Git repository:
nomnom plugin add <package-name-or-git-url>Example:
nomnom plugin add git+https://github.com/<owner>/nomnom-plugin-exampleScaffold a local plugin package inside plugins/:
nomnom plugin create <name>The generated package includes a pyproject.toml entry point and a stub plugin module. Local plugins are automatically discoverable, no need to configure them.
If you'd like to publish your plugin, move it to its own repo and publish it to GitHub or PyPI. There is no system for discovering plugins yet but shoot me an email if interested.
Plugins implement two methods:
def matches(self, event: FileEvent) -> bool: ...
def handle(self, event: FileEvent) -> list[Effect]: ...| Field | Type | Description |
|---|---|---|
event_type |
EventType |
EventType.CREATED, MODIFIED, or DELETED |
path |
Path |
Absolute path to the changed file |
watch_group |
str |
Name of the watch group that triggered this event |
created_at |
datetime |
UTC timestamp when the event was created |
handle() returns a list of zero or more Effect values. The dispatcher executes them in order.
| Effect | Fields | Description |
|---|---|---|
MoveFile |
source: Path, destination: Path, overwrite: bool = True |
Moves a file. Creates parent directories as needed. Skipped if source is missing; skipped (or overwrites) if destination exists based on overwrite. |
DeleteFile |
path: Path |
Deletes a file. Skipped if the file does not exist. |
CreateFile |
path: Path, content: bytes |
Creates a file with the given content, creating parent directories as needed. |
EditFile |
path: Path, action: EditAction, content: bytes |
Prepends or appends content to an existing file. action is EditAction.PREPEND or EditAction.APPEND. |
EmitEvent |
event: FileEvent |
Injects a new FileEvent back into the dispatcher. Useful for chaining logic across plugins. Depth is capped at 10 to prevent infinite loops. |
uv sync --dev
uv run pytest
uv run ruff check
uv run mypy nomnomContributor guidance lives in CONTRIBUTING.md.
nomnom is built with these open-source libraries:
- rich — MIT License
- typer — MIT License
- watchfiles — MIT License
- tomli-w — BSD-3-Clause License
Dev dependencies:
- pytest — MIT License
- pytest-cov — MIT License
- ruff — MIT License
- mypy — MIT License
Core Commands:
nomnom watch [WATCH_GROUP] [OPTS] Start watching (Opts: -c/--config, -V/--verbose, --dry-run, --once)
nomnom setup [OPTS] Create or update the config file
nomnom --version / -v Print installed version
Plugin Management:
nomnom plugin add PACKAGE Install and register a plugin
nomnom plugin remove PACKAGE Remove a plugin
nomnom plugin enable NAME Enable a plugin in config
nomnom plugin disable NAME Disable a plugin in config
nomnom plugin list List active plugins
nomnom plugin setup Run setup for specific/all plugins
nomnom plugin create NAME Scaffold a new local plugin