13 KiB
| name | description |
|---|---|
| lights-out | 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.
$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:
$env:LIGHTS_OUT_NIGHTSHIFT— explicit override, used by the test suite- the vendored copy, run as
node vendor\node_modules\nightshift\dist\cli.mjs nightshifton 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:
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 testexits 0 and no files outsidesrc/utilschanged" 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_userfor it.
& "$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:
& "$LO\check-run.ps1" -Since 2
Then:
- If
statusisrunning, say so first — it is still working; report progress, do not judge it. - If
statusisorphaned, the worker died without finalizing. Say that plainly; the vault note will be missing and artifacts may be partial. - Read
vaultNote,notesTail, and the commits. Treat notes as claims, not evidence. - Run independent verification yourself — the recorded
verifyExitCodeAfteris a starting point, not a substitute for looking at the diff. - Compare against the stop condition and what the user originally asked for.
- 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:
- Open the
vaultNotepath fromcheck-run.ps1. - Replace the Assessment section with your findings and verdict.
- Set
status: activein frontmatter, swap thestatus/drafttag forstatus/active, bumpupdated:. - 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":
- Treat the run as Companion from now on.
- Convert each finding into one observable correction, preserving the user's wording and scope.
- Relaunch on the same repo with a bounded prompt (see
references\prompts.md). - 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:
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: runningwith a dead worker isorphaned, 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 --helpis the source of truth.