---
name: "lights-out"
description: "Use when the user wants unattended overnight or away-from-keyboard coding work; says \"lights out\", \"nightshift\", \"run this overnight\", or \"keep working while I sleep\"; asks to launch, check, steer, or stop a running overnight job; wants a morning review; or gives follow-up findings on an overnight agent run."
---
# Lights Out
## Overview
Lights Out delegates a long-running coding objective to `nightshift` , which loops a coding agent
until a natural-language stop condition is met. Scout prepares and supervises the run;
**nightshift executes it**.
Core rule: **you orchestrate, nightshift implements.** Do not hand-edit files inside a scope a live
worker owns. If the work needs correcting, launch a new bounded run rather than taking over.
Second rule: **a stop condition being met is not acceptance.** "Stopped" only means the worker
stopped. Compare the result against the user's actual request and your own fresh verification.
## Why a wrapper instead of running the engine directly
The Scout session that starts a night run is usually gone before it ends — the app gets closed, the
machine is left alone. So anything that must happen at exit cannot live in the conversation.
| Tier | Runs as | Guaranteed | Responsibility |
|---|---|---|---|
| Finalize | PowerShell, inside the detached worker | Always | Capture commits, diff, end-state, re-run verification, write the vault note |
| Judgment | You, on demand | When asked | Assess quality, decide merge vs follow-up, fill in the Assessment section |
| Push | One-shot automation | Opt-in, always asked as a plain, non-blocking question | Completion ping (see "Digest: ask, don't assume") |
Never promise the user a summary that depends on this session still being alive.
## Scripts
Always go through these. Never invoke `nightshift` directly — the preflight is the entire point.
```powershell
$LO = "$env:USERPROFILE\.scout\m-skills\lights-out\scripts"
```
| Script | Use |
|---|---|
| `$LO\start-run.ps1` | Preflight, launch detached, register the run |
| `$LO\check-run.ps1` | Reconstruct state: polling, morning review, "what happened" |
State registry: `~\.scout\lights-out\runs.json` . **This is the handoff file.** A brand-new session
with no memory of the launch conversation finds every run here. Read it before saying you do not
know about a run.
## Nightshift is vendored
The skill ships its own pinned copy at `vendor\node_modules\nightshift\` , so it does not depend on
installing any unrelated published package at runtime.
Resolution order:
1. `$env:LIGHTS_OUT_NIGHTSHIFT` — explicit override, used by the test suite
2. the vendored copy, run as `node vendor\node_modules\nightshift\dist\cli.mjs`
3. `nightshift` on PATH — fallback only
Only `node` needs to be on PATH. Every run records which engine it used in `engineKind` ,
`engineSource` , `engineVersion` , and `engineExitCode` , plus the human-readable `engine` field in
launch output, so behavior changes stay traceable.
This vendored copy is the maintained local fork `0.1.51-lights-out` , based on
upstream `0.1.49` , commit `0227fe415d38e6df0fad4828c7999ba2a3b065de` , plus local
Nightshift changes. It is not byte-for-byte identical to upstream. Usage analytics
were removed at source level, including the client, CLI call sites, environment
handling, and build/release settings. Former telemetry variables cannot restore
it. Local logs and agent-provider networking are unchanged. Required upstream
copyright and license notices remain in the vendored `LICENSE` .
Maintenance archives are stored separately from the shareable skill:
- Source: `D:\OneDrive - Microsoft\HERE\nightshift-src.zip`
- Installer: `D:\OneDrive - Microsoft\HERE\nightshift-0.1.51-lights-out.tgz`
Normal runs do not read these archives or require access to that directory. Share the complete
skill folder, including its preinstalled `vendor\node_modules` , without either archive. The vendor
npm metadata references the maintainer's external installer only for maintenance; on another
machine, obtain that installer separately and update its path before attempting npm maintenance.
Do not run `npm install` merely to use a copied skill.
The source archive includes the maintained working-tree changes, without Git
history or installed dependencies. To refresh the pinned copy, build that source
with `pnpm run build` , create its installer with `pnpm pack` , and install the
rebuilt local archive into `vendor\node_modules\nightshift` . Never use an older
telemetry-capable archive. Then run:
```powershell
pwsh -NoProfile -File "$env:USERPROFILE\.scout\m-skills\lights-out\scripts\test\e2e.ps1"
```
No runtime install step is required.
On Windows, the skill folder can contain spaces. The pinned engine retains a
separate limitation: native `.cmd` agent paths configured through
`agentPathOverride` must not contain spaces. This does not require reinstalling
the engine or its dependencies.
## Launch
Gather these before launching. Ask for anything missing rather than guessing:
- **Repo path** — must be a git repo with a clean tree.
- **Objective** — one concrete outcome.
- **Stop condition** — observable. "Looks good" is not one; "`npm test` exits 0 and no files
outside `src/utils` changed" is.
- **Verification command** — how success is checked. Must actually exist on this machine.
- **Agent** — default `copilot` . Others: `claude` , `codex` , `cursor` , `opencode` , `rovodev` , `pi` ,
`acp:<target>` .
- **Mode** — Hands-Off or Companion (below).
- **Completion ping** — end your launch message with the plain question from "Digest: ask,
don't assume" below. Do not block on it and do not use `m_ask_user` for it.
```powershell
& "$LO\start-run.ps1" `
-RepoPath 'D:\Repos\myapp' `
-Prompt '< worker prompt — see references \prompts . md > ' `
-Title 'Short Title For The Vault Folder' `
-StopWhen '< observable condition > ' `
-VerifyCommand 'npm test' `
-Agent copilot `
-MaxIterations 40 `
-Mode hands-off
```
Returns JSON. If `ok` is `false` , report the `problems` list to the user and stop.
| Check | What it prevents |
|---|---|
| Nightshift unresolvable | Vendored copy missing and no fallback command available |
| Empty prompt | Engine blocks forever reading stdin with no TTY |
| Dirty working tree | Engine refuses to start |
| Already on a `nightshift/*` branch | Interactive overwrite prompt hard-fails without a TTY |
| Agent CLI missing | Run dies on iteration 1 at 2am |
| Verify command not on PATH | Morning review starts from a broken check |
Pass `-Worktree` to run alongside other work, `-CurrentBranch` to commit on the current branch, and
`-Push` to push after each successful iteration.
## Modes
**Hands-Off** — bounded task, clear verification, user is asleep. Launch, report the run id, stop.
Intervene only for hard failure or destructive behavior.
**Companion** — uncertain, exploratory, or design-heavy work; the user is around and wants steering.
Default to Companion when the user asks to iterate until satisfied, supplies review findings, or
asks for supervision. Poll with `check-run.ps1` between iterations.
## Steer
In Companion mode, judge each poll:
| Signal | Action |
|---|---|
| Real blocker found | Stop; relaunch with blocker-specific instructions |
| Good partial slice | Let it run, or tighten the next stop condition |
| Skipped requested research | Relaunch with research as the explicit first deliverable |
| Touching unrelated files | Stop and review before continuing |
| Claims success, verification disagrees | Review now; relaunch only with an evidence-based stop condition |
| User says not good enough | Relaunch with that finding as the sole bounded correction |
Steering means a **new bounded run** , not editing the files yourself.
## Morning Review
Triggered by "good morning", "how did it go", or any post-run check-in. Never answer from memory
and never ask the user what to review. Reconstruct:
```powershell
& "$LO\check-run.ps1" -Since 2
```
Then:
1. If `status` is `running` , say so first — it is still working; report progress, do not judge it.
2. If `status` is `orphaned` , the worker died without finalizing. Say that plainly; the vault note
will be missing and artifacts may be partial.
3. Read `vaultNote` , `notesTail` , and the commits. Treat notes as **claims, not evidence** .
4. Run independent verification yourself — the recorded `verifyExitCodeAfter` is a starting point,
not a substitute for looking at the diff.
5. Compare against the stop condition *and* what the user originally asked for.
6. Decide: **Mergeable** , **Needs follow-up run** , or **Do not merge** .
Then write the judgment back into the vault note. Do not merge unless explicitly authorized.
## Writing the assessment back
The wrapper leaves `## Assessment` as a stub. Complete it:
1. Open the `vaultNote` path from `check-run.ps1` .
2. Replace the Assessment section with your findings and verdict.
3. Set `status: active` in frontmatter, swap the `status/draft` tag for `status/active` , bump
`updated:` .
4. Leave every other file in the folder untouched — they are raw artifacts.
## Findings
When the user says "that is not right", "scope drift", "you missed X", or "why did it stop":
1. Treat the run as Companion from now on.
2. Convert each finding into one observable correction, preserving the user's wording and scope.
3. Relaunch on the same repo with a bounded prompt (see `references\prompts.md` ).
4. Review again. Repeat until no blocking findings remain or a real blocker surfaces.
## JSON schema note
Wrapper metadata now uses the generic `engine*` fields instead of the prior engine-specific prefix.
Historical entries are still read from one coherent legacy descriptor prefix when matching
`Kind` +`Source` fields are present; ambiguous legacy prefixes are rejected instead of guessed. New
runs write `engineKind` , `engineSource` , `engineVersion` , `engineExitCode` , and the launch-output
`engine` summary field.
## Digest: ask, don't assume
Do this on **every** launch, not just when the user brings it up first. Ask it as one plain,
direct question inside your normal launch message — not a special prompt, and not buried
inside other prose where it reads as a mention rather than a question.
**Do not use `m_ask_user` for this.** The user should be able to answer by *not* answering.
End your launch message with a question like "Want me to ping you on Teams when this
finishes?" and then stop there — do not add a follow-up tool call that blocks the turn
waiting for a reply. Treat non-response as "no" for this run: create nothing, and don't
chase an answer. If they answer yes — in that same turn or a later one — register the
ping then, exactly as if they'd brought it up unprompted.
A "no" (including silence) is not a standing preference for next time. Ask again, the same
low-friction way, on the next launch.
If they accept, register a **condition-triggered** one-shot automation immediately (not a
fixed daily schedule — a fixed time means the user waits until that time even if the run
finished hours earlier, or misses it if the run runs past it). It should poll
`check-run.ps1` and fire the moment status leaves `running` :
```text
m_create_automation(
name: "Lights Out ping — < run id > ",
triggerType: "condition",
conditionCheckInterval: 15,
oneShot: true,
teamsNotify: "always",
condition: "The lights-out run with id < run id > has finished — i.e. check-run.ps1 -Id < run id >
reports a status other than \"running\" (completed, failed, or orphaned).",
prompt: "Run the lights-out Morning Review for run < run id > : execute check-run.ps1 -Id < run id > .
Independently re-run verification and spot-check the actual artifacts yourself — do not
just relay the worker's self-report. Write the assessment back into the vault note per
'Writing the assessment back'. Then send a concise Teams message with the verdict
(Mergeable / Needs follow-up / Do not merge), the vault note path, and the branch name.
If status is still 'running' when you check, say so and skip the Teams message rather
than asserting completion."
)
```
The condition check interval (15 minutes here) trades promptness against automation-check
overhead; widen it for long expected runs, tighten it for short ones. The ping is a
convenience layered on top of guaranteed finalize — the artifacts and vault note exist
with or without it.
## Safety
- Never run destructive git commands to tidy a `nightshift/*` branch. The branch *is* the night's
work.
- Never present a worker's success summary as verified fact.
- If the user is away, produce branches and a report — not merges, not pushes to shared branches,
unless they explicitly authorized it beforehand.
- A run left on `status: running` with a dead worker is `orphaned` , not successful. Say so.
- Stop conditions must be observable. Rewrite vague ones before launching.
## References
- `references\prompts.md` — worker, steering, and findings prompt templates.
- `references\cli.md` — Nightshift flags and agent roster; `nightshift --help` is the source of
truth.