EN 中文

seek doesn't run a background process. Instead, it teaches your OS scheduler (launchd / systemd / Task Scheduler) to invoke seek cron tick once a minute. seek's tick reads ~/.seek/cron/jobs.jsonl + triggers/, fires whatever's due, exits.

1. Register a job

$ seek cron create --name ci-watch --at @daily \
    --cwd ~/code/myproj \
    'check main branch CI status; summarise failures'

$ seek cron list             # see what's registered
$ seek cron run ci-watch     # fire NOW, bypass schedule

Saved to ~/.seek/cron/jobs.jsonl. But nothing fires yet — the OS scheduler hasn't been wired up. Continue below.

2. Wire up the OS scheduler

macOS · launchd
Linux · systemd
Windows · Task Scheduler
$ mkdir -p ~/Library/LaunchAgents
$ cat > ~/Library/LaunchAgents/com.seek.cron.plist <<'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key><string>com.seek.cron</string>
  <key>ProgramArguments</key>
    <array>
      <string>/usr/local/bin/seek</string>
      <string>cron</string>
      <string>tick</string>
    </array>
  <key>StartInterval</key><integer>60</integer>
  <key>RunAtLoad</key><true/>
</dict>
</plist>
EOF
$ launchctl load ~/Library/LaunchAgents/com.seek.cron.plist

Replace /usr/local/bin/seek with your actual which seek. Uninstall: launchctl unload + rm.

$ mkdir -p ~/.config/systemd/user

$ cat > ~/.config/systemd/user/seek-cron.service <<'EOF'
[Unit]
Description=seek cron tick

[Service]
Type=oneshot
ExecStart=%h/.local/bin/seek cron tick
EnvironmentFile=-%h/.seek/cron/env
EOF

$ cat > ~/.config/systemd/user/seek-cron.timer <<'EOF'
[Unit]
Description=Fire seek cron tick every minute

[Timer]
OnBootSec=1min
OnUnitActiveSec=1min

[Install]
WantedBy=timers.target
EOF

$ systemctl --user daemon-reload
$ systemctl --user enable --now seek-cron.timer

The EnvironmentFile=- prefix means "no error if missing" — pairs with the env overlay in §3 below. Check: systemctl --user list-timers, journalctl --user -u seek-cron.service.

PS> schtasks /create /tn "seek cron tick" /tr "seek cron tick" /sc minute /mo 1 /ru "$env:USERNAME"

Or GUI: Task Scheduler → Create Basic Task; trigger Daily + Repeat every 1 minute; action Start a program (seek) with args cron tick. Uninstall: schtasks /delete /tn "seek cron tick" /f.

3. Inject the API key — ~/.seek/cron/env

Core problem: the OS scheduler hands your seek cron tick process a near-empty environment. launchd typically gives PATH=/usr/bin:/bin and nothing else; systemd user units inherit only what EnvironmentFile= names; Windows Task Scheduler is similar. Your .zshrc's DEEPSEEK_API_KEY will not cross that boundary automatically.

Fix: write a dotenv-style file at ~/.seek/cron/env — seek auto-overlays it onto every spawned cron child:

# ~/.seek/cron/env (macOS / Linux / WSL)
# %USERPROFILE%\.seek\cron\env (Windows)

DEEPSEEK_API_KEY=sk-...
PATH=/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin
# Linux desktop notifications additionally need:
# DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus

Format: one KEY=VALUE per line; # comments; balanced quotes stripped; no shell expansion ($HOME is three literal characters). Duplicate keys: last wins. Parse errors (missing =, empty key) fail spawn loudly — far safer than silently running without the API key you thought you set.

💡 systemd users: pointing EnvironmentFile=%h/.seek/cron/env at this same file makes it drive both systemd's env AND seek's overlay — one file, two layers.

4. OS notifications

Platform Implementation Status
macOSosascript -e 'display notification ...'✅ Notification Center banner
Linuxnotify-send (libnotify)✅ Desktop (auto no-op when $DISPLAY + $WAYLAND_DISPLAY empty)
Windows(none)⚠️ v0.6.1 no-op — BurntToast adapter planned for v0.6.x dot
⚠️ Windows fallback: until a toast adapter ships, rely on seek cron list for last_status and tail -F %USERPROFILE%\.seek\cron\runs\*.jsonl for run output.

5. External triggers (file bridge)

CI, IDE plugins, shell scripts can write JSON to ~/.seek/cron/triggers/ — the next tick consumes + deletes:

$ cat > ~/.seek/cron/triggers/ci-$(date +%s).json <<EOF
{
  "trigger_id": "ci-build-1234",
  "prompt": "CI build 1234 finished on branch foo-feature; summarise test failures",
  "cwd": "/Users/me/code/myproj",
  "ttl_seconds": 3600
}
EOF

Rules: file mtime must be older than "now − 1s" (prevents tick reading partial writes); parse failure → file renamed to triggers/.malformed/<id>.json + WARN, doesn't block; ttl_seconds expired → silently dropped; consumed → deleted (the directory is an inbox, not history). Run history lives in ~/.seek/cron/runs/<id>.jsonl with two-axis GC (keep 100 recent + 30d max-age).

6. schedule_wakeup LLM tool

The model can schedule a future check-in on itself — e.g., "30 minutes from now, look at CI again." Internally this is just a one-shot cron job (max_runs=1) registered through the same jobs.jsonl pipeline.

# Model calls (not user):
schedule_wakeup({
  "seconds": 1800,
  "prompt": "Check CI for branch foo-feature; report any new failures"
})
# → registered as cron job; fires once; auto-deletes

7. Troubleshooting

Symptom Likely cause / check
Registered a job, never firesOS scheduler not running. macOS: launchctl list | grep seek · Linux: systemctl --user list-timers · Windows: schtasks /query /tn "seek cron tick"
Fires but every run auth-failsSubprocess env missing API key. Check ~/.seek/cron/env exists; seek cron run <name> to reproduce
Linux: "notify-send: command not found"libnotify not installed. apt install libnotify-bin / dnf install libnotify
Windows: notifications enabled but no popupKnown v0.6.1 limitation (see §4). Fallback to seek cron list.
Same job triggered twicePrior run still active — per-job lock skips new trigger (design behavior; check runs/<id>.jsonl for WARN: prior run still active)
Env file parse error fails spawnBy design — far safer than silent. Fix the syntax; re-run.
📖 Full design + risk discussion: feature-routines.md · markdown guide for direct command paste: guide-cron.md · book chapter on routines architecture: chapter 22.