claude-overnight 1.0.1 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -52,7 +52,7 @@ claude-overnight
52
52
  ◆ Assessing... ✓ Vision met
53
53
  ```
54
54
 
55
- You interact once (objective, budget, model, review themes), then everything runs autonomously — thinking, planning, executing, reflecting, steering. Rate-limited? It waits and retries. Crash? Resume where you left off.
55
+ You interact once (objective, budget, model, review themes), then everything runs autonomously — thinking, planning, executing, reflecting, steering. Rate-limited? It waits and retries. Crash? Resume where you left off. Capped at usage limit? Pick up next time with full context preserved.
56
56
 
57
57
  ## How it works
58
58
 
@@ -62,7 +62,7 @@ For budgets > 15, the tool launches **architect agents** that explore your codeb
62
62
 
63
63
  ### 2. Orchestration
64
64
 
65
- An orchestrator agent reads all design documents and synthesizes concrete execution tasks — grounded in real files and patterns the architects found. No guesswork.
65
+ An orchestrator agent reads all design documents and synthesizes concrete execution tasks — grounded in real files and patterns the architects found. No guesswork. The task plan is also written to a file for resilience — if orchestration is interrupted, partial results survive.
66
66
 
67
67
  ### 3. Iterative execution
68
68
 
@@ -97,20 +97,30 @@ Every run gets its own folder in `.claude-overnight/runs/`. Nothing is ever over
97
97
  run.json, sessions/
98
98
  ```
99
99
 
100
- If a run crashes, gets rate-limited, or you Ctrl+C:
100
+ Any run that stops before the steering system declares the objective complete — capped at usage limit, Ctrl+C, crash, rate limit timeout, steering failure — is automatically resumable:
101
101
 
102
102
  ```
103
- Interrupted run
103
+ Unfinished run
104
104
  ╭──────────────────────────────────────────────────╮
105
105
  │ refactor auth, add tests, update docs │
106
- │ 50/200 sessions · 3 waves · $69.16
106
+ │ 50/200 sessions · 150 remaining · $69.16
107
107
  │ 34 merged · 16 unmerged · 0 failed branches │
108
108
  ╰──────────────────────────────────────────────────╯
109
109
 
110
110
  Resume │ Fresh │ Quit
111
111
  ```
112
112
 
113
- On resume: unmerged branches auto-merge, the wave loop continues, all context is preserved.
113
+ On resume: unmerged branches auto-merge, the wave loop continues, all context is preserved. Designs and reflections stay on disk until the objective is truly complete.
114
+
115
+ If the thinking phase succeeds but orchestration crashes, the next run detects the orphaned design docs and reuses them — no re-running $9 worth of architect agents:
116
+
117
+ ```
118
+ ✓ Reusing 5 design docs (from prior attempt)
119
+ Focus 0: Project Wizard UI vs VISION.md Flow
120
+ Focus 1: Team Load and Rebalancer Surface
121
+ Focus 2: Code Health After Swarm Wave
122
+ ...
123
+ ```
114
124
 
115
125
  **Knowledge carries forward** — new runs inherit knowledge from completed previous runs. Thinking agents and steering see what past runs built. Run 2 knows run 1 already built the auth system.
116
126
 
@@ -186,9 +196,10 @@ Built for unattended runs lasting hours or days.
186
196
 
187
197
  - **Hard block**: pauses until the rate limit window resets, then resumes
188
198
  - **Soft throttle**: slows dispatch at >75% utilization
199
+ - **Cooldown between phases**: waits for rate limit reset after thinking before starting orchestration
189
200
  - **Retry with backoff**: transient errors (429, overloaded) retry automatically
190
- - **Usage cap**: set a ceiling, active agents finish, no new ones start
191
- - **Planner retries**: steering and orchestration also retry on rate limits (30s/60s/120s backoff)
201
+ - **Usage cap**: set a ceiling, active agents finish, no new ones start — run is resumable
202
+ - **Planner retries**: steering and orchestration retry on rate limits (30s/60s/120s backoff) with full context
192
203
 
193
204
  ## Worktrees and merging
194
205
 
package/dist/index.js CHANGED
@@ -606,9 +606,18 @@ async function main() {
606
606
  console.log(chalk.dim(`\n ${completedRuns.length} previous run${completedRuns.length > 1 ? "s" : ""}`));
607
607
  for (const r of completedRuns.slice(0, 3)) {
608
608
  const date = r.state.startedAt?.slice(0, 10) || "unknown";
609
- const obj = r.state.objective?.slice(0, 40) || "";
609
+ const obj = r.state.objective?.slice(0, 50) || "";
610
610
  const cost = r.state.accCost > 0 ? ` · $${r.state.accCost.toFixed(0)}` : "";
611
- console.log(chalk.dim(` ${date} · ${r.state.accCompleted} tasks${cost}${obj ? ` · ${obj}` : ""}${obj.length >= 40 ? "…" : ""}`));
611
+ const merged = r.state.branches.filter(b => b.status === "merged").length;
612
+ console.log(chalk.dim(` ${date} · ${r.state.accCompleted} done · ${merged} merged${cost}${obj ? ` · ${obj}` : ""}${obj.length >= 50 ? "…" : ""}`));
613
+ // Show status if available
614
+ let status = "";
615
+ try {
616
+ status = readFileSync(join(r.dir, "status.md"), "utf-8").trim().split("\n")[0].slice(0, 80);
617
+ }
618
+ catch { }
619
+ if (status)
620
+ console.log(chalk.dim(` ${status}`));
612
621
  }
613
622
  }
614
623
  // ── Resume detection ──
@@ -628,10 +637,11 @@ async function main() {
628
637
  lastStatus = readFileSync(join(incomplete.dir, "status.md"), "utf-8").trim().slice(0, 120);
629
638
  }
630
639
  catch { }
631
- console.log(chalk.yellow(`\n ⚠ Interrupted run`));
640
+ const label = "Unfinished run";
641
+ console.log(chalk.yellow(`\n ⚠ ${label}`));
632
642
  const boxLines = [
633
643
  `${obj}${obj.length >= 50 ? "…" : ""}`,
634
- `${prev.accCompleted}/${prev.budget} sessions · ${prev.waveNum + 1} waves · $${prev.accCost.toFixed(2)}`,
644
+ `${prev.accCompleted}/${prev.budget} sessions · ${prev.remaining} remaining · $${prev.accCost.toFixed(2)}`,
635
645
  ];
636
646
  if (lastStatus)
637
647
  boxLines.push(lastStatus);
@@ -862,7 +872,17 @@ async function main() {
862
872
  mkdirSync(designDir, { recursive: true });
863
873
  const existingDesigns = readMdDir(designDir);
864
874
  if (existingDesigns) {
865
- console.log(chalk.green(`\n ✓ Reusing ${readdirSync(designDir).filter(f => f.endsWith(".md")).length} existing design docs`) + chalk.dim(` (from prior attempt)\n`));
875
+ const designFiles = readdirSync(designDir).filter(f => f.endsWith(".md")).sort();
876
+ console.log(chalk.green(`\n ✓ Reusing ${designFiles.length} design docs`) + chalk.dim(` (from prior attempt)`));
877
+ for (const f of designFiles) {
878
+ try {
879
+ const firstLine = readFileSync(join(designDir, f), "utf-8").split("\n")[0].replace(/^#+\s*/, "").trim();
880
+ if (firstLine)
881
+ console.log(chalk.dim(` ${firstLine.slice(0, 80)}`));
882
+ }
883
+ catch { }
884
+ }
885
+ console.log("");
866
886
  }
867
887
  else {
868
888
  const thinkingTasks = buildThinkingTasks(objective, themes, designDir, plannerModel, previousKnowledge || undefined);
@@ -1030,7 +1050,7 @@ async function main() {
1030
1050
  const waveHistory = [];
1031
1051
  let accCost, accCompleted, accFailed, accTools;
1032
1052
  let accIn = 0, accOut = 0;
1033
- let lastCapped = false, lastAborted = false;
1053
+ let lastCapped = false, lastAborted = false, objectiveComplete = false;
1034
1054
  let lastWaveKind;
1035
1055
  let reflectionBudgetUsed;
1036
1056
  const branches = [];
@@ -1193,6 +1213,7 @@ async function main() {
1193
1213
  if (steer.done || steer.action === "done") {
1194
1214
  console.log(chalk.green(` \u2713 ${steer.reasoning}\n`));
1195
1215
  steerDone = true;
1216
+ objectiveComplete = true;
1196
1217
  remaining = 0; // exit outer loop too
1197
1218
  break;
1198
1219
  }
@@ -1245,6 +1266,7 @@ async function main() {
1245
1266
  // action === "execute"
1246
1267
  if (steer.tasks.length === 0) {
1247
1268
  console.log(chalk.green(` \u2713 ${steer.reasoning}\n`));
1269
+ objectiveComplete = true;
1248
1270
  remaining = 0;
1249
1271
  break;
1250
1272
  }
@@ -1262,22 +1284,26 @@ async function main() {
1262
1284
  }
1263
1285
  waveNum++;
1264
1286
  }
1265
- // Mark run as done keep sessions/milestones/status/goal, clean transient files
1287
+ // Only truly "done" if steering explicitly completed the objective (or non-flex single wave with budget exhausted)
1288
+ const trulyDone = objectiveComplete || (!flex && remaining <= 0);
1289
+ const finalPhase = trulyDone ? "done" : "capped";
1266
1290
  saveRunState(runDir, {
1267
1291
  id: `run-${new Date().toISOString().slice(0, 19)}`, objective: objective ?? "", budget: budget ?? tasks.length,
1268
1292
  remaining, workerModel, plannerModel, concurrency, permissionMode,
1269
1293
  usageCap, flex, useWorktrees, mergeStrategy, waveNum, currentTasks: [],
1270
1294
  lastWaveKind, reflectionBudgetUsed, accCost, accCompleted, accFailed,
1271
- branches, phase: "done", startedAt: new Date(runStartedAt).toISOString(), cwd,
1295
+ branches, phase: finalPhase, startedAt: new Date(runStartedAt).toISOString(), cwd,
1272
1296
  });
1273
- try {
1274
- rmSync(join(runDir, "designs"), { recursive: true, force: true });
1275
- }
1276
- catch { }
1277
- try {
1278
- rmSync(join(runDir, "reflections"), { recursive: true, force: true });
1297
+ if (trulyDone) {
1298
+ try {
1299
+ rmSync(join(runDir, "designs"), { recursive: true, force: true });
1300
+ }
1301
+ catch { }
1302
+ try {
1303
+ rmSync(join(runDir, "reflections"), { recursive: true, force: true });
1304
+ }
1305
+ catch { }
1279
1306
  }
1280
- catch { }
1281
1307
  // Switch back if we created a run branch
1282
1308
  if (runBranch && originalRef) {
1283
1309
  try {
package/dist/types.d.ts CHANGED
@@ -125,7 +125,7 @@ export interface RunState {
125
125
  accCompleted: number;
126
126
  accFailed: number;
127
127
  branches: BranchRecord[];
128
- phase: "executing" | "steering" | "reflecting" | "done";
128
+ phase: "executing" | "steering" | "reflecting" | "capped" | "done";
129
129
  startedAt: string;
130
130
  cwd: string;
131
131
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-overnight",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "Run 10, 100, or 1000 Claude agents overnight. Parallel autonomous AI coding with thinking waves, iterative quality steering, crash recovery, and rate limit handling. Built on the Claude Agent SDK.",
5
5
  "type": "module",
6
6
  "bin": {