From cortex-overnight
Plan and launch autonomous overnight development sessions. Selects eligible features from the backlog, presents a session plan for user approval, and hands off to the runner for unattended execution. Use when user says "/overnight", "/overnight resume", "/overnight status", "start overnight session", "overnight plan", "launch overnight", "overnight status", or wants to run multiple features autonomously overnight.
How this skill is triggered — by the user, by Claude, or both
Slash command
/cortex-overnight:overnightThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Interactive entry point for overnight autonomous orchestration. Guides the user through selecting features from the backlog, reviewing a session plan, and launching overnight execution. The skill itself handles planning and approval; execution is delegated to the runner.
Interactive entry point for overnight autonomous orchestration. Guides the user through selecting features from the backlog, reviewing a session plan, and launching overnight execution. The skill itself handles planning and approval; execution is delegated to the runner.
/overnight -- start a new overnight session (select features, build plan, launch)/overnight resume -- resume an interrupted overnight session or view results/overnight status -- check the status of a running or recent overnight sessionValidate inputs before entering any flow:
| Input | Type | Valid Values | Error Response |
|---|---|---|---|
time-limit | string | \d+(\.\d+)?h (e.g., 6h, 8h, 1.5h) | "Invalid time-limit format '{value}'. Expected hours, e.g. '6h'." → stop |
Precondition checks (fail fast before any backlog reads):
.git/ must exist in the current directory. If missing: "Not a git repository root. Run /overnight from the repository root." → stop.backlog/ must exist. If missing: "No backlog directory found. Run from the project root." → stop.overnight, overnight resume, and overnight status are valid. Unknown variants (e.g., /overnight foobar) should report: "Unknown subcommand '{variant}'. Use /overnight, /overnight resume, or /overnight status." → stop./overnight)Python module note: After
uv tool install git+<url>@<tag>, thecortexconsole script is available globally and thecortex_command.*package is importable inside the tool venv — noPYTHONPATHmanipulation required. Invoke planning helpers either through the CLI entry point (cortex <subcommand>) or, where subcommands are not yet wired, viapython3 -m cortex_command.<module>.
Call load_state() from cortex_command.overnight.state (no arguments — uses its default path at $CORTEX_COMMAND_ROOT/lifecycle/overnight-state.json).
complete: Warn the user that an active overnight session exists. Report the phase and feature count. Ask whether to resume the existing session (switch to the Resume Flow) or abandon it and start fresh.complete phase: Treat as no active session. Proceed as new.Regenerate the backlog index so that feature selection in Step 3 operates on up-to-date metadata.
cortex-generate-backlog-index from the project root. If the command exits with a non-zero status, report: "Backlog index regeneration failed (exit {code}). Fix the issue and retry /overnight." → halt.git add backlog/index.json backlog/index.md.Run select_overnight_batch() from cortex_command.overnight.backlog on the project's backlog directory.
This function composes the full selection pipeline: parse backlog items, filter for readiness (status, blockers, research + spec + plan artifacts), score by weighted algorithm (dependency structure, priority, tag cohesion, type routing), and group into batches.
A feature is eligible only if the following exist on disk:
lifecycle/{slug}/research.md exists on disk (slug = item.lifecycle_slug if set, else slugify(item.title))lifecycle/{slug}/spec.md exists on disk (produced by /refine or /lifecycle)type: is not epic — epic items are non-implementable and excluded at step 4 (after blocked-by, before artifact checks); a blocked epic reports its blocking dependency, not the epic exclusionIf lifecycle/{slug}/plan.md is missing, it is generated automatically during the
overnight session before the feature executes — no pre-run /lifecycle plan needed.
If no eligible items (selection result has zero batches): Report "Nothing ready for overnight execution." List the ineligible items with their reasons from the selection result. Suggest running /lifecycle through the plan phase on the highest-priority ineligible items to produce the required lifecycle artifacts. Stop.
Error: If select_overnight_batch() raises an exception (e.g., malformed backlog frontmatter), report: "Failed to parse backlog: {error}. Check backlog file frontmatter for syntax errors." → stop.
Present the selection result summary to the user. This includes:
The summary string is available as selection.summary on the SelectionResult object.
Call render_session_plan() from cortex_command.overnight.plan with the selection result and default configuration:
render_session_plan(
selection=selection,
time_limit_hours=6,
)
This produces a formatted markdown session plan with:
Error: If render_session_plan() raises an exception, report: "Failed to render session plan: {error}." → stop.
Collect specs for all selected features, display the session plan with specs inline, and get a single approval covering both.
Collect specs: For each selected feature (in round-then-priority order), attempt to read lifecycle/{slug}/spec.md.
Display plan + specs: Present the rendered session plan from Step 5, then immediately display each feature's spec content inline:
{rendered session plan}
─────────────────────────────────────────
Spec [1/{total}]: {feature_title} (lifecycle/{slug}/spec.md)
─────────────────────────────────────────
{spec content}
─────────────────────────────────────────
Spec [2/{total}]: {feature_title} (lifecycle/{slug}/spec.md)
─────────────────────────────────────────
{spec content}
Approval prompt: After all specs are shown, present a single approval:
Approve this plan and specs?
[A] Approve — proceed to launch
[R] Remove a feature — specify which to exclude, then re-display
[T] Adjust time limit — change from the default 6h
[Q] Abort — stop planning
Error: If lifecycle/{slug}/spec.md exists but cannot be decoded (e.g., binary content, encoding error), treat it the same as a missing file and offer the remove-or-abort choice.
On user approval, execute these steps in order:
Validate target repos: Call validate_target_repos(selection) from cortex_command.overnight.plan. If the returned list is non-empty, report:
Cannot start overnight session: the following repo: paths are not valid git repositories:
- {path1}
- {path2}
Run `git clone <url> <path>` or correct the repo: field in the affected backlog items.
Do not write any artifacts, create any worktrees, or mark the session executing. → stop.
Pre-flight: uncommitted lifecycle/backlog files: Run git status --porcelain -- lifecycle/ backlog/ and capture the output.
If output is non-empty (any untracked files, staged, or modified-but-unstaged files in lifecycle/ or backlog/): block launch with:
Uncommitted lifecycle files detected. The overnight worktree is created from HEAD, so
these files will not be visible to the runner. Commit or stash them before launching.
Uncommitted paths:
{lines from git status output}
Then offer: "Would you like me to run /commit now?"
/commit. After it returns, re-run git status --porcelain -- lifecycle/ backlog/. If the output is now empty, proceed to Launch sub-step 2. If the output is still non-empty, display the block message again with the remaining paths and stop — do not offer /commit a second time./overnight again." Do not proceed to Launch sub-step 2.If output is empty: proceed to Launch sub-step 2 without any message.
Error: If git status fails (unexpected git error), report the error and stop. In practice this cannot occur — the git repository check in Input Validation (.git/ exists) runs before Step 7.
Bootstrap the session: Call bootstrap_session(selection, plan_content) from cortex_command.overnight.plan with the approved selection and the rendered plan string from Step 5. Returns (state, state_dir) with overnight-state.json, overnight-plan.md, and session.json already written on disk.
This performs all initialization atomically:
overnight-{YYYY-MM-DD}-{HHmm}) with collision-avoidancepending status with round assignments matching batch numbersexecuting$TMPDIR/overnight-worktrees/{session_id}/ with a new overnight/{session_id} integration branch; the user's active branch is not changedovernight-plan.md, session.json, and overnight-state.json into the MC lifecycle session directoryError: If bootstrap_session() raises (worktree creation, disk write, or save failure), report the error and stop. Clean up any orphaned worktree: run git worktree prune and then ls $TMPDIR/overnight-worktrees/ to identify and remove any leftover directory (rm -rf $TMPDIR/overnight-worktrees/<session_id>). The session ID is inside the directory name — check modification time to find the orphan.
latest-overnight symlink: Handled by the runner on startup. The skill does not create this symlink — it writes to the repo root which is outside the sandbox's write allowlist in sandboxed projects.
Extract batch spec sections: Read the worktree path from the initialized state (state.worktree_path). Call extract_batch_specs(state, Path(worktree_path)) from cortex_command.overnight.plan, passing the worktree path instead of the repository root so that extracted specs are written into the worktree's lifecycle/ directory. If the returned list is non-empty, cd to the worktree directory, stage each returned path with git add (paths are relative to the worktree, not the repo root), and commit using /commit with message "Extract batch spec sections for overnight session {session_id}" (substituting the actual session ID). This commits the specs on the integration branch, not on main. If the list is empty, skip the commit — no batch-spec items were selected.
Error: If git add or git commit fails, report: "Batch spec commit failed: {error}. Proceeding without committing batch spec sections — they may be extracted during runner startup." Continue — the runner can still function without the pre-commit.
Log session start: Call log_event() from cortex_command.overnight.events with event='session_start', round=1, and details including the session ID, feature count, and time limit. Pass log_path=state_dir / "overnight-events.log" so the event log lands in the MC lifecycle session directory alongside the other session artifacts. Note: the parameter is event (not event_type) and event names are lowercase strings (e.g., 'session_start', not 'SESSION_START').
Error: If log_event() fails, report: "Failed to log session start event: {error}." Continue — logging failure is non-fatal.
Launch the dashboard (if not already running): Check whether the dashboard is live by reading ${XDG_CACHE_HOME:-$HOME/.cache}/cortex/dashboard.pid. If the file exists and the stored PID is alive (kill -0 $(cat "${XDG_CACHE_HOME:-$HOME/.cache}/cortex/dashboard.pid") exits 0), the dashboard is already running — note the URL and skip launch. Otherwise, instruct the user to run cortex dashboard (installer-tier) or just dashboard (clone-only) in a separate terminal before starting the runner, or explain that they can launch it at any time during the session. Poll GET http://localhost:8080/health for a 200 response (up to 5 seconds, 1-second intervals); if successful, note "Dashboard available at http://localhost:8080" in the session start message.
Error: If the dashboard health check times out or the PID file is unreadable, continue without failing — the dashboard is optional. Report: "Dashboard not detected at http://localhost:8080. Run cortex dashboard in a separate terminal to enable live progress monitoring."
Execute the runner command: Ask the user whether to run now or schedule for later using AskUserQuestion:
Run now or schedule for later?
[1] Run now — launch the overnight session immediately
[2] Schedule for specific time — delay launch until a target time
Usage context (dormant): No programmatic access to Claude Code's subscription usage data (remaining tokens, reset time) currently exists from within an agent context. When such access becomes available (e.g., a
/usageAPI, ausage-cache.jsonfile, or an environment variable), auto-display it alongside the scheduling prompt to help the user choose a launch time. Until then, no usage information is shown.
Run now (option 1): Execute via Bash tool with dangerouslyDisableSandbox: true (substitute actual {session_id} and time limit):
overnight-start $CORTEX_COMMAND_ROOT/lifecycle/sessions/{session_id}/overnight-state.json 6h
Args are positional — do not use --flag=value syntax. overnight-start creates a detached tmux session named overnight-runner and returns immediately.
Schedule for specific time (option 2): Prompt the user for a target time. Accept either HH:MM (24-hour local time) or YYYY-MM-DDTHH:MM (ISO 8601 date + time with T separator). Execute via Bash tool with dangerouslyDisableSandbox: true (substitute actual {session_id} and target time):
cortex overnight schedule <target-time> --state $CORTEX_COMMAND_ROOT/lifecycle/sessions/{session_id}/overnight-state.json
cortex overnight schedule registers a one-shot LaunchAgent (no tmux) that fires the runner at the target time and returns immediately. The Bash tool call MUST set dangerouslyDisableSandbox: true so the harness can reach launchctl.
Inform the user: After the Bash tool returns successfully, report the outcome:
tmux attach -t overnight-runner to monitor progress."The runner operates autonomously and tracks progress in the state file and event log. Resume at any time with /overnight resume.
/overnight resume)Scan $CORTEX_COMMAND_ROOT/lifecycle/sessions/*/overnight-state.json (sorted by modification time, most recent first) and load the first file whose phase is not complete using load_state(state_path=<path>) from cortex_command.overnight.state. You should pass the explicit state_path argument — the default path points to a different location. This mirrors the runner's own auto-discovery logic and works correctly whether state was written by a sandboxed or non-sandboxed session.
phase: complete): Report "No active overnight session found. Use /overnight to start a new session." Stop./overnight." → stop.Present the current session state to the user:
paused and state.paused_reason is non-None, include:
Session paused — reason: {paused_reason}
With contextual guidance based on the value:
budget_exhausted → "Resume when Anthropic budget resets, then run: overnight-start ..."stall_timeout → "Session stalled; investigate logs before resuming."signal → "Session received a kill signal; resume when ready."round_historyRead deferred questions from the deferred/ directory using read_deferrals() from cortex_command.overnight.deferral.
If there are deferred questions, present them using summarize_deferrals() from cortex_command.overnight.deferral. For blocking questions, highlight that the affected features are paused and waiting for a human decision.
Error: If read_deferrals() fails (e.g., directory permission error), report: "Could not read deferred questions from deferred/: {error}." Continue — proceed as if there are no deferred questions.
Based on the session phase, ask the user what to do:
| Phase | Options |
|---|---|
executing | Resume execution (print runner command), or view current progress |
paused | Address the cause of the pause (deferred questions, failures), then resume execution |
complete | View the morning report at lifecycle/morning-report.md |
planning | This should not normally occur (planning happens interactively). Offer to restart the session. |
Resume execution: Execute via Bash tool with dangerouslyDisableSandbox: true (substitute actual {session_id} from the loaded state):
overnight-start $CORTEX_COMMAND_ROOT/lifecycle/sessions/{session_id}/overnight-state.json 6h
The runner resumes from where it left off, skipping already-merged features. After the Bash tool returns, report: "Overnight session resumed. Attach with tmux attach -t overnight-runner to monitor progress."
View morning report: Direct the user to read lifecycle/morning-report.md for a summary of what was accomplished, what failed, and any deferred questions.
Address deferred questions: Present each blocking question from deferred/ and collect the user's answers. After answering, the user can resume execution.
/overnight status)Run overnight-status (the deployed script) and present its output to the user. If the command is not found, instruct the user to install the cortex-core plugin.
A successful /overnight invocation satisfies all of the following:
lifecycle/sessions/{session_id}/overnight-plan.md exists and contains the approved feature list with round assignments.lifecycle/sessions/{session_id}/overnight-state.json exists with phase: executing and all selected features in pending status.lifecycle/sessions/{session_id}/session.json exists with correct session_id, type: overnight, and feature slugs.git branch overnight/{session_id} exists in the repository.latest-overnight symlink is updated by the runner on startup, not by the skill.overnight-start was executed via Bash tool with an absolute state path using $CORTEX_COMMAND_ROOT and the correct time limit.overnight-events.log has a SESSION_START entry.A successful /overnight resume satisfies:
# Overnight Session Plan
**Session ID**: overnight-2025-11-14-2230
**Generated**: 2025-11-14 22:30:15
**Throttle**: tier-based adaptive
**Time Limit**: 6h
## Selected Features
| Round | Feature | Backlog | Type | Priority | Pre-work |
|-------|---------|---------|------|----------|----------|
| 1 | Add user authentication | #042 | feature | high | plan needed |
| 1 | Fix pagination bug | #051 | bug | high | plan ready |
| 2 | Add export to CSV | #038 | feature | medium | plan ready |
## Execution Strategy
- **Rounds**: 2
- **Throttle**: tier-based adaptive (round size determined by conflict tiers)
- **Features**: 3 (2 in Round 1, 1 in Round 2)
## Not Ready
| Feature | Reason |
|---------|--------|
| Add dark mode | Missing spec (`lifecycle/<feature-slug>/spec.md` not present) |
## Risk Assessment
- No file overlap detected between Round 1 features
- Round 2 depends on Round 1 completing successfully
## Stop Conditions
- Zero progress in a round (all features fail or defer)
- Time limit reached (6h)
{
"session_id": "overnight-2025-11-14-2230",
"type": "overnight",
"started": "2025-11-14T22:30:15Z",
"features": [
"add-user-authentication",
"fix-pagination-bug",
"add-export-to-csv"
]
}
The three orchestration skills are complementary:
/lifecycle): Interactive single-feature development. User present throughout all phases./pipeline): Batch multi-feature orchestration with an interactive front-end (research, spec, plan) and an execution back-end. User participates in planning, then execution runs autonomously./overnight): Fully autonomous overnight execution of features that already have research and spec artifacts. No interactive research or spec phases -- features must be ready (have completed discovery) before selection. The skill handles plan approval, then hands off entirely to the runner.The key difference: pipeline creates research and specs interactively during the session; overnight requires them to already exist (produced by /discovery or /lifecycle earlier).
cortex_command.overnight.backlog, cortex_command.overnight.plan, cortex_command.overnight.state, cortex_command.overnight.events, cortex_command.overnight.deferral).lifecycle/{slug}/research.md and lifecycle/{slug}/spec.md on disk, and must not be type: epic (checked after blocked-by, before artifact checks). The readiness gate in select_overnight_batch() enforces this. Features without all required artifacts are reported as ineligible with a reason. plan.md is generated during the session if missing — a plan generation sub-agent runs before dispatch and defers the feature (with a captured reason) if it cannot produce a valid plan.overnight/{session_id}). The runner opens a single PR from the integration branch to main at session end, containing all overnight changes for review.lifecycle/sessions/{session_id}/overnight-plan.md), the plan does not change. Runtime state lives in lifecycle/sessions/{session_id}/overnight-state.json.Agent isolation: "worktree". When launching features in parallel, always use the Agent tool with isolation: "worktree". Do not call git worktree add manually — in sandboxed sessions this fails because .claude/worktrees/ is Seatbelt-restricted, and a failed checkout leaves an orphaned branch requiring git branch -d <name> cleanup before retrying.Creates, edits, and optimizes skills for Claude Code, including drafting, evaluating with test prompts, iterating on performance, and improving skill descriptions for better triggering accuracy.
npx claudepluginhub charleshall888/cortex-command --plugin cortex-overnight