mcpField
B
boxlang-file-watchers

boxlang-file-watchers

github.com/ortus-boxlangDevelopment039 installsUpdated 1w ago
Repository

About boxlang-file-watchers

A filesystem watcher service in the BoxLang runtime for developers and applications that react to file changes. It supports app-scoped registration, multiple listener forms, and event tuning for hot reload, builds, and file ingestion.

Key features

  1. 01Watch create, modify, and delete events
  2. 02Register app-scoped watchers in Application.bx
  3. 03Use closure, struct, or class listeners
  4. 04Tune recursion, debounce, and throttling
  5. 05Manage watcher lifecycle and inspect state

Use cases

  1. 01Trigger hot reload after file changes
  2. 02Regenerate assets in build pipelines
  3. 03Start ingestion or ETL when files arrive
  4. 04Reduce duplicate editor events with debounce

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/ortus-boxlang/skills --skill boxlang-file-watchers

After installing, type/boxlang-file-watchersin Claude Code to confirm it loaded.

Details

From the project README

BoxLang File Watchers

BoxLang is the AI-native software productivity platform for building, modernizing and running applications, with developers and AI agents working together.

Overview

BoxLang includes a runtime watcher service for real-time filesystem automation.

Watchers are ideal for:

  • hot reload and development workflows
  • build pipelines and asset regeneration
  • file drop ingestion and ETL triggers
  • reacting to create/modify/delete events

Application.bx Integration

You can register app-scoped watchers directly in Application.bx with this.watchers.

For full descriptor behavior (app discovery, multi-app isolation, lifecycle), see application-descriptor.

class {

    this.name = "MyApp"

    watcherListener = new app.listeners.HotReloadListener()

    this.watchers = {
        sourceWatcher : {
            paths : [ expandPath( "./src" ) ],
            listener : watcherListener,
            recursive : true,
            debounce : 250,
            atomicWrites : true,
            errorThreshold : 10
        }
    }

}

Supported watcher definition keys commonly include paths, listener, recursive, debounce, throttle, atomicWrites, and errorThreshold.

Event Model

Event kinds:

  • created
  • modified
  • deleted
  • overflow

Listener payload struct keys:

  • kind
  • path (absolute path; blank for overflow)
  • relativePath (relative to watcher root; blank for overflow)
  • watchRoot (blank for overflow)
  • timestamp (ISO-8601)

Creating a Watcher

watcherNew() registers a watcher but does not automatically start it unless you chain .start() on the returned instance.

watcher = watcherNew(
    name = "sourceWatcher",
    paths = [ "./src" ],
    listener = ( event ) => {
        println( "[#event.kind#] #event.relativePath#" )
    },
    recursive = true,
    debounce = 250,
    throttle = 0,
    atomicWrites = true,
    errorThreshold = 10,
    force = false
).start()

Accepted paths forms:

  • string path
  • array of string paths

Listener Forms

1) Closure / Lambda
listener = ( event ) => {
    if ( event.kind == "modified" ) {
        recompileAsset( event.path )
    }
}
2) Struct Listener
listener = {
    onCreate: ( event ) => onCreated( event ),
    onModify: ( event ) => onModified( event ),
    onDelete: ( event ) => onDeleted( event ),
    onOverflow: ( event ) => fullResync(),
    onEvent: ( event ) => writeLog( text: "Watcher event: #event.kind#", log: "async" )
}
3) Class Name String or Class Instance
watcherNew(
    name = "prodWatcher",
    paths = [ "/srv/import" ],
    listener = "app.listeners.ImportWatcher"
)
watcherNew(
    name = "prodWatcher",
    paths = [ "/srv/import" ],
    listener = new ImportWatcher()
)

For class listeners, implement onEvent( required struct event ) as the baseline and optionally:

  • onCreate( event )
  • onModify( event )
  • onDelete( event )
  • onOverflow( event )
  • onError( error )

Watcher BIFs

  • watcherNew( name?, paths, listener, recursive=true, debounce=0, throttle=0, atomicWrites=true, delay=0, errorThreshold=10, force=false )
  • watcherStart( name )
  • watcherStop( name, force=false )
  • watcherRestart( name )
  • watcherGet( name )
  • watcherGetAll()
  • watcherList()
  • watcherExists( name )
  • watcherShutdown( name )
  • watcherStopAll( force=false )
  • watcherShutdownAll( force=false )

Lifecycle distinctions:

  • stop = stop execution but keep registration
  • shutdown = stop + unregister (destructive)

WatcherInstance API

watcherNew() and watcherGet() return WatcherInstance:

  • start()
  • stop( force=false )
  • restart()
  • isRunning() / isStopped()
  • getState() / getStateAsString()
  • getName()
  • getWatchPaths()
  • getListener()
  • getStats()

getStats() includes:

  • name
  • state (CREATED, RUNNING, STOPPED)
  • paths
  • recursive
  • debounce
  • throttle
  • atomicWrites
  • errorThreshold
  • consecutiveErrors

Tuning and Safety

  • debounce: suppress rapid duplicate events on the same path.
  • throttle: limit event frequency per path.
  • atomicWrites: reduce noisy editor temp-write sequences.
  • errorThreshold: auto-stop watcher after consecutive listener failures.
  • force stop: faster stop via interruption and watch service close; may drop in-flight events.

Production Pattern

watcher = watcherNew(
    name = "inbound-files",
    paths = [ "/data/inbound" ],
    listener = new InboundFileListener(),
    recursive = true,
    debounce = 300,
    atomicWrites = true,
    errorThreshold = 5
).start()

if ( !watcher.isRunning() ) {
    watcher.restart()
}

Best Practices

  • Keep listener logic fast; offload expensive work to executors/futures.
  • Use class listeners for reusable, testable production handlers.
  • Always log errors in onError and track consecutiveErrors.
  • Use absolute paths in production to avoid cwd ambiguity.
  • Use watcherStopAll() in graceful shutdown flows.

Troubleshooting

  • No events firing: confirm path exists and is a directory.
  • Too many duplicate events: increase debounce, enable atomicWrites, and consider throttle.
  • Watcher disappears: check if watcherShutdown() or watcherShutdownAll() was invoked.
  • Unexpected stop: inspect listener exceptions; watcher may have hit errorThreshold.

Comments

Markdown supported