@bahulam/code 2.6.13 → 2.6.15
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 +4 -4
- package/src/agents/scaffold.mjs +1 -0
- package/src/commands/agent.mjs +3 -2
- package/src/core/approval-log.mjs +45 -4
- package/src/core/approval.mjs +265 -41
- package/src/core/file-diff.mjs +1 -1
- package/src/core/headless.mjs +14 -3
- package/src/core/local-agent.mjs +3 -2
- package/src/core/risk-tier.mjs +53 -2
- package/src/core/safety.mjs +61 -4
- package/src/core/tool-executor.mjs +38 -16
- package/src/core/trust.mjs +5 -3
- package/src/index.mjs +1 -1
- package/src/terminal/agents.mjs +194 -18
- package/src/terminal/repl-render.mjs +126 -11
- package/src/terminal/repl-state.mjs +2 -0
- package/src/terminal/repl.mjs +553 -88
- package/src/terminal/tool-display.mjs +154 -2
- package/src/ui/approval.mjs +211 -14
- package/src/ui/icons.mjs +11 -5
- package/src/ui/input-dock.mjs +214 -29
- package/src/ui/slash-commands.mjs +10 -0
- package/src/ui/tool-card.mjs +261 -30
- package/src/ui/tool-details.mjs +206 -14
- package/src/ui/transcript-block.mjs +2 -3
package/src/ui/input-dock.mjs
CHANGED
|
@@ -52,6 +52,7 @@ const INPUT_RIGHT_PAD = 2;
|
|
|
52
52
|
const META_INDENT = 4;
|
|
53
53
|
|
|
54
54
|
const DEFAULT_MAX_INPUT_ROWS = 6;
|
|
55
|
+
const DEFAULT_OVERLAY_MAX_ROWS = 8;
|
|
55
56
|
const MIN_INPUT_ROWS = 1;
|
|
56
57
|
const MAX_INPUT_ROWS_CAP = 12;
|
|
57
58
|
|
|
@@ -71,10 +72,25 @@ let unsubResize = null;
|
|
|
71
72
|
// the cursor at the correct input position. Without this, readline echoes
|
|
72
73
|
// land on rows the dock briefly moved through mid-render and characters
|
|
73
74
|
// appear above/below the input row.
|
|
74
|
-
let lastFrame = { context: '', meta: '', tips: '', prefix: '', value: '', cursor: null };
|
|
75
|
+
let lastFrame = { context: '', meta: '', tips: '', prefix: '', value: '', cursor: null, overlayLines: null };
|
|
75
76
|
let resetting = false;
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
let lastGeometry = null;
|
|
78
|
+
let contentCursorRow = 1;
|
|
79
|
+
let contentCursorCol = 1;
|
|
80
|
+
let contentTrackingActive = false;
|
|
81
|
+
let suppressWriteTracking = 0;
|
|
82
|
+
let originalStdoutWrite = null;
|
|
83
|
+
let originalStderrWrite = null;
|
|
84
|
+
|
|
85
|
+
function write(s) {
|
|
86
|
+
try {
|
|
87
|
+
suppressWriteTracking++;
|
|
88
|
+
OUT.write(s);
|
|
89
|
+
} catch {
|
|
90
|
+
} finally {
|
|
91
|
+
suppressWriteTracking = Math.max(0, suppressWriteTracking - 1);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
78
94
|
function setScrollRegion(top, bottom) { write(`${ESC}${top};${bottom}r`); }
|
|
79
95
|
function clearScrollRegion() { write(`${ESC}r`); }
|
|
80
96
|
function saveCursor() { write(`${ESC}s`); }
|
|
@@ -90,10 +106,80 @@ function cols() {
|
|
|
90
106
|
return Math.max(40, term().columns || 80);
|
|
91
107
|
}
|
|
92
108
|
|
|
109
|
+
function drawableColumns() {
|
|
110
|
+
// Avoid writing into the final terminal column. Many terminals enter
|
|
111
|
+
// autowrap state there; during rapid resizes that can push fixed dock
|
|
112
|
+
// frame lines into scrollback.
|
|
113
|
+
return Math.max(1, cols() - 1);
|
|
114
|
+
}
|
|
115
|
+
|
|
93
116
|
function contentBottomRow() {
|
|
94
117
|
return Math.max(1, rows() - reservedRows);
|
|
95
118
|
}
|
|
96
119
|
|
|
120
|
+
function clampContentCursor() {
|
|
121
|
+
const bottom = contentBottomRow();
|
|
122
|
+
contentCursorRow = Math.max(1, Math.min(bottom, contentCursorRow || 1));
|
|
123
|
+
contentCursorCol = Math.max(1, Math.min(cols(), contentCursorCol || 1));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function resetContentCursor(row = 1, col = 1) {
|
|
127
|
+
contentCursorRow = Math.max(1, Math.min(contentBottomRow(), Math.floor(row || 1)));
|
|
128
|
+
contentCursorCol = Math.max(1, Math.min(cols(), Math.floor(col || 1)));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function trackContentWrite(chunk) {
|
|
132
|
+
if (!mounted || !contentTrackingActive || suppressWriteTracking > 0) return;
|
|
133
|
+
const text = Buffer.isBuffer(chunk) ? chunk.toString('utf8') : String(chunk ?? '');
|
|
134
|
+
if (!text) return;
|
|
135
|
+
const clean = text
|
|
136
|
+
.replace(/\x1b\[[0-9;?]*[ -/]*[@-~]/g, '')
|
|
137
|
+
.replace(/\x1b[()][A-Za-z0-9]/g, '');
|
|
138
|
+
const bottom = contentBottomRow();
|
|
139
|
+
const width = Math.max(1, drawableColumns());
|
|
140
|
+
for (const ch of clean) {
|
|
141
|
+
if (ch === '\r') {
|
|
142
|
+
contentCursorCol = 1;
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (ch === '\n') {
|
|
146
|
+
contentCursorRow = Math.min(bottom, contentCursorRow + 1);
|
|
147
|
+
contentCursorCol = 1;
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
contentCursorCol++;
|
|
151
|
+
if (contentCursorCol > width) {
|
|
152
|
+
contentCursorRow = Math.min(bottom, contentCursorRow + 1);
|
|
153
|
+
contentCursorCol = 1;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function patchOutputTracking() {
|
|
159
|
+
if (originalStdoutWrite || originalStderrWrite) return;
|
|
160
|
+
originalStdoutWrite = process.stdout.write.bind(process.stdout);
|
|
161
|
+
originalStderrWrite = process.stderr.write.bind(process.stderr);
|
|
162
|
+
process.stdout.write = function trackedStdoutWrite(chunk, ...args) {
|
|
163
|
+
trackContentWrite(chunk);
|
|
164
|
+
return originalStdoutWrite(chunk, ...args);
|
|
165
|
+
};
|
|
166
|
+
process.stderr.write = function trackedStderrWrite(chunk, ...args) {
|
|
167
|
+
trackContentWrite(chunk);
|
|
168
|
+
return originalStderrWrite(chunk, ...args);
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function unpatchOutputTracking() {
|
|
173
|
+
if (originalStdoutWrite) {
|
|
174
|
+
process.stdout.write = originalStdoutWrite;
|
|
175
|
+
originalStdoutWrite = null;
|
|
176
|
+
}
|
|
177
|
+
if (originalStderrWrite) {
|
|
178
|
+
process.stderr.write = originalStderrWrite;
|
|
179
|
+
originalStderrWrite = null;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
97
183
|
// Row map (top → bottom of the reserved region).
|
|
98
184
|
function topRuleRow() { return contentBottomRow() + 1; }
|
|
99
185
|
function spacerAboveRow() { return topRuleRow() + 1; }
|
|
@@ -117,6 +203,18 @@ function resolveMaxInputRows(requested) {
|
|
|
117
203
|
return Math.max(MIN_INPUT_ROWS, Math.min(MAX_INPUT_ROWS_CAP, n));
|
|
118
204
|
}
|
|
119
205
|
|
|
206
|
+
function resolveOverlayRowCap(requested = DEFAULT_OVERLAY_MAX_ROWS) {
|
|
207
|
+
const n = Number.parseInt(String(requested), 10);
|
|
208
|
+
if (!Number.isFinite(n)) return DEFAULT_OVERLAY_MAX_ROWS;
|
|
209
|
+
return Math.max(MIN_INPUT_ROWS, Math.min(MAX_INPUT_ROWS_CAP, n));
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function overlayRowsForWrapped(wrappedLength, requestedMaxRows = DEFAULT_OVERLAY_MAX_ROWS) {
|
|
213
|
+
const rowCap = resolveOverlayRowCap(requestedMaxRows);
|
|
214
|
+
const wanted = Math.max(MIN_INPUT_ROWS, Math.floor(Number(wrappedLength) || 0));
|
|
215
|
+
return Math.min(rowCap, wanted);
|
|
216
|
+
}
|
|
217
|
+
|
|
120
218
|
// How many input rows does this (prefix + value) buffer need? Wrapped line
|
|
121
219
|
// count clamped to [1, inputRowsMax]. Beyond the cap, tail-with-ellipsis
|
|
122
220
|
// takes over inside drawInputLines so at most inputRowsMax rows render.
|
|
@@ -137,8 +235,8 @@ function computeInputRowsForBuffer(prefix, value) {
|
|
|
137
235
|
// meta line) which then leaks into the transcript as streamed content
|
|
138
236
|
// scrolls past them. We clear the old dock region BEFORE moving the frame
|
|
139
237
|
// so the freed rows are blank when they enter the scroll region.
|
|
140
|
-
function setInputRowsTo(nextRows) {
|
|
141
|
-
const clamped = Math.max(MIN_INPUT_ROWS, Math.min(
|
|
238
|
+
function setInputRowsTo(nextRows, { maxRows = inputRowsMax } = {}) {
|
|
239
|
+
const clamped = Math.max(MIN_INPUT_ROWS, Math.min(maxRows, Math.floor(nextRows)));
|
|
142
240
|
if (clamped === inputRows) return false;
|
|
143
241
|
const shrinking = clamped < inputRows;
|
|
144
242
|
if (mounted && shrinking) {
|
|
@@ -162,7 +260,7 @@ function setInputRowsTo(nextRows) {
|
|
|
162
260
|
|
|
163
261
|
function padLine(text) {
|
|
164
262
|
const value = String(text || '');
|
|
165
|
-
const pad = Math.max(0,
|
|
263
|
+
const pad = Math.max(0, drawableColumns() - visibleWidth(value));
|
|
166
264
|
return value + ' '.repeat(pad);
|
|
167
265
|
}
|
|
168
266
|
|
|
@@ -183,7 +281,7 @@ function ruleChars(count) {
|
|
|
183
281
|
// - Optional right-aligned context strip (session tokens/elapsed) with rule
|
|
184
282
|
// fill between label and context
|
|
185
283
|
function topRuleLine(context = '') {
|
|
186
|
-
const w = Math.max(0,
|
|
284
|
+
const w = Math.max(0, drawableColumns());
|
|
187
285
|
const brand = paint.brand.primary;
|
|
188
286
|
const bold = paint.bold;
|
|
189
287
|
|
|
@@ -192,14 +290,19 @@ function topRuleLine(context = '') {
|
|
|
192
290
|
const label = bold(brand(BRAND_LABEL));
|
|
193
291
|
const leftBlock = `${leadRule} ${accent}${label}`;
|
|
194
292
|
const leftWidth = visibleWidth(leftBlock);
|
|
293
|
+
const rightPadRule = 2;
|
|
195
294
|
|
|
196
|
-
const
|
|
295
|
+
const maxCtxWidth = Math.max(0, Math.min(
|
|
296
|
+
Math.floor(w / 2),
|
|
297
|
+
w - leftWidth - 1 /* left gap */ - 1 /* minimum middle */ - 1 /* ctx gap */ - rightPadRule - 1 /* tail gap */,
|
|
298
|
+
));
|
|
299
|
+
const ctx = fitText(context, maxCtxWidth);
|
|
197
300
|
const ctxWidth = ctx ? visibleWidth(ctx) : 0;
|
|
198
301
|
|
|
199
302
|
// Rule fill in the middle. Reserve 1 space around ctx when present.
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
303
|
+
const middleWidth = ctx
|
|
304
|
+
? Math.max(1, w - leftWidth - ctxWidth - rightPadRule - 3)
|
|
305
|
+
: Math.max(1, w - leftWidth - 1);
|
|
203
306
|
const middleRule = brand(ruleChars(middleWidth));
|
|
204
307
|
|
|
205
308
|
if (!ctx) {
|
|
@@ -210,7 +313,7 @@ function topRuleLine(context = '') {
|
|
|
210
313
|
}
|
|
211
314
|
|
|
212
315
|
function bottomRuleLine() {
|
|
213
|
-
return paint.brand.primary(ruleChars(Math.max(0,
|
|
316
|
+
return paint.brand.primary(ruleChars(Math.max(0, drawableColumns())));
|
|
214
317
|
}
|
|
215
318
|
|
|
216
319
|
// Always park the cursor at the tracked (prefix, value) input position.
|
|
@@ -229,11 +332,15 @@ function parkCursorAtInput() {
|
|
|
229
332
|
focusDockInput(prefix, value, lastFrame.cursor);
|
|
230
333
|
}
|
|
231
334
|
|
|
232
|
-
function applyLayout() {
|
|
335
|
+
function applyLayout({ clearPrevious = false } = {}) {
|
|
233
336
|
if (!mounted) return;
|
|
337
|
+
if (clearPrevious && lastGeometry) {
|
|
338
|
+
clearDockArea({ restore: true, geometry: lastGeometry });
|
|
339
|
+
}
|
|
234
340
|
const bottom = contentBottomRow();
|
|
235
341
|
setScrollRegion(1, bottom);
|
|
236
342
|
renderFrame(lastFrame);
|
|
343
|
+
clampContentCursor();
|
|
237
344
|
// On (re)mount and resize, park at input if we have one; otherwise sit at
|
|
238
345
|
// the bottom of the content region so any pending content writes flush
|
|
239
346
|
// above the dock rather than into a stale mid-frame position.
|
|
@@ -257,6 +364,9 @@ function renderFrame(frame = {}) {
|
|
|
257
364
|
clearLine();
|
|
258
365
|
|
|
259
366
|
// (input rows are written by drawInputLines / clearInputRows)
|
|
367
|
+
if (Array.isArray(lastFrame.overlayLines)) {
|
|
368
|
+
drawInputLines(lastFrame.overlayLines);
|
|
369
|
+
}
|
|
260
370
|
|
|
261
371
|
// Spacer below input.
|
|
262
372
|
moveTo(spacerBelowRow(), 1);
|
|
@@ -290,6 +400,20 @@ function renderFrame(frame = {}) {
|
|
|
290
400
|
// pinned-status writers clobber the outer save and restore to the wrong
|
|
291
401
|
// place. Instead, callers that need the cursor parked at the input row
|
|
292
402
|
// invoke parkCursorAtInput() after renderFrame returns.
|
|
403
|
+
lastGeometry = {
|
|
404
|
+
top: topRuleRow(),
|
|
405
|
+
bottom: rows(),
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function clearDockRows(startRow, endRow) {
|
|
410
|
+
const maxRow = rows();
|
|
411
|
+
const start = Math.max(1, Math.min(maxRow, Math.floor(startRow || 1)));
|
|
412
|
+
const end = Math.max(start, Math.min(maxRow, Math.floor(endRow || maxRow)));
|
|
413
|
+
for (let row = start; row <= end; row++) {
|
|
414
|
+
moveTo(row, 1);
|
|
415
|
+
clearLine();
|
|
416
|
+
}
|
|
293
417
|
}
|
|
294
418
|
|
|
295
419
|
function clearInputRows() {
|
|
@@ -299,13 +423,10 @@ function clearInputRows() {
|
|
|
299
423
|
}
|
|
300
424
|
}
|
|
301
425
|
|
|
302
|
-
export function clearDockArea({ restore = true } = {}) {
|
|
426
|
+
export function clearDockArea({ restore = true, geometry = null } = {}) {
|
|
303
427
|
if (!mounted) return false;
|
|
304
428
|
if (restore) saveCursor();
|
|
305
|
-
|
|
306
|
-
moveTo(row, 1);
|
|
307
|
-
clearLine();
|
|
308
|
-
}
|
|
429
|
+
clearDockRows(geometry?.top || topRuleRow(), geometry?.bottom || rows());
|
|
309
430
|
if (restore) restoreCursor();
|
|
310
431
|
return true;
|
|
311
432
|
}
|
|
@@ -329,6 +450,15 @@ function layoutInput(prefix, value) {
|
|
|
329
450
|
};
|
|
330
451
|
}
|
|
331
452
|
|
|
453
|
+
function layoutOverlayLines(lines) {
|
|
454
|
+
const budget = inputTextBudget();
|
|
455
|
+
const wrapped = [];
|
|
456
|
+
for (const line of lines) {
|
|
457
|
+
wrapped.push(...wrapToLines(line, budget));
|
|
458
|
+
}
|
|
459
|
+
return wrapped.length ? wrapped : [''];
|
|
460
|
+
}
|
|
461
|
+
|
|
332
462
|
function drawInputLines(lines) {
|
|
333
463
|
const indent = ' '.repeat(INPUT_INDENT);
|
|
334
464
|
for (let i = 0; i < inputRows; i++) {
|
|
@@ -346,7 +476,11 @@ export function isInputDockMounted() {
|
|
|
346
476
|
return mounted;
|
|
347
477
|
}
|
|
348
478
|
|
|
349
|
-
export function mountInputDock({
|
|
479
|
+
export function mountInputDock({
|
|
480
|
+
inputRowsMax: requestedMax,
|
|
481
|
+
initialContentRow = 1,
|
|
482
|
+
initialContentCol = 1,
|
|
483
|
+
} = {}) {
|
|
350
484
|
const t = term();
|
|
351
485
|
if (!t.isTTY || t.plain) return false;
|
|
352
486
|
if (t.ttyMode !== 'rich' || t.fixedInput === false) return false;
|
|
@@ -356,10 +490,13 @@ export function mountInputDock({ inputRowsMax: requestedMax } = {}) {
|
|
|
356
490
|
inputRowsMax = resolveMaxInputRows(requestedMax);
|
|
357
491
|
inputRows = MIN_INPUT_ROWS;
|
|
358
492
|
reservedRows = FIXED_ROWS + inputRows;
|
|
493
|
+
resetContentCursor(initialContentRow, initialContentCol);
|
|
494
|
+
contentTrackingActive = false;
|
|
359
495
|
mounted = true;
|
|
496
|
+
patchOutputTracking();
|
|
360
497
|
applyLayout();
|
|
361
498
|
|
|
362
|
-
unsubResize = onResize(() => applyLayout());
|
|
499
|
+
unsubResize = onResize(() => applyLayout({ clearPrevious: true }));
|
|
363
500
|
process.once('exit', safeUnmount);
|
|
364
501
|
process.once('SIGTERM', () => { safeUnmount(); process.exit(143); });
|
|
365
502
|
return true;
|
|
@@ -376,7 +513,10 @@ export function unmountInputDock() {
|
|
|
376
513
|
if (unsubResize) { unsubResize(); unsubResize = null; }
|
|
377
514
|
} finally {
|
|
378
515
|
mounted = false;
|
|
516
|
+
contentTrackingActive = false;
|
|
517
|
+
unpatchOutputTracking();
|
|
379
518
|
resetting = false;
|
|
519
|
+
lastGeometry = null;
|
|
380
520
|
}
|
|
381
521
|
}
|
|
382
522
|
|
|
@@ -384,17 +524,19 @@ function safeUnmount() { try { unmountInputDock(); } catch {} }
|
|
|
384
524
|
|
|
385
525
|
export function moveToContent() {
|
|
386
526
|
if (!mounted) return false;
|
|
387
|
-
|
|
527
|
+
clampContentCursor();
|
|
528
|
+
contentTrackingActive = true;
|
|
529
|
+
moveTo(contentCursorRow, contentCursorCol);
|
|
388
530
|
return true;
|
|
389
531
|
}
|
|
390
532
|
|
|
391
|
-
//
|
|
392
|
-
//
|
|
393
|
-
//
|
|
394
|
-
// without piling copies of itself into scrollback.
|
|
533
|
+
// The next transcript row. Spinner/status overlays live next to the latest
|
|
534
|
+
// content instead of near the scroll-region bottom; otherwise sparse agent
|
|
535
|
+
// events leave large blank holes between visible lines.
|
|
395
536
|
export function pinnedStatusRow() {
|
|
396
537
|
if (!mounted) return null;
|
|
397
|
-
|
|
538
|
+
clampContentCursor();
|
|
539
|
+
return Math.max(1, Math.min(contentBottomRow(), contentCursorRow));
|
|
398
540
|
}
|
|
399
541
|
|
|
400
542
|
export function drawPinnedStatus(line) {
|
|
@@ -426,6 +568,7 @@ export function clearPinnedStatus() {
|
|
|
426
568
|
// clearing their overlay.
|
|
427
569
|
export function redrawDockFrame() {
|
|
428
570
|
if (!mounted) return false;
|
|
571
|
+
contentTrackingActive = false;
|
|
429
572
|
renderFrame(lastFrame);
|
|
430
573
|
parkCursorAtInput();
|
|
431
574
|
return true;
|
|
@@ -433,17 +576,21 @@ export function redrawDockFrame() {
|
|
|
433
576
|
|
|
434
577
|
export function prepareInputPrompt({ context = '', tips = '', meta = '' } = {}) {
|
|
435
578
|
if (!mounted) return false;
|
|
579
|
+
contentTrackingActive = false;
|
|
436
580
|
setInputRowsTo(MIN_INPUT_ROWS);
|
|
437
581
|
clearInputRows();
|
|
438
|
-
renderFrame({ context, tips, meta, prefix: '', value: '' });
|
|
582
|
+
renderFrame({ context, tips, meta, prefix: '', value: '', overlayLines: null });
|
|
439
583
|
moveTo(inputRowStart(), INPUT_INDENT + 1);
|
|
440
584
|
return true;
|
|
441
585
|
}
|
|
442
586
|
|
|
443
587
|
export function clearInputPrompt() {
|
|
444
588
|
if (!mounted) return false;
|
|
445
|
-
|
|
589
|
+
contentTrackingActive = false;
|
|
446
590
|
lastFrame.value = '';
|
|
591
|
+
lastFrame.overlayLines = null;
|
|
592
|
+
setInputRowsTo(MIN_INPUT_ROWS);
|
|
593
|
+
clearInputRows();
|
|
447
594
|
renderFrame(lastFrame);
|
|
448
595
|
parkCursorAtInput();
|
|
449
596
|
return true;
|
|
@@ -451,14 +598,42 @@ export function clearInputPrompt() {
|
|
|
451
598
|
|
|
452
599
|
export function renderDockInput(prefix, value, { context = '', tips = '', meta = '', cursor = null } = {}) {
|
|
453
600
|
if (!mounted) return false;
|
|
601
|
+
contentTrackingActive = false;
|
|
454
602
|
setInputRowsTo(computeInputRowsForBuffer(prefix, value));
|
|
455
|
-
renderFrame({ context, tips, meta, prefix, value, cursor });
|
|
603
|
+
renderFrame({ context, tips, meta, prefix, value, cursor, overlayLines: null });
|
|
456
604
|
const layout = layoutInput(prefix, value);
|
|
457
605
|
drawInputLines(layout.lines);
|
|
458
606
|
focusDockInput(prefix, value, cursor);
|
|
459
607
|
return true;
|
|
460
608
|
}
|
|
461
609
|
|
|
610
|
+
export function renderDockOverlay({
|
|
611
|
+
context = '',
|
|
612
|
+
lines = [],
|
|
613
|
+
meta = '',
|
|
614
|
+
tips = '',
|
|
615
|
+
maxRows = DEFAULT_OVERLAY_MAX_ROWS,
|
|
616
|
+
} = {}) {
|
|
617
|
+
if (!mounted) return false;
|
|
618
|
+
contentTrackingActive = false;
|
|
619
|
+
const sourceLines = Array.isArray(lines) ? lines : String(lines || '').split('\n');
|
|
620
|
+
const wrapped = layoutOverlayLines(sourceLines);
|
|
621
|
+
const rowCap = resolveOverlayRowCap(maxRows);
|
|
622
|
+
setInputRowsTo(overlayRowsForWrapped(wrapped.length, rowCap), { maxRows: rowCap });
|
|
623
|
+
const tail = tailWithEllipsis(wrapped, inputRows);
|
|
624
|
+
renderFrame({
|
|
625
|
+
context,
|
|
626
|
+
meta,
|
|
627
|
+
tips,
|
|
628
|
+
prefix: '',
|
|
629
|
+
value: '',
|
|
630
|
+
cursor: null,
|
|
631
|
+
overlayLines: tail.visible,
|
|
632
|
+
});
|
|
633
|
+
moveTo(rows(), 1);
|
|
634
|
+
return true;
|
|
635
|
+
}
|
|
636
|
+
|
|
462
637
|
/**
|
|
463
638
|
* Move the terminal cursor to the position that corresponds to
|
|
464
639
|
* `prefix + value[0..cursorInValue]` within the (possibly wrapped and
|
|
@@ -470,6 +645,7 @@ export function renderDockInput(prefix, value, { context = '', tips = '', meta =
|
|
|
470
645
|
*/
|
|
471
646
|
export function focusDockInput(prefix, value = '', cursorInValue = null) {
|
|
472
647
|
if (!mounted) return false;
|
|
648
|
+
contentTrackingActive = false;
|
|
473
649
|
const layout = layoutInput(prefix, value);
|
|
474
650
|
const valueStr = String(value || '');
|
|
475
651
|
const rawCursor = cursorInValue == null
|
|
@@ -499,7 +675,16 @@ export function _internals() {
|
|
|
499
675
|
reservedRows: () => reservedRows,
|
|
500
676
|
layoutInput,
|
|
501
677
|
inputTextBudget,
|
|
678
|
+
topRuleLine,
|
|
679
|
+
bottomRuleLine,
|
|
680
|
+
padLine,
|
|
681
|
+
drawableColumns,
|
|
682
|
+
resetContentCursor,
|
|
683
|
+
contentCursor: () => ({ row: contentCursorRow, col: contentCursorCol, active: contentTrackingActive }),
|
|
684
|
+
overlayRowsForWrapped,
|
|
502
685
|
FIXED_ROWS,
|
|
686
|
+
MAX_INPUT_ROWS_CAP,
|
|
687
|
+
DEFAULT_OVERLAY_MAX_ROWS,
|
|
503
688
|
BRAND_LABEL,
|
|
504
689
|
};
|
|
505
690
|
}
|
|
@@ -45,6 +45,7 @@ export const COMMANDS = {
|
|
|
45
45
|
'/surgical': 'Verbosity: show everything (reasoning, expanded tools)',
|
|
46
46
|
'/compact': 'Compact conversation context',
|
|
47
47
|
'/agents': 'List available agents',
|
|
48
|
+
'/run': 'Run a sub-agent or synced workflow',
|
|
48
49
|
'/explore': 'Code explorer agent',
|
|
49
50
|
'/review': 'Code review agent',
|
|
50
51
|
'/architect': 'Feature architect agent',
|
|
@@ -137,11 +138,20 @@ export const HELP_GROUPS = [
|
|
|
137
138
|
['/agents create <name>', 'Create .bahulam/agents/<name>.yaml'],
|
|
138
139
|
['/agents edit <name>', 'Open local agent YAML'],
|
|
139
140
|
['/agents sync [name]', 'Sync all or one local agent to cloud'],
|
|
141
|
+
['/run <agent> [instruction]', 'Run a local or built-in agent'],
|
|
140
142
|
['/explore <instruction>', 'Explore code'],
|
|
141
143
|
['/review <instruction>', 'Review code'],
|
|
142
144
|
['/architect <instruction>', 'Design an approach'],
|
|
143
145
|
],
|
|
144
146
|
},
|
|
147
|
+
{
|
|
148
|
+
key: 'workflows',
|
|
149
|
+
title: 'Workflows',
|
|
150
|
+
summary: 'saved automations',
|
|
151
|
+
commands: [
|
|
152
|
+
['/run <workflow> [instruction]', 'Run a synced workflow if no agent matches'],
|
|
153
|
+
],
|
|
154
|
+
},
|
|
145
155
|
{
|
|
146
156
|
key: 'session',
|
|
147
157
|
title: 'Session',
|