Download the PHP package agelgil/watchman without Composer

On this page you can find all versions of the php package agelgil/watchman. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.

FAQ

After the download, you have to make one include require_once('vendor/autoload.php');. After that you have to import the classes with use statements.

Example:
If you use only one package a project is not needed. But if you use more then one package, without a project it is not possible to import the classes with use statements.

In general, it is recommended to use always a project to download your libraries. In an application normally there is more than one library needed.
Some PHP packages are not free to download and because of that hosted in private repositories. In this case some credentials are needed to access such packages. Please use the auth.json textarea to insert credentials, if a package is coming from a private repository. You can look here for more information.

  • Some hosting areas are not accessible by a terminal or SSH. Then it is not possible to use Composer.
  • To use Composer is sometimes complicated. Especially for beginners.
  • Composer needs much resources. Sometimes they are not available on a simple webspace.
  • If you are using private repositories you don't need to share your credentials. You can set up everything on our site and then you provide a simple download link to your team member.
  • Simplify your Composer build process. Use our own command line tool to download the vendor folder as binary. This makes your build process faster and you don't need to expose your credentials for private repositories.
Please rate this library. Is it a good library?

Informations about the package watchman

Watchman

A single Laravel artisan command — an LLM-driven ops & investigation agent that reads, searches and introspects your app from inside a guarded Lua sandbox, then reports back (or opens a PR for review).

CI PHP 8.3–8.5 Laravel 12 | 13 PHPStan level 10

Watchman is a single Laravel artisan command — php artisan watchman — that you put on a schedule and forget about. It is an LLM-driven application monitoring agent that investigates your app's health on every run and reports back to Telegram.

It is also a general investigative agent. The scheduled run does its default health mission, but hand it any ad-hoc task as an argument — php artisan watchman "review the last 10 commits for bugs and write a report" — and the same agent turns loose on whatever you ask: a code review, a feature explainer, a targeted audit. It reads, globs, searches, inspects git history, introspects the app, and reports back (and may open a PR for a contained fix).

It is built on laravel/ai, and what makes it safe to point at a production codebase is code mode: instead of exposing dozens of tools to the model, it exposes exactly onerun_lua. The model writes a small Lua 5.1 script that runs inside a memory- and CPU-limited luasandbox VM and calls a tightly-guarded watchman.* API. The model can scan many files, filter, and summarise inside the VM, returning only the distilled result — fewer round-trips, and large intermediate data never reaches the context window. The entire security boundary lives in PHP; the Lua just orchestrates guarded calls.

Jump to: Security model

Features

Contents

Requirements

Installation

Publish the config, and optionally the system prompt:

Quickstart

Watchman needs two things: an LLM provider (via laravel/ai) and a Telegram destination to report to.

1. Point it at a model. Watchman reads provider credentials from your app's config/ai.php (the normal laravel/ai way), then you tell it which provider + model to use:

2. Give it somewhere to report. Configure Telegram — with no channel configured, report() returns Error: no reporting channel is configured. and the run can't deliver:

3. Run it.

That's a full run: it investigates, writes a dated report into its storage sandbox (storage/watchman/reports/*.md), updates MEMORY.md, and delivers to Telegram — the emoji-rich markdown report as the chat message. (The report file and memory write are part of the agent's standard workflow, not a separate engine step.) Everything else on this page is optional tuning.

What one run produces

Run php artisan watchman. You get a live, styled console trace — rendered with Termwind and markdown — of exactly what the agent did: the Lua it submitted, each watchman.* call, the result, and end-of-run token usage. (This trace is the watchman log channel mirrored to your terminal; the same records persist to the log file, so a scheduled, non-TTY run still leaves a full record — only the animated spinner is TTY-only.) The trace below is abbreviated; a real run also prints a final tool/step tally under the token line.

And a dated markdown report — written into the storage sandbox at storage/watchman/reports/2026-06-26-0900.md — with a clear shape:

Across runs it rewrites storage/watchman/MEMORY.md (newest-first, bounded). On the next run that file is injected back into the system prompt as the agent's memory, so it knows what to check first and which issues are still open.

The audit log

Watchman registers a dedicated watchman log channel on boot. Every run is recorded there with its full trail: each submitted Lua script, every guarded watchman.* call and its result, the report delivery, any PRs opened, and end-of-run token usage. Secrets never appear (see Security model).

If your app does not already define logging.channels.watchman, the package adds a sensible default for you: driver daily, file storage/logs/watchman.log, level debug, 14-day retention.

To send the trail somewhere else — Slack, Sentry, a stack channel, syslog — just define your own watchman channel in config/logging.php; the package detects it and leaves it untouched:

Usage

With no argument the agent runs its default ops mission (below). Pass a task argument and the same agent investigates whatever you ask and reports back — it is not limited to health checks:

In each case it reads/globs/searches the code, walks git_changes(), and introspects the app, then delivers a report (and, for a clear contained fix, may open a PR for review). It only orchestrates sandboxed Lua over the guarded watchman.* API.

The default mission (when no task argument is given) tails the newest storage/logs/*.log and flags ERROR/CRITICAL entries, reviews git_changes(), takes a host health snapshot with system_metrics() (flagging high disk %, memory pressure, or sustained load), confirms migrate:status is clean, sanity-checks about + db:show (env, app key, queue/cache/broadcast drivers), and ends with a verdict: All clear, Minor issues, or Needs attention.

Limitations & caveats

Read these before pointing Watchman at production. None are bugs — they are deliberate boundaries.

Scheduling

Watchman is a plain artisan command, so schedule it the way your app already schedules tasks — in routes/console.php on Laravel 11+ (or App\Console\Kernel::schedule() on older apps). To run the default ops mission hourly, add it with withoutOverlapping() so a slow run never stacks on the previous one:

Want a specific task on its own cadence? Pass it as an argument:

A failed run logs to the watchman channel, hands the exception to Laravel's report() handler, and exits non-zero — so withoutOverlapping() releases and your monitoring sees the failure. Pair the schedule with ->emailOutputOnFailure(...) if you want an active heads-up.

Configuration

Everything lives in config/watchman.php and every key is env-overridable. Minimum to get a run: set lab + model (and that provider's credentials in config/ai.php), and configure Telegram (bot_token + recipients).

The full set of environment overrides:

Env var Default Purpose
WATCHMAN_LAB openrouter laravel/ai provider (credentials in config/ai.php)
WATCHMAN_MODEL deepseek/deepseek-chat Model id
WATCHMAN_PROMPT_PATH Override the system-prompt file
WATCHMAN_READ_ROOT base_path() Read-only sandbox root
WATCHMAN_STORAGE_PATH storage/watchman The ONLY writable dir
WATCHMAN_LUA_MEMORY 33554432 (32 MB) Memory cap per run_lua call
WATCHMAN_LUA_CPU 10.0 CPU seconds per run_lua call
WATCHMAN_WEB_TIMEOUT 30 Per-fetch socket hang guard (seconds)
WATCHMAN_GLOB_MATCHES 5000 Max glob matches before truncation
WATCHMAN_SEARCH_FILES 2000 Max files scanned per search
WATCHMAN_SEARCH_MATCHES 500 Max search matches before truncation
WATCHMAN_FILE_BYTES 1048576 (1 MB) Max bytes any single file operation loads (read_file/tail_file/search_content)
WATCHMAN_FETCH_BYTES 1048576 (1 MB) Max response-body bytes fetch() returns before truncating with a notice
WATCHMAN_TELEGRAM_BOT_TOKEN Telegram bot token
WATCHMAN_TELEGRAM_RECIPIENTS Comma-sep chat_id[:thread_id] or @channel
WATCHMAN_TELEGRAM_CHUNK_PACING_MS 300 Pause between messages of a multi-message report
WATCHMAN_GITHUB_TOKEN PR tools token (contents:write + pull_requests:write)
WATCHMAN_GITHUB_REPO owner/repo; both token + repo required for PR tools
WATCHMAN_GITHUB_API_URL https://api.github.com For GitHub Enterprise

The per-tool tools.* toggles and introspect_allowlist have no env var — edit them in the published config. The annotated blocks below explain the keys that need more than a one-liner.

Provider + model. Pick any laravel/ai provider via lab (a Laravel\Ai\Enums\Lab value) and a model. Configure that provider's credentials in config/ai.php as usual. The 40-step cap and 0.2 temperature are set on the Watchman agent — see Extending to change them.

Per-tool toggles. Each watchman.* function is exposed to the model only when enabled here. A disabled function simply isn't registered in the Lua VM, so the model can't call it.

GitHub PR tools (gated). open_pr / list_pr are auto-disabled unless BOTH token and repo are set — even if enabled above. The token needs contents:write + pull_requests:write scoped to repo. The tools are PR-only: they never push to the default branch and never merge.

Reporting — Telegram only. Telegram fires when bot_token and recipients are both set. report(markdown) sends the emoji-rich report as the chat message — rendered from the agent's markdown to Telegram-safe HTML, with tables, per-level headings, and lists, chunked at 4096 UTF-16 units with link previews off. Two caps bound a runaway report: the input is capped at 64 KB (the memory guard — beyond that it is truncated at a character boundary with a visible [truncated N bytes] marker), and delivery is capped at 20 messages per recipient (the flood guard — rendered HTML can be several times larger than the input, so the bound is enforced on the actual message count). Either truncation surfaces a Note: telling the agent to write a shorter report next run, and successive messages are paced (WATCHMAN_TELEGRAM_CHUNK_PACING_MS, default 300 ms) so a multi-message report doesn't trip Telegram's flood limit.

A report is never silently lost. The HTML is escaped by construction, and chunking is syntax-aware (a split can never land inside an HTML tag/entity; open tags are closed at the seam and reopened in the next chunk, so code blocks survive a split). If Telegram still rejects the formatting, the rejected chunk is re-sent as plain text with no parse_mode — which Telegram always accepts. Each degradation comes back in report()'s result as a Note: telling the agent exactly what Telegram objected to, so it can simplify its formatting on the next run — the report itself has already been delivered. A transient 429/5xx/connection error is retried once with a short, capped wait before being treated as a failure. Configure no channel and report() returns Error: no reporting channel is configured.; a recipient who received only part of a multi-message report is a partial success (surfaced with a do-not-resend warning — the delivered part is already in the chat); only a recipient who receives nothing at all counts as a failure, and if every recipient gets nothing, report() returns Error: the report could not be sent. (logged to the watchman channel). The model chooses the report's content only — the destination is locked in config.

Where do reports land? As part of its standard workflow the agent writes a dated report into its storage sandbox ({storage_path}/reports/*.md) — the package creates the reports/ directory, the agent writes the file via watchman.write_file(). That is just the agent writing to its own storage: a local record, not a delivery channel. Delivery is Telegram only — the report becomes the chat message.

Upgrading from report(summary, body)? report() now takes a single markdown document and no longer attaches a PDF; WATCHMAN_TELEGRAM_PARSE_MODE and WATCHMAN_REPORT_BODY_BYTES are ignored and can be removed from your .env, along with the parse_mode and report_body_bytes keys in a previously published config.

Prompt override. Set prompt_path to point at your own file; the full resolution order is in Extending.

Sandboxes and limits. read_root is the read sandbox; storage_path is the write sandbox (writes, reports, and MEMORY.md all land there). The result caps bound how much a single scan can return — glob/search_content stop collecting at the cap and flag the result truncated — so a huge tree can never blow up memory or the model's context. The Lua heap itself is capped at memory_limit (32 MB default); a script that accumulates many paginated pages into one in-VM table can still hit it and surface a generic Error: Lua error: ..., so paginate (page/per_page) rather than raising WATCHMAN_LUA_MEMORY. The byte caps bound single operations the same way: file_bytes caps how much any one file operation loads — a full read_file refuses a larger file (page it with offset/limit or use tail_file), tail_file bounds its backward buffer, and search_content skips oversized files (flagging the result truncated) — while fetch_bytes truncates an over-limit HTTP response body with a visible … [truncated: …] notice.

Cost & runtime

Every run calls your configured LLM provider — a real, recurring cost that adds up on a tight schedule. Budget with these levers:

How code mode works

Most agents hand the model dozens of tools and let it fire them one at a time — a round-trip per file read, per search, per filter, and every intermediate blob (a 50 MB log, 400 grep hits) gets pushed back through the model's context.

Watchman exposes a single run_lua tool instead. The model writes a Lua 5.1 script, submits it, and must return a string. The script runs inside a fresh luasandbox VM whose only outside capability is the guarded watchman table — so scanning a thousand files, picking the matches, and summarising them is one round-trip, and the large intermediate data never leaves the sandbox. The model may call run_lua several times across a turn (one fresh sandbox per call) — typically once to investigate, once to write the report, once to send it. Inside the VM there is no io, no os.execute, no network, and no require — only watchman.*. Denied operations return a string starting with Error:. Each watchman.* function is backed by a plain, typed class in the package's compact src/Library/* capability library — but the model only ever sees the single run_lua tool.

The API (each function is individually toggleable in tools.*):

Function What it does
read_file(path[, offset, limit]) Read a UTF-8 text file by relative path (offset/limit are 1-based line numbers)
tail_file(path[, lines]) Last lines lines of a file (default 100)
file_size(path) Returns { path, bytes, human, lines }
list_dir([path, page, per_page]) List a directory (paginated)
glob(pattern[, page, per_page]) Match files with *, ?, ** (paginated, capped)
search_content(pattern[, path, page, per_page]) Grep *.php files (including .blade.php views) for a pattern
write_file(path, content[, append]) Write inside the storage sandbox (replaces by default, append=true to append)
fetch(url) Fetch a public http(s) URL (SSRF-guarded)
introspect(command) Run one read-only artisan command from the allowlist
system_metrics() Host health snapshot — disk, memory, CPU/load, network, uptime, php — read-only, no root, from /proc + PHP builtins (degrades gracefully)
git_changes([commits]) Recent commits + changed files via git log + git diff --stat (default 10)
report(markdown) Deliver the run report to Telegram as the emoji-rich chat message; content only, destination is locked
open_pr(title, body, files[, base]) Open a GitHub PR proposing a fix (human review only) → returns the PR URL
list_pr([state, limit]) List the repo's pull requests (state = open\|closed\|all, default open; limit 1–50, default 20)

Plus helpers, always available: output(msg) (a progress note between steps), json_encode(t), json_decode(s). Tables are 1-based — walk them with ipairs.

A worked example — tail the newest log file and surface only error lines:

How failures behave

Failures are designed to be soft — a denied or broken operation never crashes the run, it becomes a string the model reads and works around.

Extending

Watchman is built to be subclassed and reconfigured, not forked.

Change MaxSteps / Temperature / model wiring. The step cap and temperature are #[MaxSteps] / #[Temperature] attributes that laravel/ai reads off the agent class, so override them by subclassing and rebinding in a service provider:

The command resolves the agent from the container (app(Watchman::class)), so the bind is all it takes — no need to touch the command.

Override the system prompt. Three-tier resolution, first match wins: config('watchman.prompt_path') → the published resources/prompts/watchman.md (vendor:publish --tag=watchman-prompts) → the package's bundled default. The agent substitutes {date}, {memory}, and {report_channel} into whichever file is used, so keep those placeholders if you rewrite it.

Toggle individual tools. Flip any watchman.* function off in config('watchman.tools') and it is never registered in the Lua VM — the model literally cannot call it. Want a strictly read-only agent? Set write_file, report, and open_pr to false. Want no outbound HTTP at all? Set fetch => false.

Security model

The Lua VM provides isolation and hard resource limits; every actual capability is a PHP closure that validates its arguments before it acts:

Development

The suite holds 100% line coverage of every line not explicitly annotated as an unreachable defensive branch (marked with @codeCoverageIgnore), and passes PHPStan level 10 and a Pint preset. composer test alone does not enforce the coverage floor — composer coverage is the gate, and it is what CI runs, with the highest supported dependencies on PHP 8.3, 8.4 and 8.5 plus the lowest supported dependencies (Laravel 12) on PHP 8.3. (Coverage and static analysis bound the package's plumbing; the model's findings are inherently non-deterministic — see Limitations.)

Contributing

Contributions are welcome — see CONTRIBUTING.md for setup (including building luasandbox), commit style, and PR expectations. Before opening a PR, run the full gate locally — it must stay green:

CI runs the same checks with the highest supported dependencies on PHP 8.3, 8.4 and 8.5, plus the lowest supported dependencies (Laravel 12) on PHP 8.3. New tools or relaxed guards must ship with tests that keep coverage at 100% and must not weaken the boundary documented in Security model.

Security

Watchman is itself a security boundary, so its own vulnerabilities matter. If you find a sandbox escape, an SSRF or path-traversal bypass, or any way the agent can read or write outside its configured roots, please report it privately rather than opening a public issue — see SECURITY.md (email [email protected] or use GitHub's private vulnerability reporting). We will acknowledge within a few business days.

License

MIT. © Makiba General Trading PLC.


All versions of watchman with dependencies

PHP Build Version
Package Version
Requires php Version ^8.3
ext-luasandbox Version *
illuminate/console Version ^12.0 || ^13.0
illuminate/contracts Version ^12.0 || ^13.0
illuminate/http Version ^12.0 || ^13.0
illuminate/support Version ^12.0 || ^13.0
laravel/ai Version ^0.10.2
league/commonmark Version ^2.4
monolog/monolog Version ^3.0
nunomaduro/termwind Version ^2.0
symfony/finder Version ^7.0 || ^8.0
Composer command for our command line client (download client) This client runs in each environment. You don't need a specific PHP version etc. The first 20 API calls are free. Standard composer command

The package agelgil/watchman contains the following files

Loading the files please wait ...