claude-overnight 1.50.6 → 1.51.1
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/core/_version.d.ts +1 -1
- package/dist/core/_version.js +1 -1
- package/dist/run/run.js +11 -3
- package/dist/run/summary.d.ts +2 -0
- package/dist/run/summary.js +52 -28
- package/dist/run/wave-loop.js +2 -1
- package/dist/skills/librarian.js +28 -29
- package/package.json +1 -1
- package/plugins/claude-overnight/.claude-plugin/plugin.json +1 -1
package/dist/core/_version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const VERSION = "1.
|
|
1
|
+
export declare const VERSION = "1.51.1";
|
package/dist/core/_version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Auto-generated by build — do not edit manually.
|
|
2
|
-
export const VERSION = "1.
|
|
2
|
+
export const VERSION = "1.51.1";
|
package/dist/run/run.js
CHANGED
|
@@ -545,10 +545,18 @@ export async function executeRun(cfg) {
|
|
|
545
545
|
display.stop();
|
|
546
546
|
// ── Finalize ──
|
|
547
547
|
const trulyDone = objectiveComplete || (!flex && remaining <= 0);
|
|
548
|
-
// User-initiated quit (or abort via 'q' / SIGINT / stall-watchdog) ⇒ save as
|
|
549
|
-
// "stopped" so resume.ts offers the run and the incomplete work comes back.
|
|
550
548
|
const userQuit = stopping || lastAborted;
|
|
551
549
|
const wasCapped = lastCapped && !userQuit;
|
|
550
|
+
// Determine specific exit reason for the end brief
|
|
551
|
+
let exitReason;
|
|
552
|
+
if (trulyDone)
|
|
553
|
+
exitReason = "done";
|
|
554
|
+
else if (userQuit)
|
|
555
|
+
exitReason = "user-interrupted";
|
|
556
|
+
else if (wasCapped || remaining <= 0)
|
|
557
|
+
exitReason = "budget-exhausted";
|
|
558
|
+
else
|
|
559
|
+
exitReason = "planner-gave-up"; // steering returned false, planner couldn't produce tasks
|
|
552
560
|
const finalPhase = trulyDone ? "done"
|
|
553
561
|
: userQuit ? "stopped"
|
|
554
562
|
: wasCapped ? "capped"
|
|
@@ -632,7 +640,7 @@ export async function executeRun(cfg) {
|
|
|
632
640
|
runDir, runBranch, objective, waveNum, runStartedAt: cfg.runStartedAt,
|
|
633
641
|
branches, waveHistory,
|
|
634
642
|
accCost, accCompleted, accFailed, accTools, accIn, accOut,
|
|
635
|
-
remaining, lastCapped, lastAborted, stopping, trulyDone,
|
|
643
|
+
remaining, lastCapped, lastAborted, stopping, trulyDone, exitReason,
|
|
636
644
|
peakWorkerCtxTokens, peakWorkerCtxPct,
|
|
637
645
|
currentSwarmLogFile: currentSwarm?.logFile,
|
|
638
646
|
narrativeDeps: {
|
package/dist/run/summary.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ export interface FinalNarrativeDeps {
|
|
|
11
11
|
/** Generate a longer narrative summary at run end. Awaited (not fire-and-forget)
|
|
12
12
|
* because the caller wants the text inline in the final status block. */
|
|
13
13
|
export declare function generateFinalNarrative(deps: FinalNarrativeDeps, phase: string): Promise<string>;
|
|
14
|
+
export type ExitReason = "done" | "budget-exhausted" | "user-interrupted" | "planner-gave-up" | "circuit-breaker" | "stalled";
|
|
14
15
|
export interface SummaryArgs {
|
|
15
16
|
runDir: string;
|
|
16
17
|
runBranch?: string;
|
|
@@ -30,6 +31,7 @@ export interface SummaryArgs {
|
|
|
30
31
|
lastAborted: boolean;
|
|
31
32
|
stopping: boolean;
|
|
32
33
|
trulyDone: boolean;
|
|
34
|
+
exitReason: ExitReason;
|
|
33
35
|
peakWorkerCtxTokens: number;
|
|
34
36
|
peakWorkerCtxPct: number;
|
|
35
37
|
currentSwarmLogFile?: string;
|
package/dist/run/summary.js
CHANGED
|
@@ -32,7 +32,7 @@ export async function generateFinalNarrative(deps, phase) {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
export async function printFinalSummary(args) {
|
|
35
|
-
const { runDir, runBranch, objective, waveNum, runStartedAt, branches, waveHistory, accCost, accCompleted, accFailed, accTools, accIn, accOut, remaining, lastCapped,
|
|
35
|
+
const { runDir, runBranch, objective, waveNum, runStartedAt, branches, waveHistory, accCost, accCompleted, accFailed, accTools, accIn, accOut, remaining, lastCapped, exitReason, peakWorkerCtxTokens, peakWorkerCtxPct, currentSwarmLogFile, narrativeDeps, } = args;
|
|
36
36
|
const waves = waveNum + 1;
|
|
37
37
|
const elapsed = Math.round((Date.now() - runStartedAt) / 1000);
|
|
38
38
|
const elapsedStr = elapsed < 60 ? `${elapsed}s` : elapsed < 3600 ? `${Math.floor(elapsed / 60)}m ${elapsed % 60}s` : `${Math.floor(elapsed / 3600)}h ${Math.floor((elapsed % 3600) / 60)}m`;
|
|
@@ -40,26 +40,31 @@ export async function printFinalSummary(args) {
|
|
|
40
40
|
const totalConflicts = branches.filter(b => b.status === "merge-failed").length;
|
|
41
41
|
const termW = Math.max((process.stdout.columns ?? 80) || 80, 50);
|
|
42
42
|
const rule = (c = "─") => chalk.dim(` ${c.repeat(Math.min(termW - 4, 60))}`);
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
43
|
+
const bannerChar = accFailed === 0 ? "━" : "─";
|
|
44
|
+
// Banner: title + subtitle explaining why the run ended
|
|
45
|
+
const banner = {
|
|
46
|
+
done: { icon: "✓", title: "CLAUDE OVERNIGHT -- COMPLETE", color: chalk.green, explain: "The planner determined the objective was achieved." },
|
|
47
|
+
"budget-exhausted": { icon: "⚠", title: "CLAUDE OVERNIGHT -- BUDGET EXHAUSTED", color: chalk.yellow, explain: "All allocated sessions were consumed." },
|
|
48
|
+
"user-interrupted": { icon: "⚠", title: "CLAUDE OVERNIGHT -- INTERRUPTED", color: chalk.yellow, explain: "You quit mid-run with [q] or a signal." },
|
|
49
|
+
"planner-gave-up": { icon: "⚠", title: "CLAUDE OVERNIGHT -- PLANNER GAVE UP", color: chalk.magenta, explain: "The planner could not decompose the remaining work into actionable tasks." },
|
|
50
|
+
"circuit-breaker": { icon: "⚠", title: "CLAUDE OVERNIGHT -- HALTED", color: chalk.red, explain: "2+ consecutive waves produced no merged changes." },
|
|
51
|
+
stalled: { icon: "⚠", title: "CLAUDE OVERNIGHT -- STALLED", color: chalk.magenta, explain: "No progress detected; the run was halted to preserve budget." },
|
|
52
|
+
}[exitReason] ?? { icon: "⚠", title: "CLAUDE OVERNIGHT -- STOPPED", color: chalk.magenta, explain: "The run ended without a clear reason." };
|
|
53
|
+
const narrativePhase = exitReason === "done" ? "complete"
|
|
54
|
+
: exitReason === "budget-exhausted" ? "budget exhausted"
|
|
55
|
+
: exitReason === "user-interrupted" ? "interrupted"
|
|
56
|
+
: exitReason === "planner-gave-up" ? "planner gave up"
|
|
57
|
+
: exitReason === "circuit-breaker" ? "circuit breaker"
|
|
58
|
+
: exitReason === "stalled" ? "stalled"
|
|
59
|
+
: "stopped";
|
|
47
60
|
process.stdout.write(chalk.dim(`\n Writing final summary…`));
|
|
48
|
-
const narrative = await generateFinalNarrative(narrativeDeps,
|
|
61
|
+
const narrative = await generateFinalNarrative(narrativeDeps, narrativePhase);
|
|
49
62
|
process.stdout.write("\r" + " ".repeat(40) + "\r");
|
|
50
63
|
console.log("");
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
console.log(
|
|
54
|
-
|
|
55
|
-
console.log(chalk.bold.green(` ✓ CLAUDE OVERNIGHT -- COMPLETE`));
|
|
56
|
-
else if (remaining <= 0 || lastCapped)
|
|
57
|
-
console.log(chalk.bold.yellow(` ⚠ CLAUDE OVERNIGHT -- BUDGET EXHAUSTED`));
|
|
58
|
-
else if (stopping || lastAborted)
|
|
59
|
-
console.log(chalk.bold.yellow(` ⚠ CLAUDE OVERNIGHT -- INTERRUPTED`));
|
|
60
|
-
else
|
|
61
|
-
console.log(chalk.bold.yellow(` ⚠ CLAUDE OVERNIGHT -- STOPPED`));
|
|
62
|
-
console.log(bannerColor(` ${bannerChar.repeat(Math.min(termW - 4, 60))}`));
|
|
64
|
+
console.log(banner.color(` ${bannerChar.repeat(Math.min(termW - 4, 60))}`));
|
|
65
|
+
console.log(chalk.bold(banner.color(` ${banner.icon} ${banner.title}`)));
|
|
66
|
+
console.log(chalk.dim(` ${banner.explain}`));
|
|
67
|
+
console.log(banner.color(` ${bannerChar.repeat(Math.min(termW - 4, 60))}`));
|
|
63
68
|
console.log("");
|
|
64
69
|
if (objective) {
|
|
65
70
|
console.log(chalk.bold(" Objective"));
|
|
@@ -162,15 +167,34 @@ export async function printFinalSummary(args) {
|
|
|
162
167
|
if (currentSwarmLogFile)
|
|
163
168
|
console.log(chalk.dim(` Log: ${currentSwarmLogFile}`));
|
|
164
169
|
console.log("");
|
|
165
|
-
console.log(
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
170
|
+
console.log(banner.color(` ${bannerChar.repeat(Math.min(termW - 4, 60))}`));
|
|
171
|
+
// Actionable next-steps based on exit reason
|
|
172
|
+
const endMsg = (() => {
|
|
173
|
+
switch (exitReason) {
|
|
174
|
+
case "done":
|
|
175
|
+
return "Review the diff, then ship it.";
|
|
176
|
+
case "budget-exhausted":
|
|
177
|
+
return remaining > 0
|
|
178
|
+
? "Budget sessions remaining but usage cap hit. Raise the cap or re-run with --resume."
|
|
179
|
+
: "All sessions spent. Re-run with --resume to continue, or raise the budget.";
|
|
180
|
+
case "user-interrupted":
|
|
181
|
+
return "Run preserved. Use --resume to pick up where this left off.";
|
|
182
|
+
case "planner-gave-up": {
|
|
183
|
+
const lines = ["Planner could not decompose remaining work."];
|
|
184
|
+
if (remaining > 0)
|
|
185
|
+
lines.push(`${remaining} sessions unused — the work may be too vague or out of scope.`);
|
|
186
|
+
lines.push("Refine the objective or break it down manually, then re-run.");
|
|
187
|
+
return lines.join(" ");
|
|
188
|
+
}
|
|
189
|
+
case "circuit-breaker":
|
|
190
|
+
return "No changes landed in 2+ waves. Check for merge conflicts or agent errors in the log.";
|
|
191
|
+
case "stalled":
|
|
192
|
+
return "Run halted to preserve budget. Inspect status.md for blockers, then --resume.";
|
|
193
|
+
default:
|
|
194
|
+
return "Run preserved. --resume to continue.";
|
|
195
|
+
}
|
|
196
|
+
})();
|
|
197
|
+
console.log(chalk.bold(banner.color(` ${endMsg}`)));
|
|
198
|
+
console.log(banner.color(` ${bannerChar.repeat(Math.min(termW - 4, 60))}`));
|
|
175
199
|
console.log("");
|
|
176
200
|
}
|
package/dist/run/wave-loop.js
CHANGED
|
@@ -362,12 +362,13 @@ export async function runWaveLoop(host, ctx) {
|
|
|
362
362
|
const librarianStart = Date.now();
|
|
363
363
|
let librarianPromoted = 0, librarianPatched = 0, librarianQuarantined = 0, librarianRejected = 0;
|
|
364
364
|
try {
|
|
365
|
+
const librarianModel = host.fastModel ?? host.workerModel;
|
|
365
366
|
const lr = await runLibrarian({
|
|
366
367
|
fingerprint: host.repoFingerprint,
|
|
367
368
|
runId: host.runId,
|
|
368
369
|
wave: host.waveNum,
|
|
369
370
|
cwd: ctx.cwd,
|
|
370
|
-
model:
|
|
371
|
+
model: librarianModel,
|
|
371
372
|
envForModel: ctx.envForModel,
|
|
372
373
|
});
|
|
373
374
|
librarianPromoted = lr.promoted;
|
package/dist/skills/librarian.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
2
1
|
import { readFileSync, writeFileSync, mkdirSync, renameSync, existsSync, readdirSync, appendFileSync, } from "node:fs";
|
|
3
2
|
import { join } from "node:path";
|
|
4
3
|
import { openSkillsDb } from "./index-db.js";
|
|
@@ -84,44 +83,44 @@ function buildSubagentInput(canon, candidates, abOutcomes) {
|
|
|
84
83
|
return JSON.stringify({ canon, candidates, ab_outcomes: abOutcomes });
|
|
85
84
|
}
|
|
86
85
|
// ── Subagent call ──
|
|
86
|
+
// Direct POST /v1/messages — no tools needed, so the Agent SDK's CLI subprocess
|
|
87
|
+
// (with its multi-KB built-in system prompt and turn loop) is pure overhead and
|
|
88
|
+
// also pre-flight-rejects non-Anthropic model ids (qwen, composer-2, ...) even
|
|
89
|
+
// when routed through an Anthropic-compatible proxy.
|
|
87
90
|
async function callLibrarianSubagent(input, data) {
|
|
88
91
|
const env = input.envForModel?.(input.model);
|
|
89
92
|
const prompt = renderPrompt("40_skills/40-3_librarian-wrap", { vars: { data } });
|
|
90
|
-
|
|
91
|
-
const
|
|
93
|
+
const baseUrl = (env?.ANTHROPIC_BASE_URL ?? process.env.ANTHROPIC_BASE_URL ?? "https://api.anthropic.com").replace(/\/$/, "");
|
|
94
|
+
const headers = {
|
|
95
|
+
"Content-Type": "application/json",
|
|
96
|
+
"anthropic-version": "2023-06-01",
|
|
97
|
+
};
|
|
98
|
+
const bearer = env?.ANTHROPIC_AUTH_TOKEN ?? process.env.ANTHROPIC_AUTH_TOKEN;
|
|
99
|
+
const apiKey = env?.ANTHROPIC_API_KEY ?? process.env.ANTHROPIC_API_KEY;
|
|
100
|
+
if (bearer)
|
|
101
|
+
headers["Authorization"] = `Bearer ${bearer}`;
|
|
102
|
+
else if (apiKey)
|
|
103
|
+
headers["x-api-key"] = apiKey;
|
|
92
104
|
try {
|
|
93
|
-
const
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
permissionMode: "bypassPermissions",
|
|
99
|
-
allowDangerouslySkipPermissions: true,
|
|
100
|
-
maxTurns: 8,
|
|
101
|
-
...(env && { env }),
|
|
102
|
-
},
|
|
105
|
+
const res = await fetch(`${baseUrl}/v1/messages`, {
|
|
106
|
+
method: "POST",
|
|
107
|
+
headers,
|
|
108
|
+
body: JSON.stringify({ model: input.model, max_tokens: 8192, messages: [{ role: "user", content: prompt }] }),
|
|
109
|
+
signal: AbortSignal.timeout(LIBRARIAN_TIMEOUT_MS),
|
|
103
110
|
});
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
if (timedOut) {
|
|
107
|
-
pq.interrupt().catch(() => { });
|
|
108
|
-
break;
|
|
109
|
-
}
|
|
110
|
-
if (msg.type === "result" && msg.subtype === "success") {
|
|
111
|
-
resultText = msg.result || "";
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
pq.close();
|
|
115
|
-
if (timedOut) {
|
|
116
|
-
process.stderr.write("[librarian] subagent timed out\n");
|
|
111
|
+
if (!res.ok) {
|
|
112
|
+
process.stderr.write(`[librarian] HTTP ${res.status}: ${(await res.text().catch(() => "")).slice(0, 200)}\n`);
|
|
117
113
|
return null;
|
|
118
114
|
}
|
|
119
|
-
|
|
115
|
+
const body = await res.json();
|
|
116
|
+
const resultText = body.content?.map(c => c.text ?? "").join("") ?? "";
|
|
120
117
|
const cleaned = resultText.replace(/^```(?:json)?\s*\n([\s\S]*?)\n```\s*$/, "$1").trim();
|
|
121
118
|
return JSON.parse(cleaned);
|
|
122
119
|
}
|
|
123
|
-
|
|
124
|
-
|
|
120
|
+
catch (err) {
|
|
121
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
122
|
+
process.stderr.write(`[librarian] ${msg.includes("aborted") ? "timed out" : `error: ${msg}`}\n`);
|
|
123
|
+
return null;
|
|
125
124
|
}
|
|
126
125
|
}
|
|
127
126
|
// ── Action application ──
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-overnight",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.51.1",
|
|
4
4
|
"description": "Parallel Claude agents in git worktrees with a usage cap that reserves headroom for your interactive Claude Code. Crash-safe resume. Provider-agnostic model catalog (Anthropic, Cursor, OpenAI, Gemini, DeepSeek, Llama, Qwen) with capability-based task scoping.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-overnight",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.51.1",
|
|
4
4
|
"description": "Claude Code skill for understanding, installing, and inspecting claude-overnight runs -- parallel Claude agents in git worktrees with thinking waves, multi-wave steering, and crash-safe resume. Supports Cursor API Proxy, Qwen, OpenRouter.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Francesco Fornace"
|