SkillAgentSearch skills...

Watcher

Filesystem watcher. Works anywhere. Simple, efficient and friendly.

Install / Use

/learn @e-dant/Watcher

README

Watcher

Conan Center Rust/Cargo Crate PyPI/Pip Package Go Module Homebrew package nixpkgs stable 25.05 package nixpkgs unstable package GNU Guix package Builds and Publishing for Distribution CodeQL Tests Linux Tests macOS Tests Android Compilation Tests Windows Tests

Quick Start

<details> <summary>C++</summary>
#include "wtr/watcher.hpp"
#include <iostream>
#include <string>

using namespace std;
using namespace wtr;

// The event type, and every field within it, has
// string conversions and stream operators. All
// kinds of strings -- Narrow, wide and weird ones.
// If we don't want particular formatting, we can
// json-serialize and show the event like this:
//   some_stream << event
// Here, we'll apply our own formatting.
auto show(event e) {
  cout << to<string>(e.effect_type) + ' '
        + to<string>(e.path_type)   + ' '
        + to<string>(e.path_name)
        + (e.associated ? " -> " + to<string>(e.associated->path_name) : "")
       << endl;
}

auto main() -> int {
  // Watch the current directory asynchronously,
  // calling the provided function on each event.
  auto watcher = watch(".", show);

  // Do some work. (We'll just wait for a newline.)
  getchar();

  // The watcher would close itself around here,
  // though we can check and close it ourselves.
  return watcher.close() ? 0 : 1;
}
# Sigh
PLATFORM_EXTRAS=$(test "$(uname)" = Darwin && echo '-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk -framework CoreFoundation -framework CoreServices')
# Build
eval c++ -std=c++17 -Iinclude src/wtr/tiny_watcher/main.cpp -o watcher $PLATFORM_EXTRAS
# Run
./watcher
</details> <details> <summary>C</summary>
#include "wtr/watcher-c.h"
#include <stdio.h>

void callback(struct wtr_watcher_event event, void* _ctx) {
    printf(
        "path name: %s, effect type: %d path type: %d, effect time: %lld, associated path name: %s\n",
        event.path_name,
        event.effect_type,
        event.path_type,
        event.effect_time,
        event.associated_path_name ? event.associated_path_name : ""
    );
}

int main() {
    void* watcher = wtr_watcher_open(".", callback, NULL);
    getchar();
    return ! wtr_watcher_close(watcher);
}
</details> <details> <summary>Python</summary>
pip install wtr-watcher
from watcher import Watch

with Watch(".", print):
    input()
</details> <details> <summary>Rust</summary>
cargo add wtr-watcher tokio futures
use futures::StreamExt;
use wtr_watcher::Watch;

#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let show = |e| async move { println!("{e:?}") };
    let events = Watch::try_new(".")?;
    events.for_each(show).await;
    Ok(())
}
</details> <details> <summary>Node.js</summary>
import * as watcher from 'watcher';

var w = watcher.watch('.', (event) => {
  console.log(event);
});

process.stdin.on('data', () => {
  w.close();
  process.exit();
});
</details> <details> <summary>Go</summary>
package main

import (
	"fmt"
	"log/slog"

	"github.com/e-dant/watcher/watcher-go"
)

func main() {
	w := watcher.NewWatcher("/path/to/dir", func(e *watcher.Event) {
		slog.Info("filesystem event", "event", e)
	})
	defer w.Close()

	// Wait for a new line to exit
	_, _ = fmt.Scanln()
}
</details>

The output of each above will be something like this, depending on the example:

modify file /home/e-dant/dev/watcher/.git/refs/heads/next.lock
rename file /home/e-dant/dev/watcher/.git/refs/heads/next.lock -> /home/e-dant/dev/watcher/.git/refs/heads/next
create file /home/e-dant/dev/watcher/.git/HEAD.lock

Enjoy!


Tell Me More

A filesystem event watcher which is

  1. Friendly

I try to keep the 1579 lines that make up the runtime of Watcher relatively simple and the API practical:

auto w = watch(path, [](event ev) { cout << ev; });
wtr.watcher ~
  1. Modular

Watcher may be used as a library, a program, or both. If you aren't looking to create something with the library, no worries. Just use ours and you've got yourself a filesystem watcher which prints filesystem events as JSON. Neat. Here's how:

# The main branch is the (latest) release branch.
git clone https://github.com/e-dant/watcher.git && cd watcher
# Via Nix
nix run | grep -oE 'cmake-is-tough'
# With the build script
tool/build --no-build-test --no-run && cd out/this/Release # Build the release version for the host platform.
./wtr.watcher | grep -oE 'needle-in-a-haystack/.+"' # Use it, pipe it, whatever. (This is an .exe on Windows.)
  1. Efficient

You can watch an entire filesystem with this project. In almost all cases, we use a near-zero amount of resources and make efficient use of the cache. We regularly test that the overhead of detecting and sending an event to the user is an order of magnitude less than the filesystem operations being measured.

  1. Well Tested

We run this project through unit tests against all available sanitiziers. This code tries hard to be thread, memory, bounds, type and resource-safe. What we lack from the language, we try to make up for with testing. For some practical definition of safety, this project probably fits.

  1. Dependency Minimal

Watcher depends on the C++ Standard Library. For efficiency, we leverage the OS when possible on Linux, Darwin and Windows. For testing and debugging, we use Snitch and Sanitizers.

  1. Portable

Watcher is runnable almost anywhere. The only requirement is a filesystem.


Usage

Project Content

The important pieces are the (header-only) library and the (optional) CLI program.

  • C++ Header-Only Library: include/wtr/watcher.hpp. Include this to use Watcher in your C++ project. Copying this into your project, and including it as #include "wtr/watcher.hpp" (or similar) is sufficient to get up and running in this language. Some extra documentation and high-level library internals can be found in the event and watch headers.
  • C Library: watcher-c. Build this to use Watcher from C or through an FFI in other languages.
  • Full CLI Program: src/wtr/watcher/main.cpp. Build this to use Watcher from the command line. The output is an exhaustive JSON stream.
  • Minimal CLI Program: src/wtr/tiny_watcher/main.cpp. A very minimal, more human-readable, CLI program. The source for this is almost identical to the example usage for C++.

A directory tree is in the notes below.

The Library

The two fundamental building blocks here are:

  • The watch function or class (depending on the language)
  • The event object (or similarly named, again depending on the language)

watch takes a path, which is a string-like thing, and a callback, with is a function-like thing. For example, passing watch a character array and a closure would work well in C++.

Examples for a variety of languages can be found in the Quick Start. The API is relatively consistent across languages.

The watcher will happily continue watching until you stop it or it hits an unrecoverable error.

The event object is used to pass information about filesystem events to the callback given (by you) to watch.

The event object will contain:

  • path_name, which is an absolute path to the event.
  • path_type, the type of path. One of:
    • dir

Related Skills

View on GitHub
GitHub Stars793
CategoryDevelopment
Updated3d ago
Forks43

Languages

C++

Security Score

100/100

Audited on Mar 29, 2026

No findings