claude-overnight 1.11.13 → 1.11.14

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/dist/index.js CHANGED
@@ -269,15 +269,37 @@ async function main() {
269
269
  // (saveRunState always stores [] — the plan is on disk in tasks.json).
270
270
  if (resumeState.currentTasks.length === 0) {
271
271
  const loaded = salvageFromFile(join(resumeRunDir, "tasks.json"), resumeState.budget, () => { }, "resume");
272
- if (!loaded && resumeState.phase === "planning") {
273
- console.error(chalk.red(`\n Planning-phase run has no usable tasks.json — start Fresh instead.\n`));
274
- process.exit(1);
275
- }
276
272
  if (loaded) {
277
273
  resumeState.currentTasks = loaded;
278
274
  const label = resumeState.phase === "planning" ? "Resuming plan" : `Resuming ${resumeState.phase} run`;
279
275
  console.log(chalk.green(`\n ✓ ${label} · ${loaded.length} tasks loaded from tasks.json`));
280
276
  }
277
+ else if (resumeState.phase === "planning") {
278
+ // No tasks.json — the thinking wave got killed before orchestrate ran.
279
+ // If design docs survived, re-orchestrate from them (salvages the
280
+ // thinking spend instead of throwing it away).
281
+ const designs = readMdDir(join(resumeRunDir, "designs"));
282
+ if (!designs || !resumeState.objective) {
283
+ console.error(chalk.red(`\n Planning-phase run has no usable tasks.json or designs — start Fresh instead.\n`));
284
+ process.exit(1);
285
+ }
286
+ const remainingBudget = Math.max(resumeState.concurrency, resumeState.budget - resumeState.accCompleted);
287
+ const orchBudget = Math.min(50, Math.max(resumeState.concurrency, Math.ceil(remainingBudget * 0.5)));
288
+ const flexNote = `This is wave 1 of an adaptive multi-wave run (total budget: ${remainingBudget}). Plan the highest-impact foundational work first. Future waves will iterate based on what's learned.`;
289
+ console.log(chalk.cyan(`\n ◆ Re-orchestrating plan from existing designs...\n`));
290
+ process.stdout.write("\x1B[?25l");
291
+ try {
292
+ const orchTasks = await orchestrate(resumeState.objective, designs, cwd, resumeState.plannerModel, resumeState.workerModel, resumeState.permissionMode, orchBudget, resumeState.concurrency, makeProgressLog(), flexNote, join(resumeRunDir, "tasks.json"));
293
+ resumeState.currentTasks = orchTasks;
294
+ process.stdout.write(`\x1B[2K\r ${chalk.green(`✓ ${orchTasks.length} tasks`)}\n`);
295
+ }
296
+ catch (err) {
297
+ process.stdout.write("\x1B[?25h");
298
+ console.error(chalk.red(`\n Re-orchestration failed: ${err.message}\n Start Fresh instead.\n`));
299
+ process.exit(1);
300
+ }
301
+ process.stdout.write("\x1B[?25h");
302
+ }
281
303
  }
282
304
  const unmerged = resumeState.branches.filter(b => b.status === "unmerged").length;
283
305
  if (unmerged > 0) {
@@ -651,6 +673,26 @@ async function main() {
651
673
  thinkingOut = thinkingSwarm.totalOutputTokens;
652
674
  thinkingTools = thinkingSwarm.agents.reduce((sum, a) => sum + a.toolCalls, 0);
653
675
  thinkingHistory = { wave: -1, tasks: thinkingSwarm.agents.map(a => ({ prompt: a.task.prompt.slice(0, 200), status: a.status, filesChanged: a.filesChanged, error: a.error })) };
676
+ // Persist thinking cost/count into run.json so if the user quits
677
+ // between thinking and orchestrate, resume still sees the real spend
678
+ // and the run stays visible in the picker (designs on disk = resumable).
679
+ try {
680
+ saveRunState(runDir, {
681
+ id: runDir.split(/[/\\]/).pop() ?? "",
682
+ objective: objective, budget: budget ?? 10, remaining: (budget ?? 10) - thinkingUsed,
683
+ workerModel, plannerModel, concurrency, permissionMode,
684
+ usageCap, allowExtraUsage, extraUsageBudget,
685
+ flex, useWorktrees, mergeStrategy,
686
+ waveNum: 0, currentTasks: [],
687
+ accCost: thinkingCost, accCompleted: thinkingUsed, accFailed: 0,
688
+ accIn: thinkingIn, accOut: thinkingOut, accTools: thinkingTools,
689
+ branches: [],
690
+ phase: "planning",
691
+ startedAt: new Date().toISOString(),
692
+ cwd,
693
+ });
694
+ }
695
+ catch { }
654
696
  if (thinkingSwarm.rateLimitResetsAt) {
655
697
  const waitMs = thinkingSwarm.rateLimitResetsAt - Date.now();
656
698
  if (waitMs > 0) {
package/dist/state.js CHANGED
@@ -191,9 +191,12 @@ export function findIncompleteRuns(rootDir, filterCwd) {
191
191
  const state = loadRunState(runDir);
192
192
  if (!state || state.phase === "done" || state.cwd !== filterCwd)
193
193
  continue;
194
- // Planning-phase runs are only resumable if tasks.json was actually
195
- // written resuming without tasks is nothing to resume.
196
- if (state.phase === "planning" && !existsSync(join(runDir, "tasks.json")))
194
+ // Planning-phase runs are resumable if either tasks.json was written
195
+ // (orchestrate completed) OR design docs exist on disk (thinking wave
196
+ // got killed mid-way we can re-orchestrate from the designs on resume).
197
+ if (state.phase === "planning"
198
+ && !existsSync(join(runDir, "tasks.json"))
199
+ && !readMdDir(join(runDir, "designs")))
197
200
  continue;
198
201
  results.push({ dir: runDir, state });
199
202
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-overnight",
3
- "version": "1.11.13",
3
+ "version": "1.11.14",
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": {