claude-overnight 1.16.7 → 1.16.10
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/planner-query.js +8 -2
- package/dist/planner.js +3 -1
- package/dist/render.d.ts +1 -1
- package/dist/render.js +59 -9
- package/dist/swarm.js +3 -1
- package/dist/ui.d.ts +12 -0
- package/dist/ui.js +120 -5
- package/package.json +1 -1
package/dist/planner-query.js
CHANGED
|
@@ -153,8 +153,14 @@ async function runPlannerQueryOnce(prompt, opts, onLog) {
|
|
|
153
153
|
const ev = msg.event;
|
|
154
154
|
if (ev?.type === "content_block_start" && ev.content_block?.type === "tool_use") {
|
|
155
155
|
toolCount++;
|
|
156
|
-
|
|
157
|
-
|
|
156
|
+
const toolName = ev.content_block.name;
|
|
157
|
+
const input = ev.content_block.input;
|
|
158
|
+
// Enrich event with target file/path for readability
|
|
159
|
+
const target = input?.path ?? input?.file_path ?? input?.command
|
|
160
|
+
? (typeof input?.command === "string" ? input.command.split(" ").slice(0, 3).join(" ") : "")
|
|
161
|
+
: "";
|
|
162
|
+
lastLogText = target ? `${toolName} ${target}` : toolName;
|
|
163
|
+
onLog(target ? `${toolName} → ${target}` : toolName, "event");
|
|
158
164
|
}
|
|
159
165
|
if (ev?.type === "content_block_delta") {
|
|
160
166
|
const delta = ev.delta;
|
package/dist/planner.js
CHANGED
|
@@ -212,7 +212,9 @@ export function buildThinkingTasks(objective, themes, designDir, plannerModel, p
|
|
|
212
212
|
const prevBlock = previousKnowledge ? `\nKNOWLEDGE FROM PREVIOUS RUNS:\n${previousKnowledge}\n\nBuild on this — don't re-discover what's already known.\n` : "";
|
|
213
213
|
return themes.map((theme, i) => ({
|
|
214
214
|
id: `think-${i}`,
|
|
215
|
-
prompt:
|
|
215
|
+
prompt: `## Research: ${theme}
|
|
216
|
+
|
|
217
|
+
You are a senior architect exploring a codebase to design a solution.
|
|
216
218
|
|
|
217
219
|
OVERALL OBJECTIVE: ${objective}
|
|
218
220
|
${prevBlock}
|
package/dist/render.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ type RLGetter = () => {
|
|
|
10
10
|
windows: Map<string, RateLimitWindow>;
|
|
11
11
|
resetsAt?: number;
|
|
12
12
|
};
|
|
13
|
-
export declare function renderFrame(swarm: Swarm, showHotkeys: boolean, runInfo?: RunInfo): string;
|
|
13
|
+
export declare function renderFrame(swarm: Swarm, showHotkeys: boolean, runInfo?: RunInfo, selectedAgentId?: number): string;
|
|
14
14
|
export interface SteeringViewData {
|
|
15
15
|
/** The ephemeral ticker heartbeat — elapsed, tool count, cost, current reasoning snippet. */
|
|
16
16
|
statusLine: string;
|
package/dist/render.js
CHANGED
|
@@ -138,7 +138,7 @@ function renderUsageBars(out, w, swarm) {
|
|
|
138
138
|
out.push(` ${chalk.dim("Extra ")}${barStr} ${label}`);
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
|
-
export function renderFrame(swarm, showHotkeys, runInfo) {
|
|
141
|
+
export function renderFrame(swarm, showHotkeys, runInfo, selectedAgentId) {
|
|
142
142
|
const w = Math.max((process.stdout.columns ?? 80) || 80, 60);
|
|
143
143
|
const out = [];
|
|
144
144
|
const stoppingTag = swarm.aborted ? chalk.yellow("STOPPING") : "";
|
|
@@ -175,10 +175,38 @@ export function renderFrame(swarm, showHotkeys, runInfo) {
|
|
|
175
175
|
out.push(chalk.gray(" # Status Task" + " ".repeat(Math.max(1, w - 56)) + "Action"));
|
|
176
176
|
out.push(chalk.gray(" " + "\u2500".repeat(Math.min(w - 4, 100))));
|
|
177
177
|
for (const a of show)
|
|
178
|
-
out.push(fmtRow(a, w));
|
|
178
|
+
out.push(fmtRow(a, w, a.id === (selectedAgentId ?? -1)));
|
|
179
179
|
if (swarm.pending > 0)
|
|
180
180
|
out.push(chalk.gray(` ... + ${swarm.pending} queued`));
|
|
181
181
|
}
|
|
182
|
+
// ── Agent detail (progressive discovery) ──
|
|
183
|
+
const detailAgent = selectedAgentId != null
|
|
184
|
+
? swarm.agents.find(a => a.id === selectedAgentId)
|
|
185
|
+
: undefined;
|
|
186
|
+
if (detailAgent) {
|
|
187
|
+
out.push("");
|
|
188
|
+
section(out, w, `Agent ${detailAgent.id} detail \u00b7 [d] next \u00b7 [Esc] close`);
|
|
189
|
+
const taskLines = detailAgent.task.prompt.split("\n");
|
|
190
|
+
const maxTaskLines = Math.min(6, taskLines.length);
|
|
191
|
+
for (let i = 0; i < maxTaskLines; i++) {
|
|
192
|
+
out.push(` ${chalk.dim(truncate(taskLines[i].trim(), w - 6))}`);
|
|
193
|
+
}
|
|
194
|
+
if (taskLines.length > maxTaskLines)
|
|
195
|
+
out.push(chalk.dim(` \u2026 + ${taskLines.length - maxTaskLines} more lines`));
|
|
196
|
+
const meta = [];
|
|
197
|
+
if (detailAgent.currentTool)
|
|
198
|
+
meta.push(chalk.yellow(`tool: ${detailAgent.currentTool}`));
|
|
199
|
+
if (detailAgent.lastText)
|
|
200
|
+
meta.push(chalk.dim(truncate(detailAgent.lastText, 60)));
|
|
201
|
+
if (detailAgent.filesChanged != null)
|
|
202
|
+
meta.push(chalk.dim(`${detailAgent.filesChanged} files`));
|
|
203
|
+
if (detailAgent.costUsd != null)
|
|
204
|
+
meta.push(chalk.yellow(`$${detailAgent.costUsd.toFixed(3)}`));
|
|
205
|
+
if (detailAgent.toolCalls > 0)
|
|
206
|
+
meta.push(chalk.dim(`${detailAgent.toolCalls} tools`));
|
|
207
|
+
if (meta.length > 0)
|
|
208
|
+
out.push(` ${meta.join(chalk.dim(" \u00b7 "))}`);
|
|
209
|
+
}
|
|
182
210
|
// Merge results
|
|
183
211
|
if (swarm.mergeResults.length > 0) {
|
|
184
212
|
out.push("");
|
|
@@ -192,11 +220,21 @@ export function renderFrame(swarm, showHotkeys, runInfo) {
|
|
|
192
220
|
// Event log
|
|
193
221
|
out.push("");
|
|
194
222
|
out.push(chalk.gray(" \u2500\u2500\u2500 Events " + "\u2500".repeat(Math.min(w - 16, 90))));
|
|
195
|
-
const logN = Math.min(
|
|
223
|
+
const logN = Math.min(12, swarm.logs.length);
|
|
196
224
|
for (const entry of swarm.logs.slice(-logN)) {
|
|
197
225
|
const t = new Date(entry.time).toLocaleTimeString("en", { hour12: false });
|
|
198
226
|
const tag = entry.agentId < 0 ? chalk.magenta("[sys]") : chalk.cyan(`[${entry.agentId}]`);
|
|
199
|
-
|
|
227
|
+
// Tool-use events with target get a secondary detail line
|
|
228
|
+
const arrowIdx = entry.text.indexOf(" \u2192 ");
|
|
229
|
+
if (arrowIdx > 0 && arrowIdx < 20) {
|
|
230
|
+
const toolName = entry.text.slice(0, arrowIdx);
|
|
231
|
+
const target = entry.text.slice(arrowIdx + 3);
|
|
232
|
+
out.push(chalk.gray(` ${t} `) + tag + ` ${chalk.yellow(toolName)}`);
|
|
233
|
+
out.push(chalk.dim(` ${truncate(target, w - 10)}`));
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
out.push(chalk.gray(` ${t} `) + tag + ` ${colorEvent(truncate(entry.text, w - 22))}`);
|
|
237
|
+
}
|
|
200
238
|
}
|
|
201
239
|
if (showHotkeys) {
|
|
202
240
|
const pending = runInfo?.pendingSteer ?? 0;
|
|
@@ -204,7 +242,9 @@ export function renderFrame(swarm, showHotkeys, runInfo) {
|
|
|
204
242
|
const fixChip = swarm.failed > 0 && swarm.active > 0 ? chalk.yellow(" [f] fix") : "";
|
|
205
243
|
const retryChip = swarm.rateLimitPaused > 0 ? chalk.yellow(" [r] retry-now") : "";
|
|
206
244
|
const pauseLabel = swarm.paused ? "[p] resume" : "[p] pause";
|
|
207
|
-
|
|
245
|
+
const detailChip = swarm.active > 0 ? chalk.dim(" [d] detail") : "";
|
|
246
|
+
const selectChip = swarm.active > 0 && running.length <= 10 ? chalk.dim(" [0-9] select") : "";
|
|
247
|
+
out.push(chalk.dim(` [b] budget [t] cap [c] conc [e] extra ${pauseLabel} [s] steer [?] ask [q] stop`) + fixChip + retryChip + chip + detailChip + selectChip);
|
|
208
248
|
if (swarm.blocked > 0 && swarm.blocked === swarm.active) {
|
|
209
249
|
out.push(chalk.yellow(` all workers rate-limited — [r] retry-now, [c] reduce concurrency, [p] pause, [q] quit`));
|
|
210
250
|
}
|
|
@@ -317,14 +357,24 @@ export function renderSteeringFrame(runInfo, data, showHotkeys, rlGetter) {
|
|
|
317
357
|
out.push("");
|
|
318
358
|
}
|
|
319
359
|
section(out, w, "Planner activity");
|
|
320
|
-
const events = data.events.slice(-
|
|
360
|
+
const events = data.events.slice(-15);
|
|
321
361
|
if (events.length === 0) {
|
|
322
362
|
out.push(chalk.dim(" (waiting for planner\u2026)"));
|
|
323
363
|
}
|
|
324
364
|
else {
|
|
325
365
|
for (const e of events) {
|
|
326
366
|
const t = new Date(e.time).toLocaleTimeString("en", { hour12: false });
|
|
327
|
-
|
|
367
|
+
// Tool-use events with target get a secondary detail line
|
|
368
|
+
const arrowIdx = e.text.indexOf(" \u2192 ");
|
|
369
|
+
if (arrowIdx > 0 && arrowIdx < 30) {
|
|
370
|
+
const toolName = e.text.slice(0, arrowIdx);
|
|
371
|
+
const target = e.text.slice(arrowIdx + 3);
|
|
372
|
+
out.push(chalk.gray(` ${t} `) + chalk.magenta("[plan] ") + chalk.yellow(toolName));
|
|
373
|
+
out.push(chalk.dim(` ${truncate(target, w - 10)}`));
|
|
374
|
+
}
|
|
375
|
+
else {
|
|
376
|
+
out.push(chalk.gray(` ${t} `) + chalk.magenta("[plan] ") + colorEvent(truncate(e.text, w - 22)));
|
|
377
|
+
}
|
|
328
378
|
}
|
|
329
379
|
}
|
|
330
380
|
out.push("");
|
|
@@ -383,8 +433,8 @@ export function renderSummary(swarm) {
|
|
|
383
433
|
return out.join("\n");
|
|
384
434
|
}
|
|
385
435
|
// ── Row formatting ──
|
|
386
|
-
function fmtRow(a, w) {
|
|
387
|
-
const id = String(a.id).padStart(3);
|
|
436
|
+
function fmtRow(a, w, selected = false) {
|
|
437
|
+
const id = selected ? chalk.cyan.bold(String(a.id).padStart(3)) : String(a.id).padStart(3);
|
|
388
438
|
const elapsed = a.status === "running" && a.startedAt ? " " + chalk.dim(fmtDur(Date.now() - a.startedAt)) : "";
|
|
389
439
|
const spin = SPINNER[Math.floor(Date.now() / 250) % SPINNER.length];
|
|
390
440
|
const icon = a.status === "running"
|
package/dist/swarm.js
CHANGED
|
@@ -629,7 +629,9 @@ export class Swarm {
|
|
|
629
629
|
if (cb?.type === "tool_use") {
|
|
630
630
|
agent.currentTool = cb.name;
|
|
631
631
|
agent.toolCalls++;
|
|
632
|
-
|
|
632
|
+
const input = cb.input;
|
|
633
|
+
const target = input?.path ?? input?.file_path ?? (typeof input?.command === "string" ? input.command.split(" ").slice(0, 3).join(" ") : "");
|
|
634
|
+
this.log(agent.id, target ? `${cb.name} \u2192 ${target}` : cb.name);
|
|
633
635
|
}
|
|
634
636
|
}
|
|
635
637
|
else if (ev.type === "content_block_delta") {
|
package/dist/ui.d.ts
CHANGED
|
@@ -68,6 +68,9 @@ export declare class RunDisplay {
|
|
|
68
68
|
private lastCompleted;
|
|
69
69
|
private askState?;
|
|
70
70
|
private askBusy;
|
|
71
|
+
private askTempFile?;
|
|
72
|
+
/** ID of the agent whose detail panel is open; undefined = no detail shown. */
|
|
73
|
+
private selectedAgentId?;
|
|
71
74
|
private onSteer?;
|
|
72
75
|
private onAsk?;
|
|
73
76
|
constructor(runInfo: RunInfo, liveConfig?: LiveConfig, callbacks?: {
|
|
@@ -78,6 +81,15 @@ export declare class RunDisplay {
|
|
|
78
81
|
setAsk(state: AskState | undefined): void;
|
|
79
82
|
/** Signal to the UI whether an ask is in progress (prevents duplicate firings). */
|
|
80
83
|
setAskBusy(busy: boolean): void;
|
|
84
|
+
/** Cycle the selected agent detail to the next running agent (or first running if none selected). */
|
|
85
|
+
cycleSelectedAgent(): void;
|
|
86
|
+
/** Select a specific agent by ID for the detail panel. */
|
|
87
|
+
selectAgent(id: number): void;
|
|
88
|
+
/** Clear the agent detail panel. */
|
|
89
|
+
clearSelectedAgent(): void;
|
|
90
|
+
private clearAskTempFile;
|
|
91
|
+
/** Get the currently selected agent's ID for rendering. */
|
|
92
|
+
getSelectedAgentId(): number | undefined;
|
|
81
93
|
start(): void;
|
|
82
94
|
setWave(swarm: Swarm): void;
|
|
83
95
|
setSteering(rlGetter?: RLGetter, ctx?: SteeringContext): void;
|
package/dist/ui.js
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import chalk from "chalk";
|
|
2
2
|
import { renderFrame, renderSteeringFrame } from "./render.js";
|
|
3
3
|
import { splitPaste, segmentsToString, renderSegments, appendCharToSegments, appendPasteToSegments, backspaceSegments, } from "./cli.js";
|
|
4
|
+
import { mkdtempSync, writeFileSync, rmSync } from "fs";
|
|
5
|
+
import { tmpdir } from "os";
|
|
6
|
+
import { join } from "path";
|
|
7
|
+
import { execSync } from "child_process";
|
|
4
8
|
const MAX_STEERING_EVENTS = 60;
|
|
5
9
|
const MAX_INPUT_LEN = 600;
|
|
10
|
+
const MAX_ASK_LINES = 40;
|
|
11
|
+
let askTempDir;
|
|
6
12
|
export class RunDisplay {
|
|
7
13
|
runInfo;
|
|
8
14
|
liveConfig;
|
|
@@ -22,6 +28,9 @@ export class RunDisplay {
|
|
|
22
28
|
lastCompleted = -1;
|
|
23
29
|
askState;
|
|
24
30
|
askBusy = false;
|
|
31
|
+
askTempFile;
|
|
32
|
+
/** ID of the agent whose detail panel is open; undefined = no detail shown. */
|
|
33
|
+
selectedAgentId;
|
|
25
34
|
onSteer;
|
|
26
35
|
onAsk;
|
|
27
36
|
constructor(runInfo, liveConfig, callbacks) {
|
|
@@ -32,9 +41,69 @@ export class RunDisplay {
|
|
|
32
41
|
this.isTTY = !!process.stdout.isTTY;
|
|
33
42
|
}
|
|
34
43
|
/** Replace the ask state. Called by run.ts as the side query streams and completes. */
|
|
35
|
-
setAsk(state) {
|
|
44
|
+
setAsk(state) {
|
|
45
|
+
this.askState = state;
|
|
46
|
+
// Clean up previous temp file
|
|
47
|
+
this.clearAskTempFile();
|
|
48
|
+
// Write full answer to temp file when streaming is done and answer is long
|
|
49
|
+
if (state && !state.streaming && !state.error && state.answer) {
|
|
50
|
+
const lines = state.answer.split("\n");
|
|
51
|
+
if (lines.length > MAX_ASK_LINES) {
|
|
52
|
+
try {
|
|
53
|
+
askTempDir = mkdtempSync(join(tmpdir(), "overnight-ask-"));
|
|
54
|
+
this.askTempFile = join(askTempDir, "answer.txt");
|
|
55
|
+
writeFileSync(this.askTempFile, state.answer, "utf8");
|
|
56
|
+
}
|
|
57
|
+
catch { }
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
36
61
|
/** Signal to the UI whether an ask is in progress (prevents duplicate firings). */
|
|
37
62
|
setAskBusy(busy) { this.askBusy = busy; }
|
|
63
|
+
/** Cycle the selected agent detail to the next running agent (or first running if none selected). */
|
|
64
|
+
cycleSelectedAgent() {
|
|
65
|
+
if (!this.swarm)
|
|
66
|
+
return;
|
|
67
|
+
const running = this.swarm.agents.filter(a => a.status === "running");
|
|
68
|
+
if (running.length === 0) {
|
|
69
|
+
this.selectedAgentId = undefined;
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (this.selectedAgentId == null) {
|
|
73
|
+
this.selectedAgentId = running[0].id;
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
const idx = running.findIndex(a => a.id === this.selectedAgentId);
|
|
77
|
+
this.selectedAgentId = running[(idx + 1) % running.length].id;
|
|
78
|
+
}
|
|
79
|
+
/** Select a specific agent by ID for the detail panel. */
|
|
80
|
+
selectAgent(id) {
|
|
81
|
+
if (!this.swarm)
|
|
82
|
+
return;
|
|
83
|
+
const agent = this.swarm.agents.find(a => a.id === id);
|
|
84
|
+
if (agent && agent.status === "running")
|
|
85
|
+
this.selectedAgentId = id;
|
|
86
|
+
}
|
|
87
|
+
/** Clear the agent detail panel. */
|
|
88
|
+
clearSelectedAgent() { this.selectedAgentId = undefined; }
|
|
89
|
+
clearAskTempFile() {
|
|
90
|
+
if (this.askTempFile) {
|
|
91
|
+
try {
|
|
92
|
+
rmSync(this.askTempFile, { force: true });
|
|
93
|
+
}
|
|
94
|
+
catch { }
|
|
95
|
+
if (askTempDir) {
|
|
96
|
+
try {
|
|
97
|
+
rmSync(askTempDir, { recursive: true, force: true });
|
|
98
|
+
}
|
|
99
|
+
catch { }
|
|
100
|
+
}
|
|
101
|
+
this.askTempFile = undefined;
|
|
102
|
+
askTempDir = undefined;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/** Get the currently selected agent's ID for rendering. */
|
|
106
|
+
getSelectedAgentId() { return this.selectedAgentId; }
|
|
38
107
|
start() {
|
|
39
108
|
if (this.started)
|
|
40
109
|
return;
|
|
@@ -102,6 +171,8 @@ export class RunDisplay {
|
|
|
102
171
|
process.stdout.write("\x1B[?25h");
|
|
103
172
|
}
|
|
104
173
|
catch { }
|
|
174
|
+
// Clean up ask temp file
|
|
175
|
+
this.clearAskTempFile();
|
|
105
176
|
this.started = false;
|
|
106
177
|
}
|
|
107
178
|
resumeInterval() {
|
|
@@ -135,7 +206,7 @@ export class RunDisplay {
|
|
|
135
206
|
render() {
|
|
136
207
|
let frame = "";
|
|
137
208
|
if (this.swarm) {
|
|
138
|
-
frame = renderFrame(this.swarm, this.hasHotkeys(), this.runInfo);
|
|
209
|
+
frame = renderFrame(this.swarm, this.hasHotkeys(), this.runInfo, this.selectedAgentId);
|
|
139
210
|
}
|
|
140
211
|
else if (this.steeringActive) {
|
|
141
212
|
frame = renderSteeringFrame(this.runInfo, {
|
|
@@ -188,10 +259,18 @@ export class RunDisplay {
|
|
|
188
259
|
out.push(` ${chalk.dim("A: " + (a.answer || "thinking..."))}`);
|
|
189
260
|
}
|
|
190
261
|
else {
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
262
|
+
const allLines = a.answer.split("\n");
|
|
263
|
+
const showLines = allLines.slice(0, MAX_ASK_LINES);
|
|
264
|
+
out.push(` ${chalk.bold.green("A:")} ${showLines[0] || ""}`);
|
|
265
|
+
for (const ln of showLines.slice(1))
|
|
194
266
|
out.push(` ${ln}`);
|
|
267
|
+
if (allLines.length > MAX_ASK_LINES) {
|
|
268
|
+
const overflow = allLines.length - MAX_ASK_LINES;
|
|
269
|
+
out.push(chalk.dim(` \u2026 + ${overflow} more lines`));
|
|
270
|
+
if (this.askTempFile) {
|
|
271
|
+
out.push(chalk.dim(" \u23CE Enter to reveal full answer in Finder"));
|
|
272
|
+
}
|
|
273
|
+
}
|
|
195
274
|
}
|
|
196
275
|
return "\n" + out.join("\n");
|
|
197
276
|
}
|
|
@@ -249,6 +328,18 @@ export class RunDisplay {
|
|
|
249
328
|
/** Handle a typed (non-pasted) chunk. Returns true if the frame needs a redraw. */
|
|
250
329
|
handleTyped(s) {
|
|
251
330
|
const lc = this.liveConfig;
|
|
331
|
+
// Enter in hotkey mode reveals truncated ask answer in Finder
|
|
332
|
+
if (this.inputMode === "none" && this.askTempFile) {
|
|
333
|
+
for (const ch of s) {
|
|
334
|
+
if (ch === "\r" || ch === "\n") {
|
|
335
|
+
try {
|
|
336
|
+
execSync(`open -R ${JSON.stringify(this.askTempFile)}`);
|
|
337
|
+
}
|
|
338
|
+
catch { }
|
|
339
|
+
return true;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
}
|
|
252
343
|
if (this.inputMode === "budget" || this.inputMode === "threshold" || this.inputMode === "concurrency" || this.inputMode === "extra") {
|
|
253
344
|
let dirty = false;
|
|
254
345
|
for (const ch of s) {
|
|
@@ -370,6 +461,7 @@ export class RunDisplay {
|
|
|
370
461
|
return false;
|
|
371
462
|
if (key === "\x1B" && this.askState && !this.askState.streaming) {
|
|
372
463
|
this.askState = undefined;
|
|
464
|
+
this.clearAskTempFile();
|
|
373
465
|
return false;
|
|
374
466
|
}
|
|
375
467
|
if (key === "b" || key === "B") {
|
|
@@ -427,12 +519,35 @@ export class RunDisplay {
|
|
|
427
519
|
if (key === "?" && this.onAsk && this.swarm && !this.askBusy) {
|
|
428
520
|
if (this.askState && !this.askState.streaming) {
|
|
429
521
|
this.askState = undefined;
|
|
522
|
+
this.clearAskTempFile();
|
|
430
523
|
return false;
|
|
431
524
|
}
|
|
432
525
|
this.inputMode = "ask";
|
|
433
526
|
this.inputSegs = [];
|
|
434
527
|
return true;
|
|
435
528
|
}
|
|
529
|
+
// [d] cycle agent detail panel
|
|
530
|
+
if ((key === "d" || key === "D") && this.swarm && this.swarm.active > 0) {
|
|
531
|
+
if (this.selectedAgentId != null)
|
|
532
|
+
this.cycleSelectedAgent();
|
|
533
|
+
else
|
|
534
|
+
this.cycleSelectedAgent();
|
|
535
|
+
return true;
|
|
536
|
+
}
|
|
537
|
+
// ESC closes detail panel
|
|
538
|
+
if (key === "\x1B" && this.selectedAgentId != null) {
|
|
539
|
+
this.clearSelectedAgent();
|
|
540
|
+
return true;
|
|
541
|
+
}
|
|
542
|
+
// Number keys 0-9 select a specific agent by row index in the visible table
|
|
543
|
+
if (/^[0-9]$/.test(key) && this.swarm) {
|
|
544
|
+
const n = parseInt(key);
|
|
545
|
+
const running = this.swarm.agents.filter(a => a.status === "running");
|
|
546
|
+
if (n < running.length) {
|
|
547
|
+
this.selectAgent(running[n].id);
|
|
548
|
+
return true;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
436
551
|
if (key === "q" || key === "Q" || key === "\x03") {
|
|
437
552
|
if (this.swarm) {
|
|
438
553
|
if (this.swarm.aborted)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-overnight",
|
|
3
|
-
"version": "1.16.
|
|
3
|
+
"version": "1.16.10",
|
|
4
4
|
"description": "Background lane for your Claude Max plan. Parallel Claude Agent SDK sessions in git worktrees with a usage cap that reserves headroom for your interactive Claude Code. Crash-safe resume. Opus/Sonnet/Haiku + Qwen/OpenRouter.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|