@hackerrank/astra-cli 0.1.32 → 0.1.34
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/package.json +1 -1
- package/src/cli.js +2 -2
- package/src/repl.js +62 -36
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -538,7 +538,7 @@ async function main() {
|
|
|
538
538
|
`${Number(args.steps) > 0 ? ` turns<=${args.steps}` : ""}\x1b[0m`
|
|
539
539
|
);
|
|
540
540
|
console.error(`\x1b[2m[astra] workspace -> ${benchRun.workspace}\x1b[0m`);
|
|
541
|
-
console.error(`\x1b[2m[astra] session
|
|
541
|
+
console.error(`\x1b[2m[astra] To resume session use: astra --resume ${sessionId}\x1b[0m`);
|
|
542
542
|
} else {
|
|
543
543
|
console.error(
|
|
544
544
|
`\x1b[2m[astra] bench · model=${modelId} cwd=${cmdCwd}` +
|
|
@@ -618,7 +618,7 @@ async function main() {
|
|
|
618
618
|
: "") +
|
|
619
619
|
`\x1b[0m`
|
|
620
620
|
);
|
|
621
|
-
console.error(`\x1b[2m[astra] session
|
|
621
|
+
console.error(`\x1b[2m[astra] To resume session use: astra --resume ${sessionId}\x1b[0m`);
|
|
622
622
|
}
|
|
623
623
|
if (result.submission) console.log(result.submission);
|
|
624
624
|
|
package/src/repl.js
CHANGED
|
@@ -159,11 +159,12 @@ function footerLines(agent, model, yolo, mode = "agent") {
|
|
|
159
159
|
C.dim(rule),
|
|
160
160
|
modeTag + " " + C.cyan(repoLabel()) + " " + C.dim(modelBits.join(" ")),
|
|
161
161
|
C.dim(usage),
|
|
162
|
-
C.dim("shift+return: newline · opt+left/right: model · opt+up/down: reasoning ·
|
|
162
|
+
C.dim("shift+return: newline · opt+left/right: model · opt+up/down: reasoning · opt+tab: mode · ctrl+y: yolo"),
|
|
163
163
|
];
|
|
164
164
|
}
|
|
165
165
|
|
|
166
166
|
const FOOTER_LINES = 4; // divider + repo/model + usage + shortcuts
|
|
167
|
+
const LOADER_LINES = 1; // permanently reserved row below the divider
|
|
167
168
|
const PROMPT_PREFIX = C.green("› "); // live input row (minimal)
|
|
168
169
|
const HISTORY_PREFIX = C.green("you › "); // echoed into transcript/history
|
|
169
170
|
const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
|
|
@@ -300,6 +301,7 @@ export class Screen {
|
|
|
300
301
|
this._busyResume = false;
|
|
301
302
|
this._prevScrollBottom = 0;
|
|
302
303
|
this._prevFooterTop = 0;
|
|
304
|
+
this._renderedRows = new Map();
|
|
303
305
|
this._inPaste = false;
|
|
304
306
|
this.promptLabel = PROMPT_PREFIX;
|
|
305
307
|
this._onData = this._onData.bind(this);
|
|
@@ -310,16 +312,16 @@ export class Screen {
|
|
|
310
312
|
get rows() { return this.out.rows || 24; }
|
|
311
313
|
|
|
312
314
|
get promptLines() {
|
|
313
|
-
if (this._busy) return 1;
|
|
314
315
|
const visual = computeVisualLines(this.buf, this.promptLabel || PROMPT_PREFIX, this.cols);
|
|
315
|
-
const maxLines = Math.max(1, Math.floor((this.rows - FOOTER_LINES) / 2));
|
|
316
|
+
const maxLines = Math.max(1, Math.floor((this.rows - FOOTER_LINES - LOADER_LINES) / 2));
|
|
316
317
|
return Math.min(visual.length, maxLines);
|
|
317
318
|
}
|
|
318
319
|
|
|
319
|
-
get reserved() { return FOOTER_LINES + this.promptLines; }
|
|
320
|
+
get reserved() { return FOOTER_LINES + LOADER_LINES + this.promptLines; }
|
|
320
321
|
get scrollBottom() { return Math.max(1, this.rows - this.reserved); }
|
|
321
322
|
get footerTop() { return this.scrollBottom + 1; }
|
|
322
|
-
get
|
|
323
|
+
get loaderRow() { return this.footerTop + 1; }
|
|
324
|
+
get promptRow() { return this.loaderRow + LOADER_LINES; }
|
|
323
325
|
get statusStartRow() { return this.promptRow + this.promptLines; }
|
|
324
326
|
|
|
325
327
|
/** Enter sticky mode: set scroll margin, park cursor, start listening. */
|
|
@@ -342,6 +344,7 @@ export class Screen {
|
|
|
342
344
|
this.out.off("resize", this._onResize);
|
|
343
345
|
if (this.inp.isTTY) this.inp.setRawMode(false);
|
|
344
346
|
this.out.write("\x1b[?2004l"); // disable bracketed paste mode
|
|
347
|
+
this.out.write("\x1b[?25h"); // ensure the cursor is restored on exit
|
|
345
348
|
this.out.write("\x1b[r"); // reset scroll region
|
|
346
349
|
this.out.write(`\x1b[${this.rows};1H\n`); // move below footer
|
|
347
350
|
}
|
|
@@ -368,57 +371,63 @@ export class Screen {
|
|
|
368
371
|
|
|
369
372
|
/** Unified redraw of footer divider, multi-line prompt, and status lines without overwriting. */
|
|
370
373
|
redraw() {
|
|
371
|
-
|
|
374
|
+
const scrollChanged = this._prevScrollBottom !== this.scrollBottom;
|
|
375
|
+
if (scrollChanged) {
|
|
372
376
|
this.out.write(`\x1b[1;${this.scrollBottom}r`);
|
|
373
377
|
this._prevScrollBottom = this.scrollBottom;
|
|
378
|
+
// Prompt wrapping can move the footer. Clear the old pinned region once;
|
|
379
|
+
// ordinary input and spinner frames use the row diff below instead.
|
|
380
|
+
if (this._prevFooterTop && this._prevFooterTop !== this.footerTop) {
|
|
381
|
+
for (let row = this._prevFooterTop; row <= this.rows; row++) {
|
|
382
|
+
this.out.write(`\x1b[${row};1H\x1b[2K`);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
this._prevFooterTop = this.footerTop;
|
|
386
|
+
// A resize or prompt-height change moves pinned rows. Their old
|
|
387
|
+
// coordinates are no longer meaningful, so repaint them once.
|
|
388
|
+
this._renderedRows.clear();
|
|
374
389
|
}
|
|
375
390
|
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
this.out.write(`\x1b[${r};1H\x1b[2K`);
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
// Draw top divider rule at footerTop
|
|
384
|
-
this.out.write(`\x1b[${this.footerTop};1H`);
|
|
385
|
-
this.out.write(this.footer[0] || "");
|
|
391
|
+
const rows = new Map();
|
|
392
|
+
const put = (row, text) => {
|
|
393
|
+
if (row >= 1 && row <= this.rows) rows.set(row, text || "");
|
|
394
|
+
};
|
|
386
395
|
|
|
387
|
-
|
|
396
|
+
put(this.footerTop, this.footer[0]);
|
|
388
397
|
if (this._busy) {
|
|
389
|
-
this.out.write(`\x1b[${this.promptRow};1H`);
|
|
390
398
|
const frame = SPINNER_FRAMES[this._busyFrame % SPINNER_FRAMES.length];
|
|
391
|
-
this.
|
|
399
|
+
put(this.loaderRow, C.cyan(frame + " ") + C.dim(this._busyLabel + "…"));
|
|
392
400
|
} else {
|
|
393
401
|
const visual = computeVisualLines(this.buf, this.promptLabel || PROMPT_PREFIX, this.cols);
|
|
394
|
-
const
|
|
395
|
-
|
|
396
|
-
const row = this.promptRow + i;
|
|
397
|
-
this.out.write(`\x1b[${row};1H`);
|
|
398
|
-
this.out.write(visibleLines[i].text);
|
|
402
|
+
for (const [i, line] of visual.slice(-this.promptLines).entries()) {
|
|
403
|
+
put(this.promptRow + i, line.text);
|
|
399
404
|
}
|
|
400
405
|
}
|
|
401
|
-
|
|
402
|
-
// Draw status lines below the prompt
|
|
403
406
|
for (let i = 1; i < FOOTER_LINES; i++) {
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
407
|
+
put(this.statusStartRow + i - 1, this.footer[i]);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// Update only changed rows. Clearing a row before immediately redrawing it
|
|
411
|
+
// caused a visible blank frame on every keystroke and spinner tick.
|
|
412
|
+
const changed = new Set([...this._renderedRows.keys(), ...rows.keys()]);
|
|
413
|
+
let output = "";
|
|
414
|
+
for (const row of [...changed].sort((a, b) => a - b)) {
|
|
415
|
+
const next = rows.get(row) || "";
|
|
416
|
+
if (this._renderedRows.get(row) !== next) {
|
|
417
|
+
output += `\x1b[${row};1H\x1b[2K${next}`;
|
|
408
418
|
}
|
|
409
419
|
}
|
|
420
|
+
this._renderedRows = rows;
|
|
410
421
|
|
|
411
|
-
// Place cursor at the end of input
|
|
412
422
|
if (!this._busy) {
|
|
413
423
|
const visual = computeVisualLines(this.buf, this.promptLabel || PROMPT_PREFIX, this.cols);
|
|
414
424
|
const visibleLines = visual.slice(-this.promptLines);
|
|
415
425
|
const lastLine = visibleLines[visibleLines.length - 1] || { visibleLength: 0 };
|
|
416
|
-
const cursorRow = this.promptRow +
|
|
426
|
+
const cursorRow = this.promptRow + visibleLines.length - 1;
|
|
417
427
|
const cursorCol = Math.min(this.cols, lastLine.visibleLength + 1);
|
|
418
|
-
|
|
419
|
-
} else {
|
|
420
|
-
this.out.write("\x1b[u"); // restore cursor
|
|
428
|
+
output += `\x1b[${cursorRow};${cursorCol}H`;
|
|
421
429
|
}
|
|
430
|
+
if (output) this.out.write(output);
|
|
422
431
|
}
|
|
423
432
|
|
|
424
433
|
drawFooter() {
|
|
@@ -432,6 +441,7 @@ export class Screen {
|
|
|
432
441
|
/** Start the minimal loading state on the prompt row. */
|
|
433
442
|
startBusy(label = "working") {
|
|
434
443
|
this._busy = true;
|
|
444
|
+
this.out.write("\x1b[?25l"); // spinner updates must not move a visible cursor
|
|
435
445
|
this._busyLabel = label;
|
|
436
446
|
this._busyFrame = 0;
|
|
437
447
|
this.redraw();
|
|
@@ -448,6 +458,7 @@ export class Screen {
|
|
|
448
458
|
if (this._busyTimer) { clearInterval(this._busyTimer); this._busyTimer = null; }
|
|
449
459
|
this._busy = false;
|
|
450
460
|
this._busyResume = false;
|
|
461
|
+
this.out.write("\x1b[?25h");
|
|
451
462
|
this.redraw();
|
|
452
463
|
}
|
|
453
464
|
|
|
@@ -529,6 +540,13 @@ export class Screen {
|
|
|
529
540
|
this.onToggleMode();
|
|
530
541
|
return;
|
|
531
542
|
}
|
|
543
|
+
|
|
544
|
+
// Yolo toggle: Ctrl+Y
|
|
545
|
+
if (this.onToggleYolo && s === "\x19") {
|
|
546
|
+
this._clearEscLatch();
|
|
547
|
+
this.onToggleYolo();
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
532
550
|
{
|
|
533
551
|
const opt = matchOptionArrow(s, false);
|
|
534
552
|
if (opt) {
|
|
@@ -649,9 +667,10 @@ Interactive commands:
|
|
|
649
667
|
|
|
650
668
|
Keyboard shortcuts:
|
|
651
669
|
shift+return insert a newline for multi-line input (alt/opt+enter also works)
|
|
652
|
-
|
|
670
|
+
opt+tab switch agent / bench mode
|
|
653
671
|
opt+left/right cycle the model
|
|
654
672
|
opt+up/down cycle reasoning effort (off/low/medium/high)
|
|
673
|
+
ctrl+y toggle auto-run of commands (yolo mode)
|
|
655
674
|
esc clear the input line
|
|
656
675
|
`;
|
|
657
676
|
|
|
@@ -692,6 +711,13 @@ async function runStickyRepl(agent, model, yolo, intro = [], { runBench = null }
|
|
|
692
711
|
refresh();
|
|
693
712
|
};
|
|
694
713
|
|
|
714
|
+
|
|
715
|
+
// Ctrl+Y toggles yolo (auto-run) mode.
|
|
716
|
+
screen.onToggleYolo = () => {
|
|
717
|
+
yolo = !yolo;
|
|
718
|
+
log(C.dim(`[astra] auto-run ${yolo ? "ON" : "OFF"}.`));
|
|
719
|
+
refresh();
|
|
720
|
+
};
|
|
695
721
|
// Option+Left/Right cycles the model; Option+Up/Down cycles reasoning effort.
|
|
696
722
|
// Changes apply to the live model and are cached as the preferred agent
|
|
697
723
|
// prefs so the next `astra` launch reuses them.
|