---
title: Create a cron job by asking AI
---

# Create a cron job by asking AI

## What it is

Omniscio has a built-in cron scheduler — jobs that run on your machine at a fixed schedule (every hour, daily at 9am, a custom cron expression, etc.). You can create one manually in the Cron Jobs UI, **or** you can just tell an external AI — Claude Code in any project on your machine — "schedule X every day at 9am" and it will create the job for you. The AI uses the `omniscio-control` skill bundle (cron surface), which reads an auto-delivered token from `~/.amc/cli-token` and POSTs to Omniscio's local control server at `127.0.0.1:19519`. You still have to approve the first run from Omniscio's inbox, so a rogue or buggy AI cannot silently run code on your machine.

## Where to find it

Cron jobs themselves live in the **Cron Jobs** surface — the middle pane on desktop — where a job
appears with an approval badge until you arm it, and where you can also create one by hand. The
switch that lets an outside AI create jobs for you is in **Settings → CLI Control**, and that page
is also where you regenerate or revoke the token afterwards.

## How it behaves

### How to use it

1. **Enable CLI Control once.** Open Omniscio → Settings → **CLI Control** and flip it on. Omniscio starts its local control server and writes your auth token to `~/.amc/cli-token` (on Windows, `%USERPROFILE%\.amc\cli-token`). The file is permission-hardened (`0o600` on Unix, an ACL grant to your user on Windows) and deleted when you disable CLI Control or quit Omniscio.
2. **Ask an AI in plain English.** In any Claude Code session (any project on your machine) say something like "schedule a cron to run `python backup.py` every day at 9am" or "set up a recurring job to sync email hourly". The `omniscio-control` skill bundle (cron surface) auto-activates on trigger phrases like _cron job_, _every day at…_, _every N hours_, _schedule X nightly_, _recurring job_.

   **Pick a mode by how you phrase it.** Say "every day at 9am" for a recurring job, "once tomorrow at 9am" for a one-off job that fires once and stops, or "run X three times daily" for a limited job that stops after N runs. The AI picks the right mode automatically — you don't need to say "one-off" or "limited" explicitly.

3. **Answer the skill's questions.** The skill will batch ~5 questions in one round: job name, what it runs (shell command or an existing Omniscio recipe ID), when (preset like "daily at 9am" or a raw 5-field cron expression), optional timeout (default 300 s). It also writes a short plain-English **description** of what the job does — you don't have to supply one; the AI composes it so the approval card reads clearly. It translates your schedule to cron syntax using its built-in cheat sheet.
4. **Approve the first run.** After the skill POSTs the job, open Omniscio → **Cron Jobs** (middle pane on desktop). Your new job appears with an orange **Awaiting approval** badge. The approval card shows the job in plain English — a readable name (a slug like `sync-inbox-hourly` is shown as "Sync Inbox Hourly", with the raw slug on hover), the AI's description of what it does, and a humanized schedule — so you can tell what you're approving at a glance. Click **Approve** to arm it. **All AI-created jobs require approval before their first run — the server enforces this, so even a malicious AI cannot bypass it.**
5. **Regenerate or revoke the token if needed.** Settings → CLI Control → **Regenerate token** rewrites `~/.amc/cli-token` immediately; the next AI invocation picks up the new value with no restart. Disabling CLI Control entirely deletes the file and blocks further AI access.

**Rate limit**: AI-created mutations (create/update/delete/toggle) are capped at **10/min**. The skill surfaces this if you hit it.

## For agents

### How it works

CLI Control is implemented by [/src/main/services/cli/cli-server.ts](/src/main/services/cli/cli-server.ts), which binds to `127.0.0.1:19519` and calls `writeTokenFile(token)` from [/src/main/services/cli/cli-token-file.ts](/src/main/services/cli/cli-token-file.ts) on startup. The token file is written with `mode: 0o600` and, on Windows, hardened via `icacls /inheritance:r /grant:r <user>:F` with a **5-second timeout** (antivirus scans can hang icacls — this protects server startup). Token regeneration flows through the `CLI_TOKEN_REGENERATE` IPC handler in [/src/main/ipc/settings-handlers.ts](/src/main/ipc/settings-handlers.ts), which rewrites the file in place. The skill itself lives at [/.claude/skills/omniscio-control/SKILL.md](/.claude/skills/omniscio-control/SKILL.md) (cron details in [cron.md](/.claude/skills/omniscio-control/cron.md)) and installs to `~/.claude/skills/omniscio-control/` for use outside this repo. On invocation it preflights `GET /ping`, reads the token file (falling back to `$AMC_CLI_TOKEN`), and POSTs `/cron/jobs` with `Authorization: Bearer <token>`. The cron endpoint handler in [/src/main/services/cli/cli-server.ts](/src/main/services/cli/cli-server.ts) Zod-validates the body and **hard-codes `requiresApproval: true` server-side** — clients cannot override this. Jobs land in the `cron_jobs` SQLite table via [/src/main/db/queries-cron-jobs.ts](/src/main/db/queries-cron-jobs.ts); a 60-second tick loop in [/src/main/services/cron-engine-service.ts](/src/main/services/cron/cron-engine-service.ts) fires approved jobs through a Recipe or Script executor and logs results to `cron_job_runs`. The UI lives at [/src/renderer/src/features/cron/CronJobsSidebar.tsx](/src/renderer/src/features/cron/CronJobsSidebar.tsx) and [/src/renderer/src/features/cron/CronMainPane.tsx](/src/renderer/src/features/cron/CronMainPane.tsx), backed by [/src/renderer/src/stores/cron-store.ts](/src/renderer/src/stores/cron-store.ts) — jobs load on sidebar mount and the UI refreshes on `CRON_JOB_UPDATED` push events. Full feature history and security invariants: [AI cron skill postmortem](/.claude/memory/postmortems/archived/ai-cron-skill-postmortem.md).

One-off and limited modes use the same cron engine. A **one-off** job stores a synthesized cron expression of the form `MM HH DD MM *` that matches only the target minute on the target date — after it fires, the engine auto-disables it via `markJobCompleted`, wrapped in a single SQLite transaction with the run-count advance for crash safety. A **limited** job uses a normal cron expression but carries a `maxRuns` cap; once `runCount === maxRuns`, the same `markJobCompleted` path fires. In both cases, a `CRON_JOB_COMPLETED` push event notifies the renderer, which optimistically flips the row to `isActive: false` so the UI re-styles immediately without waiting for the next full job refresh.

**Windows — no popup window.** On Windows a script job runs under a **hidden console**, so a console tool the script calls (e.g. `gog`) can't pop up its own terminal window every run. A job can opt INTO a **visible** window (to watch it run) by setting `visibleWindow: true` in its config — just tell the AI "run it in a visible window". A global `AMC_DISABLE_CRON_HIDDEN_CONSOLE=1` env var restores the old behavior. Mechanism + invariants: [cron-hidden-console-contract.md](/.claude/memory/contracts/cron-hidden-console-contract.md).

## Related

The skill bundle this leans on is one of several installed into Claude Code, so
[how skills work](use-skills.md) is worth reading if you have not met them before. The approval
step above is an instance of the same waiting-for-you machinery that runs through the app, which
is described on the [approvals hub](approvals-hub.md) page. If a scheduled job is what you want
rather than a recurring shell command, the [cron session jobs](cron-session-jobs.md) page covers
starting whole agent sessions on a schedule instead.

- [use-skills.md](use-skills.md) — how skills work in general (install, edit, trusted-source gate)
- [session-stuck-in-needs-you.md](session-stuck-in-needs-you.md)
