pi-advisor-flow 0.2.3 → 0.2.6
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/README.md +45 -11
- package/package.json +33 -30
- package/src/commands.ts +141 -34
- package/src/config.ts +167 -8
- package/src/conversation.ts +28 -3
- package/src/git.ts +190 -0
- package/src/outcomes.ts +85 -0
- package/src/preferences.ts +50 -0
- package/src/session-state.ts +36 -1
- package/src/tools.ts +340 -30
- package/src/ui.ts +410 -114
- package/src/untracked.ts +98 -0
package/src/ui.ts
CHANGED
|
@@ -185,6 +185,7 @@ export interface ContextPreset {
|
|
|
185
185
|
}
|
|
186
186
|
|
|
187
187
|
export interface AdvisorSettings {
|
|
188
|
+
alwaysOn?: boolean;
|
|
188
189
|
autoLoopGate?: boolean;
|
|
189
190
|
blockOnBlocked?: boolean;
|
|
190
191
|
collapseResponses: boolean;
|
|
@@ -194,17 +195,86 @@ export interface AdvisorSettings {
|
|
|
194
195
|
effort?: string;
|
|
195
196
|
failureGate: boolean;
|
|
196
197
|
failureMode?: "block-session" | "block-tool" | "warn-and-continue";
|
|
198
|
+
gitContext?: "off" | "summary" | "full";
|
|
199
|
+
gitContextMaxChars?: number;
|
|
197
200
|
herdrIntegration?: boolean;
|
|
198
201
|
loopThreshold?: number;
|
|
199
202
|
maxCallsPerSession?: number;
|
|
203
|
+
outcomeLogging?: boolean;
|
|
200
204
|
planGate: boolean;
|
|
201
205
|
redactSecrets?: boolean;
|
|
202
206
|
sessionSummary?: boolean;
|
|
207
|
+
simpleMode?: boolean;
|
|
203
208
|
toolPolicies?: Record<string, "full" | "summary" | "exclude">;
|
|
204
209
|
toolResultMaxBytes?: number;
|
|
205
210
|
toolResultMaxLines?: number;
|
|
211
|
+
untrackedContent?: boolean;
|
|
206
212
|
}
|
|
207
213
|
|
|
214
|
+
type AdvisorSettingsRow =
|
|
215
|
+
| "simpleMode"
|
|
216
|
+
| "alwaysOn"
|
|
217
|
+
| "context"
|
|
218
|
+
| "effort"
|
|
219
|
+
| "planGate"
|
|
220
|
+
| "failureGate"
|
|
221
|
+
| "completionGate"
|
|
222
|
+
| "collapseResponses"
|
|
223
|
+
| "customRule"
|
|
224
|
+
| "blockOnBlocked"
|
|
225
|
+
| "autoLoopGate"
|
|
226
|
+
| "loopThreshold"
|
|
227
|
+
| "maxCallsPerSession"
|
|
228
|
+
| "sessionSummary"
|
|
229
|
+
| "failureMode"
|
|
230
|
+
| "herdrIntegration"
|
|
231
|
+
| "toolResultMaxLines"
|
|
232
|
+
| "toolResultMaxBytes"
|
|
233
|
+
| "redactSecrets"
|
|
234
|
+
| "gitContext"
|
|
235
|
+
| "gitContextMaxChars"
|
|
236
|
+
| "toolPolicies"
|
|
237
|
+
| "outcomeLogging"
|
|
238
|
+
| "untrackedContent"
|
|
239
|
+
| "save";
|
|
240
|
+
|
|
241
|
+
const ADVANCED_ROWS: AdvisorSettingsRow[] = [
|
|
242
|
+
"context",
|
|
243
|
+
"effort",
|
|
244
|
+
"planGate",
|
|
245
|
+
"failureGate",
|
|
246
|
+
"completionGate",
|
|
247
|
+
"collapseResponses",
|
|
248
|
+
"customRule",
|
|
249
|
+
"blockOnBlocked",
|
|
250
|
+
"autoLoopGate",
|
|
251
|
+
"loopThreshold",
|
|
252
|
+
"maxCallsPerSession",
|
|
253
|
+
"sessionSummary",
|
|
254
|
+
"failureMode",
|
|
255
|
+
"herdrIntegration",
|
|
256
|
+
"toolResultMaxLines",
|
|
257
|
+
"toolResultMaxBytes",
|
|
258
|
+
"redactSecrets",
|
|
259
|
+
"gitContext",
|
|
260
|
+
"gitContextMaxChars",
|
|
261
|
+
"toolPolicies",
|
|
262
|
+
"untrackedContent",
|
|
263
|
+
"outcomeLogging",
|
|
264
|
+
];
|
|
265
|
+
|
|
266
|
+
const SIMPLE_MODE_GRADIENT_INTERVAL_MS = 100;
|
|
267
|
+
// #763FCD and nearby lighter/darker purple steps; white is the moving shine.
|
|
268
|
+
const SIMPLE_MODE_GRADIENT_COLORS = [
|
|
269
|
+
[125, 79, 205],
|
|
270
|
+
[143, 96, 218],
|
|
271
|
+
[160, 114, 230],
|
|
272
|
+
[178, 135, 238],
|
|
273
|
+
[195, 157, 245],
|
|
274
|
+
[168, 120, 230],
|
|
275
|
+
[143, 89, 215],
|
|
276
|
+
] as const;
|
|
277
|
+
|
|
208
278
|
export class AdvisorSettingsSelector implements Component, Focusable {
|
|
209
279
|
private selectedRow: number;
|
|
210
280
|
private contextIndex: number;
|
|
@@ -215,6 +285,8 @@ export class AdvisorSettingsSelector implements Component, Focusable {
|
|
|
215
285
|
private editingCustom: boolean;
|
|
216
286
|
private editingPolicies: boolean;
|
|
217
287
|
private policiesError: string | undefined;
|
|
288
|
+
private simpleModeGradientStartedAt: number | undefined;
|
|
289
|
+
private simpleModeGradientTimer: ReturnType<typeof setInterval> | undefined;
|
|
218
290
|
private _focused = false;
|
|
219
291
|
private readonly options: AdvisorSettingsSelectorOptions;
|
|
220
292
|
|
|
@@ -233,6 +305,7 @@ export class AdvisorSettingsSelector implements Component, Focusable {
|
|
|
233
305
|
this.editingPolicies = false;
|
|
234
306
|
this.policiesError = undefined;
|
|
235
307
|
this.options = options;
|
|
308
|
+
// Retain every advanced value even when Simple mode hides its controls.
|
|
236
309
|
this.settings = { ...options.initial };
|
|
237
310
|
this.contextIndex = Math.max(
|
|
238
311
|
0,
|
|
@@ -246,6 +319,9 @@ export class AdvisorSettingsSelector implements Component, Focusable {
|
|
|
246
319
|
this.settings.effort || "Default (Model Default)"
|
|
247
320
|
)
|
|
248
321
|
);
|
|
322
|
+
if (this.settings.simpleMode) {
|
|
323
|
+
this.startSimpleModeGradient();
|
|
324
|
+
}
|
|
249
325
|
this.customInput.onSubmit = (value) => {
|
|
250
326
|
this.settings.customRule = value.trim() || undefined;
|
|
251
327
|
this.editingCustom = false;
|
|
@@ -289,6 +365,31 @@ export class AdvisorSettingsSelector implements Component, Focusable {
|
|
|
289
365
|
this.options.tui.requestRender();
|
|
290
366
|
}
|
|
291
367
|
|
|
368
|
+
/** Called by TUI teardown when it supports component disposal. */
|
|
369
|
+
dispose(): void {
|
|
370
|
+
this.stopSimpleModeGradient();
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
private visibleRows(): AdvisorSettingsRow[] {
|
|
374
|
+
return this.settings.simpleMode
|
|
375
|
+
? ["context", "simpleMode", "alwaysOn", "save"]
|
|
376
|
+
: [
|
|
377
|
+
"context",
|
|
378
|
+
"simpleMode",
|
|
379
|
+
"alwaysOn",
|
|
380
|
+
...ADVANCED_ROWS.slice(1),
|
|
381
|
+
"save",
|
|
382
|
+
];
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
private selectedRowId(): AdvisorSettingsRow {
|
|
386
|
+
return this.visibleRows()[this.selectedRow] ?? "simpleMode";
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
private focusRow(rowId: AdvisorSettingsRow): void {
|
|
390
|
+
this.selectedRow = Math.max(0, this.visibleRows().indexOf(rowId));
|
|
391
|
+
}
|
|
392
|
+
|
|
292
393
|
private currentContext(): ContextPreset {
|
|
293
394
|
const preset = this.options.presets.find(
|
|
294
395
|
(item) => item.value === this.settings.contextMaxChars
|
|
@@ -301,145 +402,294 @@ export class AdvisorSettingsSelector implements Component, Focusable {
|
|
|
301
402
|
}
|
|
302
403
|
);
|
|
303
404
|
}
|
|
405
|
+
|
|
304
406
|
private currentEffort() {
|
|
305
407
|
return this.settings.effort || "Default (Model Default)";
|
|
306
408
|
}
|
|
307
|
-
|
|
409
|
+
|
|
410
|
+
private row(label: string, value: string, rowId: AdvisorSettingsRow): string {
|
|
308
411
|
const { theme } = this.options;
|
|
309
|
-
const
|
|
412
|
+
const selected = rowId === this.selectedRowId();
|
|
413
|
+
const prefix = selected ? theme.fg("accent", "›") : " ";
|
|
310
414
|
const text = `${prefix} ${label.padEnd(28)} ${value}`;
|
|
311
|
-
|
|
415
|
+
if (
|
|
416
|
+
rowId === "simpleMode" &&
|
|
417
|
+
this.settings.simpleMode &&
|
|
418
|
+
this.simpleModeGradientStartedAt !== undefined
|
|
419
|
+
) {
|
|
420
|
+
return `${prefix} ${this.rainbowGradient(label)}${" ".repeat(Math.max(0, 28 - label.length))} ${value}`;
|
|
421
|
+
}
|
|
422
|
+
return selected
|
|
312
423
|
? theme.fg("accent", theme.bold(text))
|
|
313
424
|
: theme.fg("text", text);
|
|
314
425
|
}
|
|
315
426
|
|
|
316
|
-
private
|
|
317
|
-
|
|
318
|
-
|
|
427
|
+
private rainbowGradient(text: string): string {
|
|
428
|
+
const frame = Math.floor(
|
|
429
|
+
(Date.now() - (this.simpleModeGradientStartedAt ?? 0)) /
|
|
430
|
+
SIMPLE_MODE_GRADIENT_INTERVAL_MS
|
|
431
|
+
);
|
|
432
|
+
const shinePosition = frame % (text.length * 2);
|
|
433
|
+
return [...text]
|
|
434
|
+
.map((character, index) => {
|
|
435
|
+
const [baseRed, baseGreen, baseBlue] =
|
|
436
|
+
SIMPLE_MODE_GRADIENT_COLORS[
|
|
437
|
+
index % SIMPLE_MODE_GRADIENT_COLORS.length
|
|
438
|
+
];
|
|
439
|
+
const distance = Math.abs(index - shinePosition);
|
|
440
|
+
let brightness = 0;
|
|
441
|
+
if (distance === 0) {
|
|
442
|
+
brightness = 0.7;
|
|
443
|
+
} else if (distance === 1) {
|
|
444
|
+
brightness = 0.35;
|
|
445
|
+
}
|
|
446
|
+
const red = Math.round(baseRed + (255 - baseRed) * brightness);
|
|
447
|
+
const green = Math.round(baseGreen + (255 - baseGreen) * brightness);
|
|
448
|
+
const blue = Math.round(baseBlue + (255 - baseBlue) * brightness);
|
|
449
|
+
return `\x1b[38;2;${red};${green};${blue}m${character}`;
|
|
450
|
+
})
|
|
451
|
+
.join("")
|
|
452
|
+
.concat("\x1b[0m");
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
private startSimpleModeGradient(): void {
|
|
456
|
+
this.stopSimpleModeGradient();
|
|
457
|
+
this.simpleModeGradientStartedAt = Date.now();
|
|
458
|
+
this.simpleModeGradientTimer = setInterval(() => {
|
|
459
|
+
this.options.tui.requestRender();
|
|
460
|
+
}, SIMPLE_MODE_GRADIENT_INTERVAL_MS);
|
|
461
|
+
this.simpleModeGradientTimer.unref?.();
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
private stopSimpleModeGradient(): void {
|
|
465
|
+
if (this.simpleModeGradientTimer) {
|
|
466
|
+
clearInterval(this.simpleModeGradientTimer);
|
|
467
|
+
this.simpleModeGradientTimer = undefined;
|
|
319
468
|
}
|
|
320
|
-
|
|
321
|
-
|
|
469
|
+
this.simpleModeGradientStartedAt = undefined;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
private save(): void {
|
|
473
|
+
this.stopSimpleModeGradient();
|
|
474
|
+
this.options.onSave({
|
|
475
|
+
...this.settings,
|
|
476
|
+
contextMaxChars: this.currentContext().value,
|
|
477
|
+
effort: this.currentEffort(),
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
private cancel(): void {
|
|
482
|
+
this.stopSimpleModeGradient();
|
|
483
|
+
this.options.onCancel();
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/** Disclosure and output-limit rows, split out to keep each builder simple. */
|
|
487
|
+
private disclosureRows(): string[] {
|
|
488
|
+
const onOff = (value: boolean) => (value ? "On" : "Off");
|
|
489
|
+
return [
|
|
490
|
+
this.row(
|
|
491
|
+
"Tool result lines",
|
|
492
|
+
String(this.settings.toolResultMaxLines ?? 2000),
|
|
493
|
+
"toolResultMaxLines"
|
|
494
|
+
),
|
|
495
|
+
this.row(
|
|
496
|
+
"Tool result bytes",
|
|
497
|
+
String(this.settings.toolResultMaxBytes ?? 50 * 1024),
|
|
498
|
+
"toolResultMaxBytes"
|
|
499
|
+
),
|
|
500
|
+
this.row(
|
|
501
|
+
"Redact common secrets",
|
|
502
|
+
onOff(this.settings.redactSecrets ?? false),
|
|
503
|
+
"redactSecrets"
|
|
504
|
+
),
|
|
505
|
+
this.row(
|
|
506
|
+
"Repository context",
|
|
507
|
+
this.settings.gitContext ?? "summary",
|
|
508
|
+
"gitContext"
|
|
509
|
+
),
|
|
510
|
+
this.row(
|
|
511
|
+
"Repository context chars",
|
|
512
|
+
String(this.settings.gitContextMaxChars ?? 20_000),
|
|
513
|
+
"gitContextMaxChars"
|
|
514
|
+
),
|
|
515
|
+
this.row(
|
|
516
|
+
"Tool disclosure policies",
|
|
517
|
+
Object.keys(this.settings.toolPolicies ?? {}).length
|
|
518
|
+
? "Exact names configured"
|
|
519
|
+
: "All tools: full",
|
|
520
|
+
"toolPolicies"
|
|
521
|
+
),
|
|
522
|
+
this.row(
|
|
523
|
+
"Untracked file content",
|
|
524
|
+
onOff(this.settings.untrackedContent ?? false),
|
|
525
|
+
"untrackedContent"
|
|
526
|
+
),
|
|
527
|
+
this.row(
|
|
528
|
+
"Outcome logging (global)",
|
|
529
|
+
onOff(this.settings.outcomeLogging ?? false),
|
|
530
|
+
"outcomeLogging"
|
|
531
|
+
),
|
|
322
532
|
];
|
|
323
|
-
if (this.policiesError) {
|
|
324
|
-
rows.push(` ${this.options.theme.fg("error", this.policiesError)}`);
|
|
325
|
-
}
|
|
326
|
-
return rows;
|
|
327
533
|
}
|
|
328
534
|
|
|
329
|
-
|
|
330
|
-
const { theme, presets } = this.options;
|
|
331
|
-
const trackWidth = Math.max(24, Math.min(60, width - 4));
|
|
332
|
-
const positions = presets.map((_, index) =>
|
|
333
|
-
Math.round((index * (trackWidth - 1)) / Math.max(1, presets.length - 1))
|
|
334
|
-
);
|
|
335
|
-
const track = Array.from({ length: trackWidth }, () => "─");
|
|
336
|
-
track[positions[this.contextIndex]] = "▲";
|
|
337
|
-
const labels = Array.from({ length: trackWidth }, () => " ");
|
|
338
|
-
for (let index = 0; index < presets.length; index += 1) {
|
|
339
|
-
const { label } = presets[index];
|
|
340
|
-
const start = Math.max(
|
|
341
|
-
0,
|
|
342
|
-
Math.min(
|
|
343
|
-
trackWidth - label.length,
|
|
344
|
-
positions[index] - Math.floor(label.length / 2)
|
|
345
|
-
)
|
|
346
|
-
);
|
|
347
|
-
for (let char = 0; char < label.length; char += 1) {
|
|
348
|
-
labels[start + char] = label[char];
|
|
349
|
-
}
|
|
350
|
-
}
|
|
351
|
-
const heading = `Recent history${" ".repeat(Math.max(1, trackWidth - "Recent history".length - "Full branch".length))}Full branch`;
|
|
535
|
+
private advancedRows(width: number): string[] {
|
|
352
536
|
const onOff = (value: boolean) => (value ? "On" : "Off");
|
|
353
537
|
const rows = [
|
|
354
|
-
this.row("
|
|
355
|
-
this.row("
|
|
356
|
-
this.row("
|
|
357
|
-
this.row(
|
|
358
|
-
|
|
538
|
+
this.row("Advisor reasoning", this.currentEffort(), "effort"),
|
|
539
|
+
this.row("Plan gate", onOff(this.settings.planGate), "planGate"),
|
|
540
|
+
this.row("Failure gate", onOff(this.settings.failureGate), "failureGate"),
|
|
541
|
+
this.row(
|
|
542
|
+
"Completion gate",
|
|
543
|
+
onOff(this.settings.completionGate),
|
|
544
|
+
"completionGate"
|
|
545
|
+
),
|
|
359
546
|
this.row(
|
|
360
547
|
"Collapse long responses",
|
|
361
548
|
onOff(this.settings.collapseResponses),
|
|
362
|
-
|
|
549
|
+
"collapseResponses"
|
|
550
|
+
),
|
|
551
|
+
this.row(
|
|
552
|
+
"Custom invocation",
|
|
553
|
+
this.settings.customRule || "None",
|
|
554
|
+
"customRule"
|
|
363
555
|
),
|
|
364
|
-
this.row("Custom invocation", this.settings.customRule || "None", 6),
|
|
365
556
|
this.row(
|
|
366
557
|
"Block on critical advice",
|
|
367
558
|
onOff(this.settings.blockOnBlocked ?? true),
|
|
368
|
-
|
|
559
|
+
"blockOnBlocked"
|
|
369
560
|
),
|
|
370
561
|
this.row(
|
|
371
562
|
"Automatic loop gate",
|
|
372
563
|
onOff(this.settings.autoLoopGate ?? true),
|
|
373
|
-
|
|
564
|
+
"autoLoopGate"
|
|
374
565
|
),
|
|
375
566
|
this.row(
|
|
376
567
|
"Loop threshold",
|
|
377
568
|
`After ${this.settings.loopThreshold ?? 3} repeats`,
|
|
378
|
-
|
|
569
|
+
"loopThreshold"
|
|
379
570
|
),
|
|
380
571
|
this.row(
|
|
381
572
|
"Max Advisor calls/session",
|
|
382
573
|
this.settings.maxCallsPerSession === undefined
|
|
383
574
|
? "∞"
|
|
384
575
|
: String(this.settings.maxCallsPerSession),
|
|
385
|
-
|
|
576
|
+
"maxCallsPerSession"
|
|
386
577
|
),
|
|
387
578
|
this.row(
|
|
388
579
|
"Session Advisor Summary",
|
|
389
|
-
onOff(this.settings.sessionSummary ??
|
|
390
|
-
|
|
580
|
+
onOff(this.settings.sessionSummary ?? false),
|
|
581
|
+
"sessionSummary"
|
|
391
582
|
),
|
|
392
583
|
this.row(
|
|
393
584
|
"Gate failure mode",
|
|
394
585
|
this.settings.failureMode ?? "block-session",
|
|
395
|
-
|
|
586
|
+
"failureMode"
|
|
396
587
|
),
|
|
397
588
|
this.row(
|
|
398
589
|
"Herdr integration",
|
|
399
590
|
onOff(this.settings.herdrIntegration ?? true),
|
|
400
|
-
|
|
401
|
-
),
|
|
402
|
-
this.row(
|
|
403
|
-
"Tool result lines",
|
|
404
|
-
String(this.settings.toolResultMaxLines ?? 2000),
|
|
405
|
-
14
|
|
406
|
-
),
|
|
407
|
-
this.row(
|
|
408
|
-
"Tool result bytes",
|
|
409
|
-
String(this.settings.toolResultMaxBytes ?? 50 * 1024),
|
|
410
|
-
15
|
|
411
|
-
),
|
|
412
|
-
this.row(
|
|
413
|
-
"Redact common secrets",
|
|
414
|
-
onOff(this.settings.redactSecrets ?? false),
|
|
415
|
-
16
|
|
416
|
-
),
|
|
417
|
-
this.row(
|
|
418
|
-
"Tool disclosure policies",
|
|
419
|
-
Object.keys(this.settings.toolPolicies ?? {}).length
|
|
420
|
-
? "Exact names configured"
|
|
421
|
-
: "All tools: full",
|
|
422
|
-
17
|
|
591
|
+
"herdrIntegration"
|
|
423
592
|
),
|
|
593
|
+
...this.disclosureRows(),
|
|
424
594
|
];
|
|
425
595
|
if (this.editingCustom) {
|
|
426
596
|
rows.push(
|
|
427
597
|
` ${this.customInput.render(Math.max(10, width - 6))[0] || ""}`
|
|
428
598
|
);
|
|
429
599
|
}
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
600
|
+
if (this.editingPolicies) {
|
|
601
|
+
rows.push(
|
|
602
|
+
` ${this.policiesInput.render(Math.max(10, width - 6))[0] || ""}`
|
|
603
|
+
);
|
|
604
|
+
if (this.policiesError) {
|
|
605
|
+
rows.push(` ${this.options.theme.fg("error", this.policiesError)}`);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
rows.push(this.row("Save changes", "", "save"));
|
|
609
|
+
return rows;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
render(width: number): string[] {
|
|
613
|
+
const { theme, presets } = this.options;
|
|
614
|
+
const simpleMode = this.settings.simpleMode ?? false;
|
|
615
|
+
const modeRows = [
|
|
616
|
+
this.row("Simple mode", simpleMode ? "On" : "Off", "simpleMode"),
|
|
617
|
+
this.row("Always on", this.settings.alwaysOn ? "On" : "Off", "alwaysOn"),
|
|
618
|
+
];
|
|
619
|
+
const lines = [theme.fg("accent", theme.bold(" Advisor settings")), ""];
|
|
620
|
+
if (simpleMode) {
|
|
621
|
+
const trackWidth = Math.max(24, Math.min(60, width - 4));
|
|
622
|
+
const positions = presets.map((_, index) =>
|
|
623
|
+
Math.round((index * (trackWidth - 1)) / Math.max(1, presets.length - 1))
|
|
624
|
+
);
|
|
625
|
+
const track = Array.from({ length: trackWidth }, () => "─");
|
|
626
|
+
track[positions[this.contextIndex]] = "▲";
|
|
627
|
+
const labels = Array.from({ length: trackWidth }, () => " ");
|
|
628
|
+
for (let index = 0; index < presets.length; index += 1) {
|
|
629
|
+
const { label } = presets[index];
|
|
630
|
+
const start = Math.max(
|
|
631
|
+
0,
|
|
632
|
+
Math.min(
|
|
633
|
+
trackWidth - label.length,
|
|
634
|
+
positions[index] - Math.floor(label.length / 2)
|
|
635
|
+
)
|
|
636
|
+
);
|
|
637
|
+
for (let char = 0; char < label.length; char += 1) {
|
|
638
|
+
labels[start + char] = label[char];
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
lines.push(
|
|
642
|
+
` ${this.row("Context window", this.currentContext().label, "context")}`
|
|
643
|
+
);
|
|
644
|
+
lines.push(` ${theme.fg("muted", "Recent history")}`);
|
|
645
|
+
lines.push(` ${theme.fg("muted", track.join(""))}`);
|
|
646
|
+
lines.push(` ${theme.fg("text", labels.join(""))}`);
|
|
647
|
+
lines.push("");
|
|
648
|
+
lines.push(...modeRows.map((line) => ` ${line}`));
|
|
649
|
+
lines.push(` ${this.row("Save changes", "", "save")}`);
|
|
650
|
+
lines.push("");
|
|
651
|
+
lines.push(
|
|
652
|
+
` ${theme.fg("muted", "↑/↓ select · ←/→ adjust · Enter saves · Esc cancels")}`
|
|
653
|
+
);
|
|
654
|
+
return lines.map((line) => truncateToWidth(line, width));
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
const trackWidth = Math.max(24, Math.min(60, width - 4));
|
|
658
|
+
const positions = presets.map((_, index) =>
|
|
659
|
+
Math.round((index * (trackWidth - 1)) / Math.max(1, presets.length - 1))
|
|
660
|
+
);
|
|
661
|
+
const track = Array.from({ length: trackWidth }, () => "─");
|
|
662
|
+
track[positions[this.contextIndex]] = "▲";
|
|
663
|
+
const labels = Array.from({ length: trackWidth }, () => " ");
|
|
664
|
+
for (let index = 0; index < presets.length; index += 1) {
|
|
665
|
+
const { label } = presets[index];
|
|
666
|
+
const start = Math.max(
|
|
667
|
+
0,
|
|
668
|
+
Math.min(
|
|
669
|
+
trackWidth - label.length,
|
|
670
|
+
positions[index] - Math.floor(label.length / 2)
|
|
671
|
+
)
|
|
672
|
+
);
|
|
673
|
+
for (let char = 0; char < label.length; char += 1) {
|
|
674
|
+
labels[start + char] = label[char];
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
const heading = `Recent history${" ".repeat(Math.max(1, trackWidth - "Recent history".length - "Full branch".length))}Full branch`;
|
|
678
|
+
lines.push(
|
|
679
|
+
` ${this.row("Context window", this.currentContext().label, "context")}`
|
|
680
|
+
);
|
|
681
|
+
lines.push(` ${theme.fg("muted", heading)}`);
|
|
682
|
+
lines.push(` ${theme.fg("muted", track.join(""))}`);
|
|
683
|
+
lines.push(` ${theme.fg("text", labels.join(""))}`);
|
|
684
|
+
lines.push("");
|
|
685
|
+
lines.push(
|
|
686
|
+
...[...modeRows, ...this.advancedRows(width)].map((line) => ` ${line}`)
|
|
687
|
+
);
|
|
688
|
+
lines.push("");
|
|
689
|
+
lines.push(
|
|
690
|
+
` ${theme.fg("muted", "↑/↓ select · ←/→ adjust · Enter edits or saves · Esc cancels")}`
|
|
691
|
+
);
|
|
692
|
+
return lines.map((line) => truncateToWidth(line, width));
|
|
443
693
|
}
|
|
444
694
|
|
|
445
695
|
handleInput(keyData: string): void {
|
|
@@ -455,20 +705,24 @@ export class AdvisorSettingsSelector implements Component, Focusable {
|
|
|
455
705
|
if (matchesKey(keyData, Key.up)) {
|
|
456
706
|
this.selectedRow = Math.max(0, this.selectedRow - 1);
|
|
457
707
|
} else if (matchesKey(keyData, Key.down)) {
|
|
458
|
-
this.selectedRow = Math.min(
|
|
708
|
+
this.selectedRow = Math.min(
|
|
709
|
+
this.visibleRows().length - 1,
|
|
710
|
+
this.selectedRow + 1
|
|
711
|
+
);
|
|
459
712
|
} else if (matchesKey(keyData, Key.left)) {
|
|
460
713
|
this.adjust(-1);
|
|
461
714
|
} else if (matchesKey(keyData, Key.right)) {
|
|
462
715
|
this.adjust(1);
|
|
463
716
|
} else if (matchesKey(keyData, Key.enter)) {
|
|
464
|
-
|
|
717
|
+
const row = this.selectedRowId();
|
|
718
|
+
if (row === "customRule") {
|
|
465
719
|
this.editingCustom = true;
|
|
466
720
|
this.customInput.setValue(this.settings.customRule || "");
|
|
467
721
|
this.customInput.focused = this.focused;
|
|
468
722
|
tui.requestRender();
|
|
469
723
|
return;
|
|
470
724
|
}
|
|
471
|
-
if (
|
|
725
|
+
if (row === "toolPolicies") {
|
|
472
726
|
this.editingPolicies = true;
|
|
473
727
|
this.policiesError = undefined;
|
|
474
728
|
this.policiesInput.setValue(
|
|
@@ -478,17 +732,13 @@ export class AdvisorSettingsSelector implements Component, Focusable {
|
|
|
478
732
|
tui.requestRender();
|
|
479
733
|
return;
|
|
480
734
|
}
|
|
481
|
-
if (
|
|
482
|
-
this.
|
|
483
|
-
...this.settings,
|
|
484
|
-
contextMaxChars: this.currentContext().value,
|
|
485
|
-
effort: this.currentEffort(),
|
|
486
|
-
});
|
|
735
|
+
if (row === "save") {
|
|
736
|
+
this.save();
|
|
487
737
|
return;
|
|
488
738
|
}
|
|
489
739
|
this.adjust(1);
|
|
490
740
|
} else if (matchesKey(keyData, Key.escape)) {
|
|
491
|
-
this.
|
|
741
|
+
this.cancel();
|
|
492
742
|
return;
|
|
493
743
|
} else {
|
|
494
744
|
return;
|
|
@@ -496,9 +746,23 @@ export class AdvisorSettingsSelector implements Component, Focusable {
|
|
|
496
746
|
tui.requestRender();
|
|
497
747
|
}
|
|
498
748
|
|
|
499
|
-
private adjust(direction: number) {
|
|
500
|
-
switch (this.
|
|
501
|
-
case
|
|
749
|
+
private adjust(direction: number): void {
|
|
750
|
+
switch (this.selectedRowId()) {
|
|
751
|
+
case "simpleMode":
|
|
752
|
+
this.settings.simpleMode = !(this.settings.simpleMode ?? false);
|
|
753
|
+
if (this.settings.simpleMode) {
|
|
754
|
+
this.startSimpleModeGradient();
|
|
755
|
+
} else {
|
|
756
|
+
this.stopSimpleModeGradient();
|
|
757
|
+
}
|
|
758
|
+
// The visible row set changes with the mode, so keep the cursor on the
|
|
759
|
+
// row the user is actually operating rather than on its old index.
|
|
760
|
+
this.focusRow("simpleMode");
|
|
761
|
+
break;
|
|
762
|
+
case "alwaysOn":
|
|
763
|
+
this.settings.alwaysOn = !(this.settings.alwaysOn ?? false);
|
|
764
|
+
break;
|
|
765
|
+
case "context":
|
|
502
766
|
this.contextIndex = Math.max(
|
|
503
767
|
0,
|
|
504
768
|
Math.min(
|
|
@@ -509,7 +773,7 @@ export class AdvisorSettingsSelector implements Component, Focusable {
|
|
|
509
773
|
this.settings.contextMaxChars =
|
|
510
774
|
this.options.presets[this.contextIndex].value;
|
|
511
775
|
break;
|
|
512
|
-
case
|
|
776
|
+
case "effort":
|
|
513
777
|
this.effortIndex = Math.max(
|
|
514
778
|
0,
|
|
515
779
|
Math.min(
|
|
@@ -519,31 +783,31 @@ export class AdvisorSettingsSelector implements Component, Focusable {
|
|
|
519
783
|
);
|
|
520
784
|
this.settings.effort = this.options.effortLevels[this.effortIndex];
|
|
521
785
|
break;
|
|
522
|
-
case
|
|
786
|
+
case "planGate":
|
|
523
787
|
this.settings.planGate = !this.settings.planGate;
|
|
524
788
|
break;
|
|
525
|
-
case
|
|
789
|
+
case "failureGate":
|
|
526
790
|
this.settings.failureGate = !this.settings.failureGate;
|
|
527
791
|
break;
|
|
528
|
-
case
|
|
792
|
+
case "completionGate":
|
|
529
793
|
this.settings.completionGate = !this.settings.completionGate;
|
|
530
794
|
break;
|
|
531
|
-
case
|
|
795
|
+
case "collapseResponses":
|
|
532
796
|
this.settings.collapseResponses = !this.settings.collapseResponses;
|
|
533
797
|
break;
|
|
534
|
-
case
|
|
798
|
+
case "blockOnBlocked":
|
|
535
799
|
this.settings.blockOnBlocked = !(this.settings.blockOnBlocked ?? true);
|
|
536
800
|
break;
|
|
537
|
-
case
|
|
801
|
+
case "autoLoopGate":
|
|
538
802
|
this.settings.autoLoopGate = !(this.settings.autoLoopGate ?? true);
|
|
539
803
|
break;
|
|
540
|
-
case
|
|
804
|
+
case "loopThreshold":
|
|
541
805
|
this.settings.loopThreshold = Math.max(
|
|
542
806
|
2,
|
|
543
807
|
(this.settings.loopThreshold ?? 3) + direction
|
|
544
808
|
);
|
|
545
809
|
break;
|
|
546
|
-
case
|
|
810
|
+
case "maxCallsPerSession": {
|
|
547
811
|
const values = [undefined, 0, 1, 2, 3, 5, 10, 25, 50];
|
|
548
812
|
const index = Math.max(
|
|
549
813
|
0,
|
|
@@ -553,10 +817,10 @@ export class AdvisorSettingsSelector implements Component, Focusable {
|
|
|
553
817
|
values[Math.max(0, Math.min(values.length - 1, index + direction))];
|
|
554
818
|
break;
|
|
555
819
|
}
|
|
556
|
-
case
|
|
557
|
-
this.settings.sessionSummary = !(this.settings.sessionSummary ??
|
|
820
|
+
case "sessionSummary":
|
|
821
|
+
this.settings.sessionSummary = !(this.settings.sessionSummary ?? false);
|
|
558
822
|
break;
|
|
559
|
-
case
|
|
823
|
+
case "failureMode": {
|
|
560
824
|
const modes: AdvisorSettings["failureMode"][] = [
|
|
561
825
|
"block-session",
|
|
562
826
|
"block-tool",
|
|
@@ -570,15 +834,47 @@ export class AdvisorSettingsSelector implements Component, Focusable {
|
|
|
570
834
|
modes[Math.max(0, Math.min(modes.length - 1, index + direction))];
|
|
571
835
|
break;
|
|
572
836
|
}
|
|
573
|
-
case
|
|
837
|
+
case "herdrIntegration":
|
|
574
838
|
this.settings.herdrIntegration = !(
|
|
575
839
|
this.settings.herdrIntegration ?? true
|
|
576
840
|
);
|
|
577
841
|
break;
|
|
578
|
-
case
|
|
842
|
+
case "redactSecrets":
|
|
579
843
|
this.settings.redactSecrets = !(this.settings.redactSecrets ?? false);
|
|
580
844
|
break;
|
|
581
|
-
case
|
|
845
|
+
case "untrackedContent":
|
|
846
|
+
this.settings.untrackedContent = !(
|
|
847
|
+
this.settings.untrackedContent ?? false
|
|
848
|
+
);
|
|
849
|
+
break;
|
|
850
|
+
case "outcomeLogging":
|
|
851
|
+
this.settings.outcomeLogging = !(this.settings.outcomeLogging ?? false);
|
|
852
|
+
break;
|
|
853
|
+
case "gitContext": {
|
|
854
|
+
const levels: AdvisorSettings["gitContext"][] = [
|
|
855
|
+
"off",
|
|
856
|
+
"summary",
|
|
857
|
+
"full",
|
|
858
|
+
];
|
|
859
|
+
const index = Math.max(
|
|
860
|
+
0,
|
|
861
|
+
levels.indexOf(this.settings.gitContext ?? "summary")
|
|
862
|
+
);
|
|
863
|
+
this.settings.gitContext =
|
|
864
|
+
levels[Math.max(0, Math.min(levels.length - 1, index + direction))];
|
|
865
|
+
break;
|
|
866
|
+
}
|
|
867
|
+
case "gitContextMaxChars": {
|
|
868
|
+
const values = [0, 5000, 10_000, 20_000, 50_000, 100_000];
|
|
869
|
+
const index = Math.max(
|
|
870
|
+
0,
|
|
871
|
+
values.indexOf(this.settings.gitContextMaxChars ?? 20_000)
|
|
872
|
+
);
|
|
873
|
+
this.settings.gitContextMaxChars =
|
|
874
|
+
values[Math.max(0, Math.min(values.length - 1, index + direction))];
|
|
875
|
+
break;
|
|
876
|
+
}
|
|
877
|
+
case "toolResultMaxLines": {
|
|
582
878
|
const values = [0, 500, 1000, 2000, 5000, 10_000];
|
|
583
879
|
const index = Math.max(
|
|
584
880
|
0,
|
|
@@ -588,7 +884,7 @@ export class AdvisorSettingsSelector implements Component, Focusable {
|
|
|
588
884
|
values[Math.max(0, Math.min(values.length - 1, index + direction))];
|
|
589
885
|
break;
|
|
590
886
|
}
|
|
591
|
-
case
|
|
887
|
+
case "toolResultMaxBytes": {
|
|
592
888
|
const values = [0, 10 * 1024, 50 * 1024, 100 * 1024, 500 * 1024];
|
|
593
889
|
const index = Math.max(
|
|
594
890
|
0,
|