crw-watch — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited crw-watch (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
the page — see crw-scrape (step 2).
change tracking only as a managed cloud feature; crw exposes the same primitive as a REST endpoint that runs on your own infra — you own the snapshots, the cadence, and the data.
crw stores nothing between calls. The caller owns the snapshots:
1. Scrape now → store snapshot (markdown / json)
2. Scrape later → call /v1/change-tracking/diff with current + previous
3. On status=changed → alert / act
4. Repeat on a cronTwo modes, composable:
| Mode | Wire string | What it produces |
|---|---|---|
| Git-style text diff | "gitDiff" (alias: "git-diff") | Unified-diff text + parse-diff AST in diff.text / diff.json |
| Per-field JSON diff | "json" | Path-keyed map {"$.field": {"previous":…,"current":…}} in diff.json; requires schema |
Default (omit modes): ["gitDiff"]. Combine both: "modes": ["gitDiff", "json"].
Single page diff (REST):
curl -X POST "$CRW_API_URL/v1/change-tracking/diff" \
-H "Authorization: Bearer $CRW_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"current": {
"markdown": "# Pricing\nPro plan: $49/mo"
},
"previous": {
"markdown": "# Pricing\nPro plan: $39/mo",
"contentHash": "<hash from prior result>"
},
"modes": ["gitDiff"]
}'Response shape:
{
"success": true,
"data": {
"status": "changed",
"firstObservation": false,
"contentHash": "<new hash>",
"snapshot": { "markdown": "...", "contentHash": "..." },
"diff": {
"text": "@@ -1,2 +1,2 @@\n # Pricing\n-Pro plan: $39/mo\n+Pro plan: $49/mo",
"json": { "files": [...] }
}
}
}Batch diff (discriminated by presence of batch key):
curl -X POST "$CRW_API_URL/v1/change-tracking/diff" \
-H "Authorization: Bearer $CRW_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"batch": [
{ "url": "https://example.com/pricing", "current": {"markdown": "..."}, "previous": {"markdown": "..."} },
{ "url": "https://example.com/about", "current": {"markdown": "..."} }
],
"modes": ["gitDiff"]
}'Shared modes/schema/prompt/contentType at the top level are defaults; each batch item can override them individually.
Inline during a scrape — pass changeTracking as a format on /v1/scrape:
curl -X POST "$CRW_API_URL/v1/scrape" \
-H "Authorization: Bearer $CRW_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/pricing",
"formats": ["markdown", "changeTracking"],
"changeTracking": {
"modes": ["gitDiff"],
"previous": { "markdown": "...", "contentHash": "..." }
}
}'Single mode: { current, previous?, modes, schema?, prompt?, contentType?, tag?, goal?, judgeEnabled? }
Batch mode: { batch: [...items], modes, schema?, ... } where each item is { url?, current, previous?, modes?, schema?, ... }.
| Field | Type | Notes |
|---|---|---|
current.markdown | string | Current page content (gitDiff / mixed) |
current.json | object | Current extracted JSON (json / mixed) |
previous.markdown | string | Prior snapshot for gitDiff |
previous.contentHash | string | Persist from prior result's snapshot.contentHash |
modes | string[] | ["gitDiff"] (default), ["json"], or both |
schema | JSON Schema | Required for json mode; defines tracked fields |
prompt | string | Natural-language extraction prompt (alternative to schema) |
contentType | string | If binary/non-text, triggers byte-hash comparison only |
tag | string | Opaque caller ID echoed back on the result |
goal | string | Natural-language filter for meaningful changes (AI judge, M2) |
judgeEnabled | bool | Enable AI judgment (M2 feature; accepted but not yet applied) |
goal field (AI judge)goal is a natural-language filter for what counts as a meaningful change, fed to an LLM judge. It is accepted by the server now but applied in a future milestone (M2). Guidance for when it lands:
nav updates" beats "detect important changes"`.
section"`.
{meaningful, confidence, reason, meaningfulChanges[]} inthe result's judgment field.
#!/usr/bin/env bash
# cron-check.sh — run every hour via cron or a scheduler
SNAPSHOT_FILE=".crw/snapshot.json"
CURRENT=$(crw scrape "https://example.com/pricing" --format markdown)
if [ -f "$SNAPSHOT_FILE" ]; then
PREV_MARKDOWN=$(jq -r '.markdown' "$SNAPSHOT_FILE")
PREV_HASH=$(jq -r '.contentHash' "$SNAPSHOT_FILE")
RESULT=$(curl -s -X POST "$CRW_API_URL/v1/change-tracking/diff" \
-H "Authorization: Bearer $CRW_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"current\":{\"markdown\":$(jq -Rsc . <<<"$CURRENT")},\"previous\":{\"markdown\":$(jq -Rsc . <<<"$PREV_MARKDOWN"),\"contentHash\":\"$PREV_HASH\"},\"modes\":[\"gitDiff\"]}")
STATUS=$(echo "$RESULT" | jq -r '.data.status')
if [ "$STATUS" = "changed" ]; then
echo "CHANGED: $(echo "$RESULT" | jq -r '.data.diff.text')"
# → send alert, write to DB, trigger webhook, etc.
fi
echo "$RESULT" | jq '.data.snapshot' > "$SNAPSHOT_FILE"
else
# First observation — store the snapshot
curl -s -X POST "$CRW_API_URL/v1/change-tracking/diff" \
-H "Authorization: Bearer $CRW_API_KEY" \
-H "Content-Type: application/json" \
-d "{\"current\":{\"markdown\":$(jq -Rsc . <<<"$CURRENT")},\"modes\":[\"gitDiff\"]}" \
| jq '.data.snapshot' > "$SNAPSHOT_FILE"
fiprevious. Thesnapshot field in the response contains the normalized content and contentHash — store it, don't recompute it.
previous was supplied. The server setsstatus: "changed" and returns snapshot but produces no diff. Store it as your baseline.
input it produces no diff — use gitDiff mode for plain markdown.
of N calls. Top-level modes/schema as defaults keeps the body compact.
previous; crw computes and returns. Nothingis stored server-side. Your snapshots, your infra, your retention policy.
into the diff
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.