mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-10-19 07:23:59 +00:00
feat(coding-agent): add header-only quietStartup mode
quietStartup: "header" keeps the startup header with version and key hints but hides the model scope line and loaded-resource listing.
This commit is contained in:
parent
409e808f58
commit
f29ea3deb2
8 changed files with 64 additions and 18 deletions
|
|
@ -5,6 +5,7 @@
|
|||
### Added
|
||||
|
||||
- Added an `oauth.authServerMetadataUrl` setting for MCP servers that advertise a wrong OAuth authorization server or none. Pi uses the configured metadata document instead of discovery ([#10172](https://github.com/earendil-works/pi/issues/10172)).
|
||||
- Added `quietStartup: "header"`, which keeps the startup header with version and key hints but hides the model scope line and loaded-resource listing.
|
||||
|
||||
### Changed
|
||||
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ See [Compaction Reference](compaction.md) for trigger, summarization, and valida
|
|||
| Setting | Type | Default | Description |
|
||||
|---|---|---|---|
|
||||
| `theme` | string | `"system"` | Built-in or custom theme name. `system` derives colors from the terminal theme. |
|
||||
| `quietStartup` | boolean | `false` | Hide the startup header. |
|
||||
| `quietStartup` | boolean \| `"header"` | `false` | `true` hides the startup header and loaded-resource listing. `"header"` keeps the header (version and key hints) but hides the model scope line and loaded-resource listing. |
|
||||
| `tuiMode` | `"regular" \| "fullscreen"` | `"regular"` | Interactive terminal UI mode. |
|
||||
| `fullscreenExitOutput` | `"transcript" \| "resume-hint"` | `"transcript"` | Output printed when fullscreen mode exits. |
|
||||
| `fullscreenScrollbar` | `"auto" \| "always" \| "hidden"` | `"auto"` | Fullscreen transcript scrollbar behavior. |
|
||||
|
|
|
|||
|
|
@ -108,6 +108,8 @@ export interface CodemodeSettings {
|
|||
}
|
||||
|
||||
export type DefaultProjectTrust = "ask" | "always" | "never";
|
||||
/** true hides all startup output, "header" keeps only the startup header. */
|
||||
export type QuietStartup = boolean | "header";
|
||||
|
||||
export type TransportSetting = Transport;
|
||||
|
||||
|
|
@ -145,7 +147,7 @@ export interface Settings {
|
|||
showCacheMissNotices?: boolean; // default: false - show cache cost and provider recovery notices
|
||||
externalEditor?: string; // Command for Ctrl+G external editor; takes precedence over VISUAL/EDITOR
|
||||
shellPath?: string; // Custom shell path (e.g., for Cygwin users on Windows); supports leading ~ expansion
|
||||
quietStartup?: boolean;
|
||||
quietStartup?: QuietStartup; // default: false
|
||||
defaultProjectTrust?: DefaultProjectTrust; // default: "ask"; global setting only
|
||||
shellCommandPrefix?: string; // Prefix prepended to every bash command (e.g., "shopt -s expand_aliases" for alias support)
|
||||
npmCommand?: string[]; // Command used for npm package lookup/install operations, argv-style (e.g., ["mise", "exec", "node@20", "--", "npm"])
|
||||
|
|
@ -1084,11 +1086,12 @@ export class SettingsManager {
|
|||
this.save();
|
||||
}
|
||||
|
||||
getQuietStartup(): boolean {
|
||||
return this.settings.quietStartup ?? false;
|
||||
getQuietStartup(): QuietStartup {
|
||||
const value = this.settings.quietStartup;
|
||||
return value === true || value === "header" ? value : false;
|
||||
}
|
||||
|
||||
setQuietStartup(quiet: boolean): void {
|
||||
setQuietStartup(quiet: QuietStartup): void {
|
||||
this.globalSettings.quietStartup = quiet;
|
||||
this.markModified("quietStartup");
|
||||
this.save();
|
||||
|
|
|
|||
|
|
@ -310,6 +310,7 @@ export {
|
|||
type FullscreenExitOutput,
|
||||
type ImageSettings,
|
||||
type PackageSource,
|
||||
type QuietStartup,
|
||||
type RetrySettings,
|
||||
SettingsManager,
|
||||
type SettingsManagerCreateOptions,
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import {
|
|||
type DefaultProjectTrust,
|
||||
type FullscreenExitOutput,
|
||||
type MermaidRenderingMode,
|
||||
type QuietStartup,
|
||||
type TuiMode,
|
||||
type WarningSettings,
|
||||
} from "../../../core/settings-manager.ts";
|
||||
|
|
@ -87,7 +88,7 @@ export interface SettingsConfig {
|
|||
editorPaddingX: number;
|
||||
outputPad: 0 | 1;
|
||||
autocompleteMaxVisible: number;
|
||||
quietStartup: boolean;
|
||||
quietStartup: QuietStartup;
|
||||
defaultProjectTrust: DefaultProjectTrust;
|
||||
clearOnShrink: boolean;
|
||||
showTerminalProgress: boolean;
|
||||
|
|
@ -126,7 +127,7 @@ export interface SettingsCallbacks {
|
|||
onEditorPaddingXChange: (padding: number) => void;
|
||||
onOutputPadChange: (padding: 0 | 1) => void;
|
||||
onAutocompleteMaxVisibleChange: (maxVisible: number) => void;
|
||||
onQuietStartupChange: (enabled: boolean) => void;
|
||||
onQuietStartupChange: (quiet: QuietStartup) => void;
|
||||
onDefaultProjectTrustChange: (defaultProjectTrust: DefaultProjectTrust) => void;
|
||||
onClearOnShrinkChange: (enabled: boolean) => void;
|
||||
onShowTerminalProgressChange: (enabled: boolean) => void;
|
||||
|
|
@ -553,9 +554,9 @@ export class SettingsSelectorComponent extends Container {
|
|||
{
|
||||
id: "quiet-startup",
|
||||
label: "Quiet startup",
|
||||
description: "Disable verbose printing at startup",
|
||||
currentValue: config.quietStartup ? "true" : "false",
|
||||
values: ["true", "false"],
|
||||
description: "Disable verbose printing at startup (header: keep only the startup header)",
|
||||
currentValue: String(config.quietStartup),
|
||||
values: ["true", "header", "false"],
|
||||
},
|
||||
{
|
||||
id: "install-telemetry",
|
||||
|
|
@ -920,7 +921,7 @@ export class SettingsSelectorComponent extends Container {
|
|||
callbacks.onCollapseChangelogChange(newValue === "true");
|
||||
break;
|
||||
case "quiet-startup":
|
||||
callbacks.onQuietStartupChange(newValue === "true");
|
||||
callbacks.onQuietStartupChange(newValue === "header" ? "header" : newValue === "true");
|
||||
break;
|
||||
case "install-telemetry":
|
||||
callbacks.onEnableInstallTelemetryChange(newValue === "true");
|
||||
|
|
|
|||
|
|
@ -926,7 +926,7 @@ export class InteractiveMode {
|
|||
// Load changelog (only show new entries, skip for resumed sessions)
|
||||
this.changelogMarkdown = this.getChangelogForDisplay();
|
||||
|
||||
if (this.session.scopedModels.length > 0 && (this.options.verbose || !this.settingsManager.getQuietStartup())) {
|
||||
if (this.session.scopedModels.length > 0 && this.shouldShowStartupDetails()) {
|
||||
const modelList = this.session.scopedModels
|
||||
.map((sm) => {
|
||||
const thinkingStr = sm.thinkingLevel ? `:${sm.thinkingLevel}` : "";
|
||||
|
|
@ -982,7 +982,8 @@ export class InteractiveMode {
|
|||
await this.themeController.waitForTerminalColors();
|
||||
|
||||
// Add header with keybindings from config (unless silenced)
|
||||
if (this.options.verbose || !this.settingsManager.getQuietStartup()) {
|
||||
if (this.shouldShowStartupHeader()) {
|
||||
const showDetails = this.shouldShowStartupDetails();
|
||||
// Built on demand so the header follows theme changes. The logo's first line carries the version,
|
||||
// its second line the first line of key hints.
|
||||
const withLogo = (hints: string) => {
|
||||
|
|
@ -1027,7 +1028,10 @@ export class InteractiveMode {
|
|||
hint("app.tools.expand", "more"),
|
||||
].join(theme.fg("muted", " · "));
|
||||
const compactOnboarding = () =>
|
||||
theme.fg("dim", `Press ${keyText("app.tools.expand")} to show full startup help and loaded resources.`);
|
||||
theme.fg(
|
||||
"dim",
|
||||
`Press ${keyText("app.tools.expand")} to show full startup help${showDetails ? " and loaded resources" : ""}.`,
|
||||
);
|
||||
const onboarding = () =>
|
||||
theme.fg("dim", `Pi can explain its own features and look up its docs. Ask it how to use or extend Pi.`);
|
||||
const header = new BuiltInHeader(
|
||||
|
|
@ -1388,6 +1392,16 @@ export class InteractiveMode {
|
|||
return this.options.verbose || this.toolOutputExpanded;
|
||||
}
|
||||
|
||||
/** Startup header (logo, version, key hints). Hidden only by quietStartup: true. */
|
||||
private shouldShowStartupHeader(): boolean {
|
||||
return this.options.verbose === true || this.settingsManager.getQuietStartup() !== true;
|
||||
}
|
||||
|
||||
/** Startup details (model scope, loaded resources). Hidden by quietStartup: true or "header". */
|
||||
private shouldShowStartupDetails(): boolean {
|
||||
return this.options.verbose === true || this.settingsManager.getQuietStartup() === false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a short path relative to the package root for display.
|
||||
*/
|
||||
|
|
@ -1736,7 +1750,7 @@ export class InteractiveMode {
|
|||
// Resource rendering is idempotent; chat clears no longer clear this separate container.
|
||||
this.loadedResourcesContainer.clear();
|
||||
|
||||
const showListing = options?.force || this.options.verbose || !this.settingsManager.getQuietStartup();
|
||||
const showListing = options?.force || this.shouldShowStartupDetails();
|
||||
const showDiagnostics = showListing || options?.showDiagnosticsWhenQuiet === true;
|
||||
if (!showListing && !showDiagnostics) {
|
||||
return;
|
||||
|
|
@ -4937,8 +4951,8 @@ export class InteractiveMode {
|
|||
onEnableInstallTelemetryChange: (enabled) => {
|
||||
this.settingsManager.setEnableInstallTelemetry(enabled);
|
||||
},
|
||||
onQuietStartupChange: (enabled) => {
|
||||
this.settingsManager.setQuietStartup(enabled);
|
||||
onQuietStartupChange: (quiet) => {
|
||||
this.settingsManager.setQuietStartup(quiet);
|
||||
},
|
||||
onDefaultProjectTrustChange: (defaultProjectTrust) => {
|
||||
this.settingsManager.setDefaultProjectTrust(defaultProjectTrust);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { type Component, Container, type Focusable, type TUI } from "../../tui/s
|
|||
import { TuiMainScreen } from "../../tui/src/tui-main-screen.ts";
|
||||
import { VirtualTerminal } from "../../tui/test/virtual-terminal.ts";
|
||||
import type { AutocompleteProviderFactory } from "../src/core/extensions/types.ts";
|
||||
import type { QuietStartup } from "../src/core/settings-manager.ts";
|
||||
import type { SourceInfo } from "../src/core/source-info.ts";
|
||||
import type { AuthSelectorProvider } from "../src/modes/interactive/components/oauth-selector.ts";
|
||||
import { InteractiveMode } from "../src/modes/interactive/interactive-mode.ts";
|
||||
|
|
@ -513,7 +514,7 @@ describe("InteractiveMode.showLoadedResources", () => {
|
|||
});
|
||||
|
||||
function createShowLoadedResourcesThis(options: {
|
||||
quietStartup: boolean;
|
||||
quietStartup: QuietStartup;
|
||||
verbose?: boolean;
|
||||
toolOutputExpanded?: boolean;
|
||||
cwd?: string;
|
||||
|
|
@ -561,6 +562,7 @@ describe("InteractiveMode.showLoadedResources", () => {
|
|||
(InteractiveMode as any).prototype.formatExtensionDisplayPath.call(fakeThis, p),
|
||||
formatContextPath: (p: string) => (InteractiveMode as any).prototype.formatContextPath.call(fakeThis, p),
|
||||
getStartupExpansionState: () => (InteractiveMode as any).prototype.getStartupExpansionState.call(fakeThis),
|
||||
shouldShowStartupDetails: () => (InteractiveMode as any).prototype.shouldShowStartupDetails.call(fakeThis),
|
||||
buildScopeGroups: () => [],
|
||||
formatScopeGroups: () => "resource-list",
|
||||
isPackageSource: (sourceInfo?: SourceInfo) =>
|
||||
|
|
@ -1232,6 +1234,28 @@ describe("InteractiveMode.showLoadedResources", () => {
|
|||
expect(fakeThis.loadedResourcesContainer.children).toHaveLength(0);
|
||||
});
|
||||
|
||||
test("hides resource listing but keeps the startup header with header-only quiet startup", () => {
|
||||
const fakeThis = createShowLoadedResourcesThis({
|
||||
quietStartup: "header",
|
||||
skills: [{ filePath: "/tmp/skill/SKILL.md", name: "commit" }],
|
||||
});
|
||||
|
||||
(InteractiveMode as any).prototype.showLoadedResources.call(fakeThis, {
|
||||
force: false,
|
||||
});
|
||||
|
||||
expect(fakeThis.loadedResourcesContainer.children).toHaveLength(0);
|
||||
expect((InteractiveMode as any).prototype.shouldShowStartupHeader.call(fakeThis)).toBe(true);
|
||||
expect((InteractiveMode as any).prototype.shouldShowStartupDetails.call(fakeThis)).toBe(false);
|
||||
});
|
||||
|
||||
test("hides the startup header with full quiet startup unless verbose", () => {
|
||||
const quiet = createShowLoadedResourcesThis({ quietStartup: true });
|
||||
expect((InteractiveMode as any).prototype.shouldShowStartupHeader.call(quiet)).toBe(false);
|
||||
const verbose = createShowLoadedResourcesThis({ quietStartup: "header", verbose: true });
|
||||
expect((InteractiveMode as any).prototype.shouldShowStartupDetails.call(verbose)).toBe(true);
|
||||
});
|
||||
|
||||
test("still shows diagnostics on quiet startup when requested", () => {
|
||||
const fakeThis = createShowLoadedResourcesThis({
|
||||
quietStartup: true,
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ type LoadedResourcesContext = {
|
|||
};
|
||||
};
|
||||
getStartupExpansionState: () => boolean;
|
||||
shouldShowStartupDetails: () => boolean;
|
||||
formatDisplayPath: (resourcePath: string) => string;
|
||||
formatContextPath: (resourcePath: string) => string;
|
||||
getBuiltInCommandConflictDiagnostics: (extensionRunner: LoadedResourcesContext["session"]["extensionRunner"]) => [];
|
||||
|
|
@ -208,6 +209,7 @@ function createLoadedResourcesContext(): LoadedResourcesContext {
|
|||
},
|
||||
},
|
||||
getStartupExpansionState: () => false,
|
||||
shouldShowStartupDetails: () => true,
|
||||
formatDisplayPath: (resourcePath) => resourcePath,
|
||||
formatContextPath: (resourcePath) => resourcePath.replace("/repo/", ""),
|
||||
getBuiltInCommandConflictDiagnostics: () => [],
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue