Back to home@whaojie797-design

source-watch

Local, deterministic change-monitoring for the web sources your agent depends on. (init/check a sources.yml; flags content/title/new/gone changes, HIGH on watch keywords.)

Stars
0
Language
Python
Created
Jul 30, 2026
Updated
Jul 30, 2026

Introduction

MIT Python Zero deps CI

source-watch

Local, deterministic change-monitoring for the web sources your agent depends on.

本地的、确定性的「信源变更监控」工具:给 Agent 一份信源清单,init 建基线,check 精准报告「哪些信源变了、正文改了哪几段、是否命中你关心的关键词、哪些 404 了」。

demo


Before / After

Before — Your agent quotes an external page (library changelog, rival pricing, API docs, a regulation page) but has no idea it changed. It either re-fetches the whole noisy page every time (slow, buried in nav/ads/timestamps) or answers from stale context.

After — You keep a sources.yml. source-watch tells the agent exactly what moved, in a diff it can parse, and flags the changes you care about as HIGH. No more guessing whether the page is current.


What it does

  • init — baseline every source: noise-reduced body text + title + fingerprint.
  • check — re-fetch and classify each source as content / title / new / gone.
  • Noise reduction — strips script/style/nav/footer/header, optional CSS selector, defaults to the longest text block.
  • Severity — any change whose text matches a watch keyword/regex is flagged HIGH.
  • Output — WATCH_REPORT.md (human) + watch-state.json (machine-readable baseline and last report, so an agent can read it directly).
  • Offline-friendly — file:// sources and a pure-stdlib fetcher; zero third-party dependencies.

Install

Clone into your agent's skills directory. No pip install required.

Codex

git clone https://github.com/whaojie797-design/source-watch ~/.codex/skills/source-watch

Claude Code

git clone https://github.com/whaojie797-design/source-watch ~/.claude/skills/source-watch

Cursor

git clone https://github.com/whaojie797-design/source-watch ~/.cursor/skills/source-watch

Quick start

# 1. describe what to watch
cp sources.example.yml sources.yml
#   edit urls / selectors / watch keywords

# 2. baseline
python scripts/watch.py init --config sources.yml --store .watchstore

# 3. later: what changed?
python scripts/watch.py check --config sources.yml --store .watchstore

# 4. acknowledge the change and re-baseline
python scripts/watch.py check --config sources.yml --store .watchstore --update

sources.yml:

sources:
  - name: next-changelog
    url: https://nextjs.org/changelog
    selector: "main article"
    watch:
      - "breaking"
      - "deprecat"
  - name: rival-pricing
    url: https://example.com/pricing
    watch: ["price", "plan"]

Sample report (real)

$ python scripts/watch.py init --config sources.yml --store .ws
[BASE] next-changelog — 124 chars, fingerprint 2b299333df8099b6
[BASE] rival-pricing — 88 chars, fingerprint 7c1ae0b4f2c9d3e1
[BASE] acme-docs — 220 chars, fingerprint 9412af520f24141e
Baseline saved: .ws/watch-state.json (3 sources)

$ python scripts/watch.py check --config sources.yml --store .ws
[HIGH] next-changelog — content — matched: breaking
[OK] rival-pricing — unchanged
[UNREACHABLE] acme-docs — file not found: .../simple.html

3 sources watched · 1 changed · 1 HIGH · 1 unreachable

WATCH_REPORT.md (excerpt):

## HIGH
### next-changelog — content
- url: file:///.../changelog.html
- matched: breaking
- diff:
  @@
  - Fix: improved caching for static assets.
  + BREAKING: removed legacy config format, migrate before upgrading.

How it works

  1. Fetch each url (urllib for http(s), direct read for file://).
  2. Extract body text with html.parser, dropping noise tags; optional selector narrows the region, with silent full-document fallback if it matches nothing.
  3. Hash the normalized body → fingerprint. Compare to the baseline.
  4. Classify the delta and, if watch patterns hit, raise severity to HIGH.
  5. Write WATCH_REPORT.md + update watch-state.json (baseline + last report).

Everything is deterministic and offline-capable; the test suite runs entirely against local file:// fixtures.


Limitations

  • It compares fetched, extractable text. Pages that render content purely via JavaScript (no server HTML) need a pre-rendered snapshot or a selector over static markup.
  • file:// sources are great for offline docs and tests, but can't see live sites.
  • The "longest text block" heuristic is a reasonable default, not a full reader; use selector on noisy pages for precision.
  • watch matches on the new body text only (not the diff) — keep patterns specific.

License

MIT © 2026 whaojie797-design