Running Claude Code on a schedule with Windows Task Scheduler
Claude Code is usually driven interactively, but its headless mode (claude -p "prompt") turns it into something you can schedule: an agent that wakes up, does real work in a repo, commits, and exits. We run a twice-daily automation loop on a plain Windows 10 machine this way. Here's the setup and the sharp edges.
The building blocks
1. Headless mode. claude -p "/my-skill" runs one non-interactive session and prints the result. Slash commands work, so the clean pattern is: put your entire operating procedure in a project skill, and make the scheduled command just invoke it.
2. Project skills. A skill is a folder under .claude/skills/<name>/ containing a SKILL.md with YAML frontmatter (name, description) followed by instructions. Our orchestrator skill reads project state, does the work, updates a run log, and commits — so every session starts by reading what the last one did. A run log the agent reads and writes is what turns isolated sessions into a continuous operation.
3. Permissions. Headless sessions can't answer permission prompts — an unallowed tool call just fails. Pre-authorise what the skill legitimately needs in .claude/settings.json:
{
"permissions": {
"allow": [
"Read", "Edit", "Write", "WebSearch", "WebFetch",
"Bash(git:*)", "Bash(npm:*)", "Bash(curl:*)"
],
"deny": ["Read(./.env)", "Bash(rm -rf:*)"]
}
}
Be deliberate here: this list is the blast radius of an unattended agent. Denying Read on .env keeps secrets out of the transcript while still letting shell one-liners use them (set -a; . ./.env; set +a; curl ...).
4. Task Scheduler. Register the job in PowerShell:
$action = New-ScheduledTaskAction -Execute "cmd.exe" `
-Argument '/c claude -p "/daily-ops" >> logs\runs.log 2>&1' `
-WorkingDirectory "C:\path\to\project"
$trigger = New-ScheduledTaskTrigger -Daily -At 8:00am
Register-ScheduledTask -TaskName "MyAgent-Daily" -Action $action -Trigger $trigger
Gotchas we actually hit
- PATH in scheduled tasks. Task Scheduler doesn't load your user profile the way a terminal does. Launching via
cmd.exe /cwith an absolute working directory is the reliable route; ifclaudeisn't found, use its full path fromwhere claude. - The machine has to be on. Scheduled tasks silently skip when the PC is asleep. Either enable "Run task as soon as possible after a scheduled start is missed" or accept skipped runs — we designed our loop so any single missed run is harmless.
- Design for graceful degradation. The skill should treat every external dependency (a token, a logged-in CLI) as optional: check first, skip what's blocked, log why. An unattended agent that stalls on a missing credential wastes every run until a human notices.
- Log or it didn't happen. Redirect stdout to a log file and have the skill append a structured entry to a run log in the repo. When a 6 a.m. run misbehaves, the transcript is all you have.
Where this goes
Once the loop is stable, the same pattern extends naturally: a weekly "review" session that reads the daily logs and makes decisions, skills that call external APIs, and a git history that doubles as an audit trail of everything your agent did. We're bundling our production versions — orchestrator/reviewer skills, permission templates, scheduler scripts, and run-log conventions — into a Claude Code Automation Pack if you'd rather start from a working baseline.