Install with Codex or Claude Copy this prompt, paste it into Codex, Claude, or another assistant, and let it review the skill page and install it for you.
A direct command skips the review prompt. Inspect the source before running it.
Check one or more watch items, compare with last known status, suggest actions, and update the watchlist.
always
false
@mochi-watch โ Check Watch Items
Check watch items from the persistent watchlist, compare with last known status, and notify on changes.
The watchlist is its own scheduler. Each item's checkIntervalMins is consumed by the queue poller to decide when to re-check. NEVER create a cron_add / @kirocrew-cron job for a watch item โ the cron would run the check a second time and burn tokens twice. When creating a watch item, ALWAYS set checkIntervalMins explicitly from the user's stated frequency (daily = 1440, hourly = 60, etc.); omitting it silently applies the 10-minute default, which is almost never what the user asked for. That value is remembered as baseIntervalMins and is the floor for every later adjustment.
Steps
1. Check Status
For each item, use the appropriate tool:
Kind
How to check
url
web_fetch({ url: "<target>" })
custom
Decide based on the target description โ web_fetch, or whatever available tool fits best
slack-channel
Read the channel's recent messages since lastChecked, then summarize key topics. Use whichever Slack tool you actually have; do not guess at a name.
slack-topic
Search Slack for <target>, newest first, and keep results newer than lastChecked. Same rule about tool names.
If a check fails, try an alternative tool before incrementing failCount.
Only increment failCount if ALL alternatives also fail.
Continue checking other items โ don't abort the batch on one failure.
If a slack-* item's kind needs a Slack tool you do not have, record that as the failure reason
rather than improvising a tool call.
โ ๏ธ CRITICAL: Even if a check fails (JS-rendered page, 403, network error, no data), you MUST still call update_watchlist with a historyEntry for that item. This advances nextCheckAfter so the item is not immediately re-checked on the next poll. Failing to write historyEntry causes the item to stay "due" forever, triggering a spawn storm (100+ agents per hour). Use changed: false and record the failure reason in result.
2. Compare Results
For each item, compare the new result with lastResult:
Changed โ record changed: true
Unchanged โ record changed: false
Deduplication rule โ avoid false positives:
Compare the semantic meaning, not the exact wording. If the status, counts,
and latest activity are all the same as lastResult, mark changed: false
even if your phrasing differs.
newResult: "Still 2 open items. Config section was edited (already noted)."
โ Same state. The edit was already recorded. Mark changed: false.
lastResult: "3 new messages in the channel, latest from the release thread."
newResult: "3 new messages, latest from the release thread, plus 1 already-seen reply."
โ If that reply was already present last time, this is unchanged.
Rule of thumb: if you can't point to a specific NEW event (new message from someone else,
status transition, new result appearing) that wasn't in lastResult, it's unchanged.
Slack channel special rule:
For kind: slack-channel, changed should ALWAYS be true (channels are always active).
But this does NOT mean you should always notify. The changed flag just means "update lastResult".
The notify decision is separate โ see Step 5's UNIVERSAL DEDUP RULE.
3. Determine Completion (autoComplete)
If autoComplete is true, check terminal conditions:
Kind
Terminal condition (โ done)
url / custom
Agent judges based on triggerCondition
slack-channel
Never auto-complete (long-term subscription)
slack-topic
Agent judges โ if 3+ consecutive checks return no results, suggest stopping to user
If unsure, leave as watching and ask the user via options
4. Batch Update
Call update_watchlist ONCE with all updates batched.
Dynamic frequency adjustment: Adjust checkIntervalMins based on change patterns, but never faster than the cadence the user asked for. That cadence is on the item as baseIntervalMins (older items may not have it โ then treat the item's current checkIntervalMins as the base and do not speed it up at all).
Something changed โ reset to baseIntervalMins. NOT to some fixed few minutes: "watch this flight daily" means daily, and the moment the price moves is exactly when a hardcoded reset would start polling it every few minutes for the rest of the day.
No change โ back off from the current interval using Fibonacci-style steps, never below baseIntervalMins and never above 180 minutes:
current > 180 โ keep as-is (the user set a longer cadence deliberately; do not override)
If a step would land below baseIntervalMins, use baseIntervalMins.
Exception: slack-channel and slack-topic kinds โ do NOT adjust checkIntervalMins.
These are user-configured subscription frequencies, not adaptive polling. Keep the interval as-is.
update_watchlist({ update: [
{
id: '<item-id>',
lastResult: '<new status summary>',
checkCount: <previous + 1>,
failCount: <0 if success, previous + 1 if failed>,
status: '<done if terminal, watching otherwise>',
historyEntry: { result: '<summary>', changed: <true/false> },
checkIntervalMins: <baseIntervalMins if changed, otherwise next back-off step (never below baseIntervalMins, cap 180)>,
notified: <true if you will call perform_pet_action to notify, false/omit otherwise>
},
]})
5. Notify (only if something changed)
No change โ do NOT notify. Just update_watchlist and move to step 6.
โ ๏ธ UNIVERSAL DEDUP RULE (applies to ALL item kinds โ url, slack, custom):
Before pushing ANY notification to the user, you MUST check whether the user has already seen this information:
Read read_mochi_file({ which: "activity" }) โ scan the last 20 entries
Find any notification entry within the last 60 minutes that mentions the same item (by label or target)
Compare what you're about to say vs what was already communicated:
If the core facts are the same (same status, same counts, same alerts) โ DO NOT notify
Only notify if there is genuinely NEW information the user hasn't seen yet
When you do notify, include ONLY the new delta โ not a full re-summary of everything
Examples:
Last notification said "docs page unchanged, 2 open items" โ still unchanged, 2 open โ SKIP
Last notification said "page had 2 open items" โ now 3 โ notify "1 new item added"
Last notification said "channel quiet" โ still quiet โ SKIP
Last notification said "site returned 503" โ now returns 200 OK โ notify "site recovered โ "
Last notification said "3 matches for the topic" โ still 3 โ SKIP
Last notification said "3 matches for the topic" โ now 1 dropped off, 1 new โ notify "1 new result: "
If in doubt, don't notify. The user can always check the watchlist panel. Silence is better than spam.
Something changed or terminal state reached:
Use perform_pet_action({ action: "notify", summary: "...", pushToChat: true, watchItemId: "<item-id>" }).
โ ๏ธ CRITICAL: You MUST pass notified: true in the update_watchlist call (step 4) AND watchItemId in perform_pet_action. This prevents the system from sending duplicate notifications.
โ ๏ธ CRITICAL: When marking an item as done or failed, you MUST call perform_pet_action with pushToChat: true and watchItemId AFTER update_watchlist (with notified: true). The user needs to know WHY it was marked done. Never silently mark done without notifying.
Proactive suggestions (only when meaningful, via perform_pet_action notify with watchItemId):
A watched URL has returned an error for 3+ consecutive checks โ perform_pet_action({ action: "notify", summary: "<label> has been failing to load โ the target may be down", pushToChat: true, watchItemId: "<id>", mood: "scared" }) โ only once. After notifying, add "nudged": true to the item's notes via update_watchlist. Do NOT nudge again if notes already has "nudged": true.
3+ checks no change โ Do NOT notify. The user already knows it's being watched. Silence is fine.
โ ๏ธ DEDUP RULE: Never push the same or substantially similar notification to chat twice. Before calling perform_pet_action with pushToChat, ask yourself: "Did I already tell the user this exact thing?" If the status hasn't changed since the last notification (check lastResult vs new result), do NOT notify again. Only notify on NEW information the user hasn't seen.
6. Finish
After all tool calls, you're done. No further action needed.
Tool Restrictions
Allowed tools:
update_watchlist โ update check results
perform_pet_action โ notify the user (only when something changed)
read_mochi_file โ the activity log, for the dedup rule above
web_fetch โ fetch any public URL to check its status
Slack READ tools, if the user has connected a Slack server. Names vary between servers: find the one that reads a channel, the one that reads a thread, and the one that searches, from the tools you actually have.
โ Slack write tools are forbidden โ Mochi must NEVER send messages or modify anything on Slack.
โ Do NOT read DM channel content โ skip DM channels entirely.