AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Pollers

skill-tkellogg-open-strix-pollers · by tkellogg

Create and manage pollers — lightweight monitoring scripts that check external services on a schedule. Use when the user wants to monitor something (Bluesky, GitHub, RSS, APIs), create a new poller, debug why a poller isn't firing, or manage pollers.json files in skills.

No reviews yet
0 installs
22 views
0.0% view→install

Install

$ agentstack add skill-tkellogg-open-strix-pollers

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access Used
  • Shell / process execution Used
  • Environment & secrets Used
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-tkellogg-open-strix-pollers)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
3mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Pollers? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Pollers — Event-Driven Monitoring

Pollers are lightweight scripts that check external services on a schedule and report back when something needs attention. They live inside skills and are discovered automatically by the scheduler.

How It Works

  1. A skill includes a pollers.json file alongside its SKILL.md
  2. The scheduler discovers all pollers.json files at startup and when reload_pollers is called
  3. On each cron tick, the scheduler runs the poller command as a subprocess
  4. Each line of stdout is parsed as JSON and delivered to the agent as an event
  5. If there's nothing to report, the poller outputs nothing — silence is the filter

Creating a Poller

1. Write the poller script

The script runs in the skill directory. It receives these environment variables automatically:

| Variable | Description | |----------|-------------| | STATE_DIR | The skill directory (writable, for cursors/state) | | POLLER_NAME | The poller's name from pollers.json |

Plus any custom env vars from the env field, plus the agent's existing environment.

Output contract:

  • stdout: JSONL (one JSON object per line). Each line must have poller (string) and prompt (string) fields.
  • stderr: Free-form logging. Not forwarded to the agent.
  • Exit 0: Success. Non-zero: Error, this cycle is skipped.

Example poller script:

#!/usr/bin/env python3
"""Check for new items since last poll."""
import json, os, sys
from pathlib import Path

STATE_DIR = Path(os.environ.get("STATE_DIR", "."))
CURSOR_FILE = STATE_DIR / "cursor.json"

def load_cursor():
    if CURSOR_FILE.exists():
        return json.loads(CURSOR_FILE.read_text())
    return {}

def save_cursor(cursor):
    CURSOR_FILE.write_text(json.dumps(cursor, indent=2))

def main():
    cursor = load_cursor()
    # ... check your service, compare against cursor ...

    new_items = []  # your logic here

    for item in new_items:
        event = {
            "poller": os.environ.get("POLLER_NAME", "my-poller"),
            "prompt": f"New item: {item['title']}"
        }
        print(json.dumps(event))

    # Update cursor so next run skips these items
    save_cursor(cursor)

if __name__ == "__main__":
    main()

2. Create pollers.json in the skill directory

{
  "pollers": [
    {
      "name": "my-service-check",
      "command": "python poller.py",
      "cron": "*/5 * * * *",
      "env": {
        "SERVICE_URL": "https://example.com/api"
      }
    }
  ]
}

Top-level must be a dict with a pollers key (not a bare array).

| Field | Required | Description | |-------|----------|-------------| | name | yes | Unique identifier. Used in logs and event routing. | | command | yes | Shell command, relative to the skill directory. | | cron | yes | Cron expression (5-field, UTC). | | env | no | Additional environment variables for the script. |

3. Register the pollers

After creating or updating pollers.json, call the reload_pollers tool. This re-scans all skill directories and registers any new pollers with the scheduler.

reload_pollers()
# → "Reloaded. 2 poller(s) registered: bluesky-mentions, github-issues"

Pollers are also loaded automatically at startup.

File Layout

skills/my-monitor/
├── SKILL.md
├── pollers.json        ← declares pollers
├── poller.py           ← the script
├── cursor.json         ← poller state (managed by script)
└── events.jsonl        ← optional local event log

Design Patterns

See [design-patterns.md](design-patterns.md) for detailed guidance on:

  • State management — cursor pattern, timestamp vs URI cursors, external service state, recovery on first run
  • Filtering — selecting actionable notification types, avoiding shared is_read traps
  • Prompt quality — including URIs/CIDs so the agent can act, not just observe
  • Error handling — fail silently (exit non-zero), never emit on error
  • Anti-patterns — common mistakes and how to avoid them

Security & Privacy

See [security.md](security.md) for guidance on:

  • Trust tiers — the follow-gate pattern for sorting trusted vs unknown sources
  • Operator in the loop — keeping the human informed without locking everything down
  • Credential handling — env vars, per-agent accounts, what not to log
  • Prompt injection — honest reporting with context, not sanitization

Key Constraints

  • 60-second timeout. If a poller doesn't finish in 60s, it's killed and the cycle is skipped.
  • Silence means nothing to report. Only output lines when there's something actionable.
  • One JSON object per line. Each line must parse independently.
  • poller and prompt are required fields. Lines missing either are dropped.
  • Pollers are dumb. No LLM calls. Check a service, output what changed, exit. Keep them fast and pure.
  • State management is the poller's job. Use STATE_DIR to store cursors, history, or any persistent state. The scheduler doesn't track state for you.

Debugging

If a poller isn't working:

  1. Check it was discovered: reload_pollers reports the count and names
  2. Run it manually: cd skills/my-monitor && STATE_DIR=. POLLER_NAME=test python poller.py
  3. Check stderr: Poller stderr is logged as poller_stderr events
  4. Check exit code: Non-zero exits are logged as poller_nonzero_exit
  5. Check JSON format: Each stdout line must be valid JSON with poller and prompt keys

Available Tool

| Tool | Description | |------|-------------| | reload_pollers | Re-scan all skills/*/pollers.json and register pollers. Call after installing/updating skills. |

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.