本文へ移動
cccskills
無料GitHub で公開

watcher-design

Turn a monitoring wish in plain language into a deterministic watcher rule tree.

インストール方法を見る

含まれるファイル(1)

  • SKILL.md14.0 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

Watcher architect

The user says when they want to be told something. You turn that sentence into a rule tree, once. The watcher then evaluates it mechanically at every poll, for free, forever.

Use this whenever someone asks to be warned, notified, or to have something run WHEN a condition becomes true: a file appears or vanishes, a service goes down or comes back, a page changes, a log line matches, on certain days, within certain hours, after a certain delay.

Never express days, hours, durations or patterns in the legacy condition text field. That field wakes a model on every single event. The regles tree costs nothing.

The three traps, first, because they are what goes wrong

1. The op must match watcher_type, or it is false at every poll.

watcher_typeWhat it observesOps it can satisfy
lognew lines appended to a growing filecontient, contenu_change
filethe lifecycle AND presence of a path. Carries NO text.apparu, supprime, modifie, existe, absent, taille_depasse_mo
urlreachability and page textest_down, down_depuis_min, retour_en_ligne, status_http, contient, contenu_change
commandthe OUTPUT and exit code of a shell command, re-run at every pollcontient, contenu_change, code_retour

"Tell me when a line of app.log contains ERROR" is a log watcher. Written as file, contient reads an observation that carries no text, so it is false forever and the watcher never fires while reporting itself as active.

2. heure_entre bounds are STRINGS and the end is EXCLUSIVE. a: "23:00" stops at 22:59. To cover 23:55, write a: "23:56". "22:00" is the canonical form; a bare "22" and "8h30" are accepted too.

3. apparu means "the file appeared", not "a new line appeared". On a log watcher, new lines are what contient reads. Combining apparu with a log watcher yields a rule that can never be true.

The tree

One JSON object tagged by op, nesting through regles arrays.

Combinators: et {regles: [...]}, ou {regles: [...]}, non {regle: {...}}. The child array is named regles. Not clauses, not conditions.

Deterministic leaves, evaluated at every poll at zero cost:

opargumentstrue when
jour_semainejours: ["mar","jeu"] (fr or en, short or full)today is one of them
heure_entrede: "08:00", a: "22:00" (overnight windows work)local time is inside, end excluded
plage_datedu: "2026-07-01", au: "2026-07-31"local date is inside, both included
apparu / supprime / modifienonethe watched FILE appeared, was deleted, changed
existe / absentnonethe path IS there right now, or is not
contenu_changenonethe page text or the log gained content
est_downnonethe URL is unreachable or answers 5xx
down_depuis_minminutes: 10it has been down for at least that long
retour_en_lignenonethe URL just came back
contientmotif: "ERROR"fresh content contains it, case-insensitive
taille_depasse_momo: 100the watched file reached that size
status_httpcodes: [500, 503]the last status is one of them
code_retourcodes: [0]the command exited with one of them
nouvelle_lignemotif: "aa:bb" (optional)a line appeared that was NOT in the previous poll

One semantic leaf, the only one that costs a model call, and only after the deterministic part already passed:

| llm_check | question: "..." | the gate answers yes |

Put it LAST inside an et, so the free leaves filter first.

Firing

A STATE leaf (est_down, down_depuis_min, contient on a page) stays true while the situation lasts, so the watcher re-fires every cooldown_secs on its own. An EVENT leaf (apparu, retour_en_ligne) is true only on the poll where it happens: one fire.

Set interval_secs to how fast the user needs to KNOW. Set cooldown_secs to how often they want to be REMINDED.

Procedure

  1. Run watcher_list. If one already covers this target, update or delete it rather than create a twin. The tool refuses duplicates on the same target.
  2. Determine watcher_type from what is actually observed, using the table above. Get this wrong and nothing else matters.
  3. Write the tree. Every mechanical condition goes into a deterministic leaf.
  4. Use an ABSOLUTE path for a file or log target. Relative paths resolve against the server's working directory, not the user's folder, and the tool refuses them. A folder is refused too: it would fire on every unrelated change inside it.
  5. Call watcher_create. If it answers "Rule accepted by the parser but it can never fire", read the reason: the window is unreadable or empty, a pattern is blank, a threshold is zero. Fix and retry.
  6. Restate the compiled tree to the user in one readable line, so they can confirm the logic you inferred.

Watching something that is not a file, a page or a log

A lamp, a service, a container, free disk space: none of those is a file or a URL, and they all answer to a CLI. That is the command watcher. target IS the command, it is re-run at every poll, and the rules read its output and its exit code.

{"watcher_type":"command","target":"openhue get light \"Hue Play Bureau fab\"",
 "regles":{"op":"contient","motif":"[on]"}}

Reach for this before a cron. A cron wakes a whole model turn at every tick to look at something that did not change; a rule tree evaluates for free. The only reason to schedule a prompt instead is when the ANSWER itself needs a model, not the detection.

The first poll only records a baseline and never fires: otherwise a lamp that was already on would notify you the moment you asked to be told when it comes on.

Default interval is 60s and default cooldown 900s, because running a process is heavier than reading a file and because a state that stays true would otherwise notify every minute. Destructive commands are refused outright: a watcher runs unattended, forever, so what is merely risky by hand is a standing hazard here.

Detecting an ARRIVAL, not just a change

contenu_change fires when the output differs, which includes something LEAVING. For any list-shaped output, a device list, running containers, changed files, what is wanted is the arrival.

{"watcher_type":"command","target":"arp -a",
 "regles":{"op":"nouvelle_ligne"}}

Someone connects to the network, you are told. Someone leaves, you are not. The first poll only records a baseline and announces nothing, otherwise every device already present would be reported as an arrival the moment the watcher is created.

Lines are compared against the PREVIOUS poll, not against all of history. A phone that left and came back really is someone arriving again, and remembering every line ever seen would grow without bound on a chatty command.

What it DOES when it fires

Three behaviours, and the default is the expensive one. Choose deliberately.

actionCostUse when
{"type":"agent"} (default)a full model turnthe message has to be reasoned about, or something must be worked out
{"type":"notifier"}nothingthe job is just to tell the user. The observation IS the message
{"type":"commande","commande":"..."}nothingthe watcher must ACT: the lamp came on after midnight, turn it off
{"type":"aucune"}nothingit is a pure SENSOR, existing only so another watcher can correlate on it

notifier is right far more often than the default. "Tell me when the file is gone" needs no thinking: the sentence is known in advance, and a model asked to write it can also get it wrong.

commande is what turns monitoring into automation. It runs through PowerShell on Windows and sh elsewhere, it is bounded by a timeout, and it goes through the same refusal list as a watched command, which is stricter here because an action mutates by design.

Transition or state, the distinction that decides what you can combine

apparu, supprime, modifie, retour_en_ligne are TRANSITIONS: true on the single poll where the thing happened, false again immediately after. existe, absent, est_down, contient are STATES: true for as long as the situation lasts.

A transition can never mean "it is there". So this is wrong:

{"op":"et","regles":[{"op":"apparu"},{"op":"watcher","nom":"lumiere-allumee"}]}

The file side is true for one poll only, so the two halves would essentially never be true together. Use the state:

{"op":"et","regles":[{"op":"existe"},{"op":"watcher","nom":"lumiere-allumee"}]}

Rule of thumb: inside an et, use states. Alone, a transition is usually what you want, because "tell me when the file appears" should fire once, not every minute.

absent is how you catch what did NOT happen: no backup file this morning, an export that never landed. Combine it with heure_entre. No transition can express that, because nothing happened to observe.

Correlating two watchers

One signal rarely means anything. Two together diagnose.

{"op":"et","regles":[
  {"op":"watcher","nom":"site-down","depuis_min":5},
  {"op":"watcher","nom":"hote-repond"}]}

That says: the site has been down for five minutes AND the host still answers. The application is broken, not the network. Alerting on the first leaf alone would wake someone for a cut cable.

watcher reads another watcher's published verdict, so it is valid on every watcher_type: it looks at that one's conclusion, not at this one's observation.

Give the upstream watchers {"type":"aucune"}. They exist to publish a verdict. Without it each of them alerts on its own, so "the site is down" and "the host answers" both reach the user alongside the single conclusion they actually asked for.

A worked example, "tell me if test.txt is on the desktop AND the office light is on":

{"name":"lumiere-bureau","watcher_type":"command",
 "target":"openhue get light \"Bureau\"",
 "regles":{"op":"contient","motif":"[on]"},
 "action":{"type":"aucune"}}
{"name":"alerte-bureau","watcher_type":"file",
 "target":"C:\\Users\\me\\Desktop\\test.txt",
 "regles":{"op":"et","regles":[
   {"op":"existe"},
   {"op":"watcher","nom":"lumiere-bureau"}]},
 "action":{"type":"notifier"}}

The sensor says nothing, the alert concludes. No script, no model call, and no cron.

Two rules that matter. An unknown name is FALSE, never an error, so a correlation whose peer was deleted degrades quietly instead of breaking the tree on every poll. And the verdicts are read from a SNAPSHOT taken before the round, not live, so the result never depends on the order watchers happen to be polled in. The cost is one tick of latency on a correlation, which is the right trade: an alert a minute late is fine, an alert that fires at random is not.

Pausing one

watcher_toggle with id and active. false pauses it, true starts it again. The target, the rule and the action are kept untouched, so a paused watcher comes back exactly as it was.

Prefer it to watcher_delete whenever the watcher is right but badly timed: noisy during a migration, irrelevant while the user is away, firing on a machine that is down. Deleting means rebuilding the rule later from memory, and a rule rebuilt from memory is a rule that observes something slightly different.

Delete when the watcher is WRONG or the need is over. Pause when the need will come back.

A paused watcher still appears in watcher_list, with active: false. That is the answer to "why did it stop telling me?" more often than a broken rule is.

Removing one, and checking a file by hand

watcher_delete with id removes a watcher. Get the id from watcher_list; there is no deletion by name or by target. Run watcher_list again afterwards to confirm it is gone, because a watcher that survives keeps firing long after everyone forgot it exists.

file_watch with path and since is NOT a watcher. It is a one-shot question: was this file modified after that timestamp? It answers now and monitors nothing. Use it to check a single file inside a task. When the user wants to be TOLD when something happens, they want a watcher.

Examples

Warn me when ala.txt appears on the desktop, or disappears.

{"watcher_type":"file","target":"C:\\Users\\me\\Desktop\\ala.txt",
 "regles":{"op":"ou","regles":[{"op":"apparu"},{"op":"supprime"}]}}

Ping me on Telegram if a line of release.log contains ERROR, but not at night.

{"watcher_type":"log","target":"C:\\logs\\release.log",
 "regles":{"op":"et","regles":[
   {"op":"contient","motif":"ERROR"},
   {"op":"heure_entre","de":"08:00","a":"23:56"}]}}

On Tuesday or Thursday, if the local site has been down 10 minutes, remind me every 20.

{"watcher_type":"url","target":"http://127.0.0.1:8080","interval_secs":60,
 "cooldown_secs":1200,
 "regles":{"op":"et","regles":[
   {"op":"jour_semaine","jours":["mar","jeu"]},
   {"op":"down_depuis_min","minutes":10}]}}

On Tuesdays, if the changelog mentions a security fix. The model is consulted only on Tuesdays, and only when the page actually changed.

{"watcher_type":"url","target":"https://example.com/changelog",
 "regles":{"op":"et","regles":[
   {"op":"jour_semaine","jours":["mar"]},
   {"op":"contenu_change"},
   {"op":"llm_check","question":"the new changelog content mentions a security vulnerability or fix"}]}}

Failure modes

Created, shown as active, never fires. Almost always trap 1 or trap 2. Check the op against watcher_type, then check the hour window: an unreadable one makes the whole tree false at every hour.

"missing field op" or "missing field regles". The tree is not tagged, or the child array is named clauses. Every node carries op; children live under regles.

Fires far too often. A state leaf with no cooldown. Set cooldown_secs.

Fires once and never again when it should repeat. An event leaf where a state leaf was meant: apparu instead of contient, retour_en_ligne instead of est_down.

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

同じリポジトリのスキル

概要と使いどころ

Drive an installed LaRuche App through its declared actions, never its files.

日本語の概要は準備中です。原文の説明を表示しています。

infinition/LaRuche332026年9月22日 更新

arxiv

無料

Find academic papers on arXiv, with citation counts and BibTeX.

日本語の概要は準備中です。原文の説明を表示しています。

infinition/LaRuche332026年9月22日 更新

ascii-art

無料

Render text or an image as ASCII art for terminal-friendly output.

日本語の概要は準備中です。原文の説明を表示しています。

infinition/LaRuche332026年9月22日 更新

Track RSS/Atom feeds and blogs via blogwatcher-cli.

日本語の概要は準備中です。原文の説明を表示しています。

infinition/LaRuche332026年9月22日 更新

Drive a real web browser: navigate, read, find, click, fill, screenshot

日本語の概要は準備中です。原文の説明を表示しています。

infinition/LaRuche332026年9月22日 更新

Measure a codebase: lines of code, language mix, and symbol lookups.

日本語の概要は準備中です。原文の説明を表示しています。

infinition/LaRuche332026年9月22日 更新

infinition のスキルをすべて見る

このスキルの問題を報告する