--- title: Scheduled Personal Ops description: Run autonomous agents on cron schedules for recurring personal tasks --- # Scheduled Personal Ops This tutorial walks through `examples/scheduled_ops/` — three scripts that run autonomous agents on cron-like schedules to handle recurring personal tasks. Together they demonstrate how to combine the `Jarvis` SDK, the scheduler CLI, and the Python `TaskScheduler` API to build a personal operations layer that runs in the background. !!! tip "Prerequisites" - Python 3.10 or later - OpenJarvis installed: `uv sync --extra dev` from the repository root - An inference engine running (Ollama with `qwen3:8b` pulled, or a cloud API key) - For full cron expression support, install `croniter`: `uv add croniter` ## The Three Scripts | Script | Agent | Tools | Default Schedule | Purpose | |---|---|---|---|---| | `daily_digest.py` | `orchestrator` | `web_search`, `think` | Daily 9:00 AM | Search and summarize top news for chosen topics | | `code_review.py` | `native_react` | `git_log`, `git_diff`, `file_read`, `think` | Monday 8:00 AM | Review the past week of commits in a repository | | `gym_scheduler.py` | `orchestrator` | `web_search`, `think` | MWF 6:00 AM | Check gym hours and class availability | Each script follows the same SDK pattern: create a `Jarvis` instance, call `j.ask()` with an agent and tools, print the result, and close the instance. The schedule is managed externally by the OpenJarvis scheduler daemon. ## Quick Start: Run Scripts Manually Test each script without a running scheduler by invoking it directly: ```bash title="Terminal" # Morning news digest for AI and robotics uv run python examples/scheduled_ops/daily_digest.py --topics "AI,robotics" # Code review for the current repository (last 7 days of commits) uv run python examples/scheduled_ops/code_review.py --repo-path . # Gym schedule check uv run python examples/scheduled_ops/gym_scheduler.py --gym "24 Hour Fitness" ``` All scripts accept `--model` and `--engine` flags: ```bash title="Terminal" uv run python examples/scheduled_ops/daily_digest.py \ --model qwen3:8b --engine ollama --topics "AI,finance" ``` ## How the Scheduler Works ```mermaid graph TD A[jarvis scheduler start] --> B[Scheduler Daemon] B --> C{Cron trigger fires} C -->|0 9 * * *| D[daily_digest.py] C -->|0 8 * * 1| E[code_review.py] C -->|0 6 * * 1,3,5| F[gym_scheduler.py] D --> G[OrchestratorAgent] E --> H[NativeReActAgent] F --> G G --> I[web_search + think] H --> J[git_diff + git_log + file_read + think] I --> K[Output / Channel] J --> K ``` The scheduler daemon reads registered tasks from SQLite, fires them at the correct time, and passes the configured prompt to the agent. Each script can also be run directly — the scheduler is only needed for recurring, unattended operation. ## Set Up Schedules with the CLI Register each script as a recurring task using `jarvis scheduler create`: ```bash title="Terminal" # Morning digest every day at 9 AM jarvis scheduler create "Run daily news digest" \ --type cron --value "0 9 * * *" # Weekly code review every Monday at 8 AM jarvis scheduler create "Run weekly code review" \ --type cron --value "0 8 * * 1" # Gym check on Monday, Wednesday, Friday at 6 AM jarvis scheduler create "Check gym schedule" \ --type cron --value "0 6 * * 1,3,5" ``` Then start the scheduler daemon in the foreground (or as a background service): ```bash title="Terminal" jarvis scheduler start ``` List registered tasks at any time: ```bash title="Terminal" jarvis scheduler list ``` !!! note "Cron expression syntax" OpenJarvis uses standard five-field cron syntax: `minute hour day-of-month month day-of-week`. Install `croniter` (`uv add croniter`) for full expression support including ranges and step values. Without it, basic `hour:minute` patterns still work. ## Configure Schedules with TOML The `schedules.toml` file in `examples/scheduled_ops/` defines all three schedules declaratively. This is convenient for version-controlling your personal ops configuration or sharing it across machines: ```toml title="examples/scheduled_ops/schedules.toml" [schedules.daily_digest] type = "cron" value = "0 9 * * *" description = "Morning news and social media digest" script = "daily_digest.py" [schedules.code_review] type = "cron" value = "0 8 * * 1" description = "Weekly code review" script = "code_review.py" [schedules.gym_scheduler] type = "cron" value = "0 6 * * 1,3,5" description = "Gym hours and class check" script = "gym_scheduler.py" ``` Point your own tooling or a custom loader at this file to register tasks in bulk. ## Register Tasks via the Python API The `gym_scheduler.py` script includes a `--register` flag that demonstrates programmatic task registration using `TaskScheduler` directly: ```bash title="Terminal" uv run python examples/scheduled_ops/gym_scheduler.py \ --register --gym "Planet Fitness" ``` The equivalent Python code: ```python title="Programmatic task registration" from openjarvis.scheduler import TaskScheduler from openjarvis.scheduler.store import SchedulerStore store = SchedulerStore() scheduler = TaskScheduler(store) task = scheduler.create_task( # (1)! prompt="Check gym schedule for 'Planet Fitness'", schedule_type="cron", schedule_value="0 6 * * 1,3,5", agent="orchestrator", tools="web_search,think", ) print(f"Task registered: {task.id}") print(f"Next run: {task.next_run}") ``` 1. `create_task()` persists the task to SQLite and computes the next trigger time. The scheduler daemon picks it up without a restart. ## The Daily Digest Script The digest script is the simplest of the three. It builds a date-stamped prompt and passes it to an orchestrator with `web_search` and `think`: ```python title="examples/scheduled_ops/daily_digest.py" hl_lines="5 6 7 8" from openjarvis import Jarvis j = Jarvis() # uses defaults from ~/.openjarvis/config.toml response = j.ask( f"Today is {today}. Search and summarize the top news on: {topics}", agent="orchestrator", tools=["web_search", "think"], ) j.close() ``` The orchestrator searches for each topic in a separate turn, uses `think` to synthesize across topics, and returns a structured digest with bullet-point summaries and a one-paragraph outlook. ## Send Results to a Channel To route script output to Slack or any other supported channel, pipe stdout through `jarvis channel send`: ```bash title="Terminal" uv run python examples/scheduled_ops/daily_digest.py \ --topics "AI,finance" | jarvis channel send slack ``` Or add channel output inside the script: ```python title="In-script channel output" from openjarvis.channels import ChannelRegistry channel = ChannelRegistry.create("slack", webhook_url="https://hooks.slack.com/...") channel.send(response) ``` List all available channels: ```bash title="Terminal" jarvis channel list ``` !!! warning "Channel credentials" Live channel output requires channel-specific credentials. Run `jarvis add slack` (or the relevant provider) to set up the MCP server and credential store, then configure environment variables in your `.env` file before starting the scheduler daemon. ## Customization Tips - **Change topics**: Pass `--topics "finance,healthcare,sports"` to `daily_digest.py` for a different digest. - **Review window**: Pass `--days 14` to `code_review.py` for a two-week review cycle instead of one week. - **Swap agents**: Replace `orchestrator` with `native_react` in any script to compare agent behavior on the same task. - **Add file output**: Append `"file_write"` to the `tools` list and update the prompt to save reports to disk instead of printing them. - **One-time tasks**: Use `--type once --value "2026-04-01T09:00:00"` with `jarvis scheduler create` for non-recurring tasks. ## See Also - [Architecture: Agents](../architecture/agents.md) — `OrchestratorAgent` and `NativeReActAgent` internals - [Architecture: Tools and Memory](../architecture/memory.md) — tool registry and `ToolExecutor` - [Getting Started: Configuration](../getting-started/configuration.md) — engine and model defaults