AgentSkillsCN

scheduled-job-best-practices

打造具备韧性的、非交互式定时 OpenCode 作业的通用模式。

SKILL.md
--- frontmatter
name: scheduled-job-best-practices
description: Patterns for resilient, non-interactive scheduled opencode jobs

Use This Skill

Put this line at the very top of any scheduled job prompt:

@scheduled-job-best-practices

Then write your task below it.

Core Principles

  1. No magic injection. Do not assume placeholders like TODAY exist. Compute runtime values using tools (bash) during the run.
  2. Non-interactive. Scheduled jobs must not rely on QR codes, manual logins, or confirmation dialogs.
  3. Idempotent. Make reruns safe (maintain a seen/state file; avoid duplicate messages).
  4. Observable. Print a short summary at the end with status + outputs.
  5. Minimal side effects. Write durable artifacts under outputs/ in the job workdir.

Runtime Values: Dates

If you need local dates, compute them at runtime.

macOS

bash
TODAY="$(date +%F)"
TOMORROW="$(date -v+1d +%F)"

Linux

bash
TODAY="$(date +%F)"
TOMORROW="$(date -d 'tomorrow' +%F)"

Portable snippet

bash
if [ "$(uname)" = "Darwin" ]; then
  TODAY="$(date +%F)"
  TOMORROW="$(date -v+1d +%F)"
else
  TODAY="$(date +%F)"
  TOMORROW="$(date -d 'tomorrow' +%F)"
fi

If timezone matters, set TZ explicitly (example: TZ=America/Los_Angeles date +%F).

Preflight Checklist

Before doing any expensive work:

  • Confirm required tools are available (browser, network, etc).
  • Confirm required env vars exist (source .env only if needed).
  • If a dependency is missing/offline, stop early and emit a single concise reason.

Notifications (Telegram)

Prefer the Telegram Bot API (non-interactive) over web.telegram.org.

Output Contract

End every run with a compact summary:

  • Status: success | skipped | failed
  • Reason (1 line)
  • Outputs written (paths)
  • Notifications sent (message_id, chat_id) if applicable

Idempotency Pattern

When notifying about “new” items (deals, alerts, etc.):

  • Store a seen list in outputs/<job>/seen.json
  • Only notify on items not in seen.json
  • Update seen.json after sending