> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mibyanai.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Script-Only Cron Jobs (No LLM)

> Classic watchdog cron jobs that skip the LLM entirely — a script runs on schedule and its stdout gets delivered to your messaging platform. Memory alerts, disk alerts, CI pings, periodic health checks.

Sometimes you already know exactly what message you want to send. You don't need an agent to reason about it — you just need a script to run on a timer, and its output (if any) to land in Telegram / Discord / Slack / Signal.

Mibyan calls this **no-agent mode**. It's the cron system minus the LLM.

```
   ┌──────────────────┐          ┌──────────────────┐
   │ scheduler tick   │  every   │ run script       │
   │ (every N minutes)│ ──────▶ │ (bash or python) │
   └──────────────────┘          └──────────────────┘
                                          │
                                          │ stdout
                                          ▼
                                 ┌──────────────────┐
                                 │ delivery router  │
                                 │ (telegram/disc…) │
                                 └──────────────────┘
```

* **No LLM call.** Zero tokens, zero agent loop, zero model spend.
* **Script is the job.** The script decides whether to alert. Emit output → message gets sent. Emit nothing → silent tick.
* **Bash or Python.** `.sh` / `.bash` files run under `bash` from `PATH` when available, otherwise `/bin/bash`; any other extension runs under the current Python interpreter. A Python script can also pin a **user-managed venv** via `--interpreter` (see [Using your own Python environment](#using-your-own-python-environment)). Paths must resolve inside `~/.mibyan/scripts/` (relative, absolute, or `~` forms are OK if they stay in that directory). Cron scripts do **not** inherit provider credentials from the Mibyan process environment.
* **Same scheduler.** Lives in `cronjob` alongside LLM jobs — pausing, resuming, listing, logs, and delivery targeting all work the same way.

## When to Use It

Use no-agent mode for:

* **Memory / disk / GPU watchdogs.** Run every 5 minutes, alert only when a threshold is breached.
* **CI hooks.** Deploy finished → post the commit SHA. Build failed → send the last 100 lines of the log.
* **Periodic metrics.** "Daily Stripe revenue at 9am" as a simple API call + pretty-print.
* **External event pollers.** Check an API, alert on state change.
* **Heartbeats.** Ping a dashboard every N minutes to prove the host is alive.

Use a normal (LLM-driven) cron job when you need the agent to **decide** what to say — summarize a long document, pick interesting items from a feed, draft a human-friendly message. The no-agent path is for cases where the script's stdout already IS the message.

## Create One from Chat

The real win of no-agent mode is that the agent itself can set up the watchdog for you — no editor, no shell, no remembering CLI flags. You describe what you want, Mibyan writes the script, schedules it, and tells you when it'll fire.

### Example transcript

> **You:** ping me on telegram if RAM is over 85% every 5 minutes
>
> **Mibyan:** *(writes `~/.mibyan/scripts/memory-watchdog.sh`, then calls `cronjob(...)` with `no_agent=true`)*
>
> Set up. Runs every 5 min, alerts Telegram only when RAM is over 85%. Script: `memory-watchdog.sh`. Job ID: `abc123`.

Under the hood, the agent makes two tool calls:

```python theme={null}
# 1. Write the check script
write_file(
    path="~/.mibyan/scripts/memory-watchdog.sh",
    content='''#!/usr/bin/env bash
ram_pct=$(free | awk '/^Mem:/ {printf "%d", $3 * 100 / $2}')
if [ "$ram_pct" -ge 85 ]; then
  echo "RAM ${ram_pct}% on $(hostname)"
fi
# Empty stdout = silent tick; no message sent.
''',
)

# 2. Schedule it — no_agent=True skips the LLM on every tick
cronjob(
    action="create",
    schedule="every 5m",
    script="memory-watchdog.sh",
    no_agent=True,
    deliver="telegram",
    name="memory-watchdog",
)
```

From that point on every tick is free: the scheduler runs the script, pipes its stdout to Telegram if non-empty, and never touches a model.

### What the agent decides for you

When you phrase a request like "alert me when X" or "every N minutes check Y and tell me if Z", Mibyan' `cronjob_manage` tool description tells it to reach for `no_agent=True` whenever the message content is fully determined by the script. It falls back to the normal LLM-driven path when the request needs reasoning (*"summarize the new issues"*, *"pick the most interesting headlines"*, *"draft a friendly reminder"*).

You don't have to specify `--no-agent` yourself. Just describe the behavior.

### Managing watchdogs from chat

The agent can pause, resume, edit, and remove jobs the same way it creates them:

> **You:** stop the memory watchdog for tonight
>
> **Mibyan:** *(calls `cronjob(action='pause', job_id='abc123')`)*
>
> Paused. Resume with "turn it back on" or via `mibyan cron resume abc123`.

> **You:** change it to every 15 minutes
>
> **Mibyan:** *(calls `cronjob(action='update', job_id='abc123', schedule='every 15m')`)*

The full lifecycle (create / list / update / pause / resume / run-now / remove) is available to the agent without you learning any CLI commands.

## Create One from the CLI

Prefer the shell? The CLI path gives you the same result with three commands:

```bash theme={null}
# 1. Write your script
cat > ~/.mibyan/scripts/memory-watchdog.sh <<'EOF'
#!/usr/bin/env bash
# Alert when RAM usage is over 85%. Silent otherwise.
RAM_PCT=$(free | awk '/^Mem:/ {printf "%d", $3 * 100 / $2}')
if [ "$RAM_PCT" -ge 85 ]; then
  echo "⚠ RAM ${RAM_PCT}% on $(hostname)"
fi
# Empty stdout = silent run; no message sent.
EOF
chmod +x ~/.mibyan/scripts/memory-watchdog.sh

# 2. Schedule it
mibyan cron create "every 5m" \
  --no-agent \
  --script memory-watchdog.sh \
  --deliver telegram \
  --name "memory-watchdog"

# 3. Verify
mibyan cron list
mibyan cron run <job_id>    # fire it once to test
```

That's the whole thing. No prompt, no skill, no model.

## How Script Output Maps to Delivery

| Script behavior | Result |
| - | - |
| Exit 0, non-empty stdout | stdout is delivered verbatim |
| Exit 0, empty stdout | Silent tick — no delivery |
| Exit 0, stdout contains `{"wakeAgent": false}` on the last line | Silent tick (shared gate with LLM jobs) |
| Non-zero exit code | Error alert is delivered (so a broken watchdog doesn't fail silently) |
| Script timeout | Error alert is delivered |

The "silent when empty" behavior is the key to the classic watchdog pattern: the script is free to run every minute, but the channel only sees a message when something actually needs attention.

## Script Rules

Scripts must resolve inside `~/.mibyan/scripts/`. This is enforced at run time — relative names, absolute paths, and `~`-prefixed paths are accepted when the resolved target stays in that directory; path traversal and symlink escapes are rejected. The same directory is shared with the pre-check script gate used by LLM jobs.

Interpreter choice is by file extension:

| Extension | Interpreter |
| - | - |
| `.sh`, `.bash` | `bash` from `PATH` (fallback `/bin/bash`) |
| anything else | `sys.executable` (current Python), or a [configured interpreter](#using-your-own-python-environment) |

We intentionally do NOT honour `#!/...` shebangs — keeping the interpreter set explicit and small reduces the surface the scheduler trusts.

### Using your own Python environment

By default a Python cron script runs under Mibyan' own Python environment, which only carries Mibyan' own dependencies — so a script that imports `openpyxl`, a database driver, or any other package you installed would fail with `ModuleNotFoundError`.

You can point the job at a **user-managed venv** instead with `--interpreter`:

```bash theme={null}
# 1. Create a venv you own — it survives Mibyan reinstalls/rebuilds.
uv venv ~/venvs/mibyan-reporting --python 3.11
uv pip install --python ~/venvs/mibyan-reporting/bin/python openpyxl

# 2. Schedule the job with that interpreter.
mibyan cron create "0 8 * * *" \
  --no-agent \
  --script daily-report.py \
  --interpreter ~/venvs/mibyan-reporting/bin/python \
  --deliver telegram
```

Like `--model`, this is a user-owned setting: set it with `mibyan cron create/edit`; the agent's `cronjob` tool can't.

Rules:

* The venv is **user-managed**. Mibyan does not create, freeze, restore, or install packages into it — it just invokes the path you give.
* The path must be **absolute or `~`-prefixed** (e.g. `~/venvs/reporting/bin/python3`). Bare names like `python3` are rejected, because they are not stable across `PATH` changes.
* It must be a **Python executable** (`python`, `python3`, `python3.12`, …), including a symlink's target — `/bin/bash` or other interpreters are refused.
* Applies **only to Python scripts**. `.sh` / `.bash` always run under bash regardless.
* The job-level setting applies to both `script` and `monitor_script` when they are Python files.
* It is validated **at run time**, not at creation — a cron job is long-lived, and the venv may be rebuilt or moved between when you create the job and when it fires. A missing or non-executable interpreter produces a clear script failure that is delivered like any other error.
* To clear it later: `mibyan cron edit <job_id> --interpreter ""`.

## Schedule Syntax

Same as all other cron jobs:

```bash theme={null}
mibyan cron create "every 5m"        # interval
mibyan cron create "every 2h"
mibyan cron create "0 9 * * *"       # standard cron: 9am daily
mibyan cron create "30m"             # one-shot: run once in 30 minutes
```

See the [cron feature reference](/desktop/user-guide/features/cron) for the full syntax.

## Delivery Targets

`--deliver` accepts everything the gateway knows about. Some common shapes:

```bash theme={null}
--deliver telegram                       # platform home channel
--deliver telegram:-1001234567890        # specific chat
--deliver telegram:-1001234567890:17585  # specific Telegram forum topic
--deliver discord:#ops
--deliver slack:#engineering
--deliver signal:+15551234567
--deliver local                          # just save to ~/.mibyan/cron/output/
```

No running gateway is required at script-run time for bot-token platforms (Telegram, Discord, Slack, Signal, SMS, WhatsApp) — the tool calls each platform's REST endpoint directly using the credentials already in `~/.mibyan/.env` / `~/.mibyan/config.yaml`.

## Editing and Lifecycle

```bash theme={null}
mibyan cron list                                    # see all jobs
mibyan cron pause <job_id>                          # stop firing, keep definition
mibyan cron resume <job_id>
mibyan cron edit <job_id> --schedule "every 10m"    # adjust cadence
mibyan cron edit <job_id> --agent                   # flip to LLM mode
mibyan cron edit <job_id> --no-agent --script …     # flip back
mibyan cron remove <job_id>                         # delete it
```

Everything that works on LLM jobs (pause, resume, manual trigger, delivery target changes) works on no-agent jobs too.

## Worked Example: Disk Space Alert

```bash theme={null}
cat > ~/.mibyan/scripts/disk-alert.sh <<'EOF'
#!/usr/bin/env bash
# Alert when / or /home is over 90% full.
THRESHOLD=90
df -h / /home 2>/dev/null | awk -v t="$THRESHOLD" '
  NR > 1 && $5+0 >= t {
    printf "⚠ Disk %s full on %s\n", $5, $6
  }
'
EOF
chmod +x ~/.mibyan/scripts/disk-alert.sh

mibyan cron create "*/15 * * * *" \
  --no-agent \
  --script disk-alert.sh \
  --deliver telegram \
  --name "disk-alert"
```

Silent when both filesystems are under 90%; fires exactly one line per over-threshold filesystem when one fills up.

## Comparison with Other Patterns

| Approach | What runs | When to use |
| - | - | - |
| `cronjob --no-agent` (this page) | Your script on Mibyan' schedule | Recurring watchdogs / alerts / metrics that don't need reasoning |
| `cronjob_manage` (default, LLM) | Agent with optional pre-check script | When the message content requires reasoning over data |
| OS cron + `curl` to a [webhook subscription](/desktop/user-guide/messaging/webhooks) | Your script on the OS schedule | When Mibyan might be unhealthy (the thing you're monitoring) |

For critical system-health watchdogs that must fire *even when the gateway is down*, use OS-level cron with a plain `curl` to a Mibyan webhook subscription (or any external alerting endpoint) — those run as independent OS processes and don't depend on Mibyan being up. The in-gateway scheduler is the right choice when the thing being monitored is external.

## Related

* [Automate Anything with Cron](/desktop/guides/automate-with-cron) — LLM-driven cron patterns.
* [Scheduled Tasks (Cron) reference](/desktop/user-guide/features/cron) — full schedule syntax, lifecycle, delivery routing.
* [Webhook Subscriptions](/desktop/user-guide/messaging/webhooks) — fire-and-forget HTTP entry points for external schedulers.
* [Gateway Internals](/desktop/developer-guide/gateway-internals) — delivery-router internals.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.