After Hours
Orchestrate unattended development. You never edit application code yourself. Every implementation change is done by a worker subagent you launch.
Usage
/after-hours [goal] [--interval Nm] [--max N]
| Flag | Default | Meaning |
|---|---|---|
| goal | contents of TODO.md if present |
Freeform objective for the night |
--interval |
10m |
Fixed wake interval (30s, 5m, 10m, 1h, …) |
--max |
20 |
Stop after this many loop ticks |
If no goal is given and TODO.md is missing or empty, stop and ask for a goal. Do not invent work.
Laravel Sail (required)
This app runs in Docker via Laravel Sail. Never run host PHP / Composer / Artisan / npm against the project directly.
| Wrong | Right |
|---|---|
php artisan … |
./vendor/bin/sail artisan … |
php artisan test |
./vendor/bin/sail artisan test |
composer … |
./vendor/bin/sail composer … |
npm … / npx … |
./vendor/bin/sail npm … / ./vendor/bin/sail npx … |
php … |
./vendor/bin/sail php … |
Prefer ./vendor/bin/sail over a bare sail alias so workers work in any shell. If Sail containers are not up, start with ./vendor/bin/sail up -d before tests; if that fails, treat the tick as failed.
Put this Sail rule in every worker subagent prompt.
Unattended command execution
Workers must not pause for human approval of routine commands.
- For every Sail / Docker /
git pushshell call, passrequired_permissions: ["all"]on the first attempt (Sail needs Docker outside the sandbox). - Do not ask the user whether a command may run. Proceed; if Cursor still blocks, report the tick as failed with the exact blocker.
- At startup, remind the user once: for a truly hands-off night, set Settings → Agents → Approvals & Execution to Run Everything (Auto-review + project
permissions.jsonstill helps, but Docker/Sail often prompts otherwise). - Project helpers already in place:
.cursor/permissions.json(Auto-review allow lean) and.cursor/hooks.json(auto-allow Sail/git while.after-hours/state.mdhasstatus: running).
Hard rules
- Orchestrator only — no direct edits to app/tests/config/frontend source. Allowed local writes:
.after-hours/**,docs/after-hours/**. - Workers do the code — launch
Taskwithsubagent_type: "generalPurpose"(orshellfor git/test-only chores). One slice per worker. - Branch — all work lands on
development. Create it frommainif missing, then checkout and trackorigin/developmentwhen possible. - Sail for all runtime commands — workers and any shell chores use Sail only (see above). Verify with
./vendor/bin/sail artisan testbefore reporting success. - Ship by default — after a successful worker slice with changes: commit and
git push -u origin development. Never force-push, never--no-verify, never amend unless the user rules for amend are fully satisfied. - Morning report — always maintain
docs/after-hours/YYYY-MM-DD.mdwhere the date is the loop start date (local), regardless of commit/push outcome for a given tick.
Startup (once)
- Parse args (goal, interval, max).
- Ensure on
development(create/checkout as needed). - Create dirs:
.after-hours/,docs/after-hours/. - Write
.after-hours/state.md:
---
started: YYYY-MM-DD
started_at: ISO-8601
interval: 10m
max_iterations: 20
iteration: 0
status: running
goal: |
<goal text>
loop_pid:
---
# Plan
- [ ] <break goal into concrete slices>
# Log
- Create or overwrite the morning report for tonight:
docs/after-hours/YYYY-MM-DD.md (date = started from state):
# After Hours — YYYY-MM-DD
**Goal:** <goal>
**Branch:** development
**Started:** <ISO-8601>
## Summary
_Working…_
## Commits
- Confirm briefly: goal, interval, max, branch, report path. Remind once to use Run Everything approvals for hands-off Sail/Docker.
- Arm the fixed loop (below), then run tick 1 immediately.
Fixed loop
Follow the Cursor loop skill fixed-schedule pattern.
- Check terminals for an existing after-hours loop; do not duplicate.
- Convert interval to seconds (
10m→ 600). - Start a background shell titled
Loop every <interval>: after-hours, withnotify_on_output:
while true; do
sleep <seconds>
echo 'AGENT_LOOP_TICK_after_hours {"prompt":"Continue /after-hours: read .after-hours/state.md and run the next tick."}'
done
- Regex:
^AGENT_LOOP_TICK_after_hours - Smoke-check startup once; store PID in
loop_pidin state. - On each wake: read the latest sentinel line and run a tick.
Tick protocol
On every tick (including the first):
- Read
.after-hours/state.md. Ifstatusisstopped/done/stuck, do not re-arm work; kill the loop if still running. - If
iteration >= max_iterations: set statusdone, write final summary on the morning report, kill the loop, stop. - Increment
iterationin state. - Choose one unchecked plan slice (or derive the next smallest slice from the goal / remaining
TODO.mditems). - Launch a worker subagent with a tight prompt:
You are an after-hours worker for lock-security-checker.
Branch: development (already checked out by orchestrator).
Slice: <one concrete task>
Rules:
- Implement only this slice.
- Match existing project style.
- Laravel Sail is mandatory: never run bare php/composer/npm/artisan on the host.
Use ./vendor/bin/sail <cmd> (artisan, composer, npm, php, …).
- Unattended: never ask the human for permission to run commands.
For Sail/Docker/git push Shell calls, request required_permissions: ["all"] immediately.
- Before success: ./vendor/bin/sail artisan test
- If Sail is down: ./vendor/bin/sail up -d, then retest. If still down, report failure.
- If tests fail, fix within this slice or revert your broken changes and report failure.
- Do NOT commit, push, or force-push.
- Do NOT edit .after-hours/ or docs/after-hours/.
Return: (1) what you changed, (2) test result, (3) suggested commit message (conventional, concise), (4) wide description for the morning report (what/why/how, files touched, risks).
- When the worker returns:
- No code changes → log it; if nothing left on the plan, finish (status
done). - Tests failed / stuck → append to Log; if 3 consecutive failed ticks, status
stuck, finalize report, kill loop, stop. - Success with changes → you (orchestrator) commit and push:
git status/git diff/ recentgit logstyle check- Stage relevant files (never
.env/ secrets) - Commit with the worker's suggested message (HEREDOC)
git push -u origin development- Append a Commits section entry to the morning report (see below)
- Check off the plan item; add follow-ups if the worker discovered more work
- No code changes → log it; if nothing left on the plan, finish (status
- Update
.after-hours/state.mdLog with a short tick line. - Refresh the morning report Summary (running tally of progress / blockers).
- End the turn. The fixed loop wakes the next tick; do not start a second sleeper.
Morning report format
File: docs/after-hours/YYYY-MM-DD.md (loop start date, never rename mid-run).
After each successful commit, append:
### <exact commit subject line>
<wide description from the worker: intent, approach, files, test outcome, leftover risks>
- **SHA:** <short sha>
- **Tick:** <n>
When the loop ends (done, stuck, max iterations, or /after-hours-stop), replace the Summary placeholder with a real wrap-up: what shipped, what failed, what a human should do next.
Finish / stop
- Goal complete or plan empty → status
done, finalize report, kill loop PID, confirm. - User runs
/after-hours-stop→ that skill kills the loop; if you notice stop, finalize report if needed. - Never leave a runaway sleeper without recording
loop_pid.
Safety
- No production deploys, secret exfiltration, or destructive git.
- No
git push --force,reset --hard, or hook bypass. - If Sail/Docker is down and tests cannot run, mark tick failed; after 3 failures stop as
stuckwith instructions in the report.