feat(coding-agent): add startup.quiet setting to silence startup output (#777)

* feat(coding-agent): add startup.quiet setting to silence startup output

* feat(coding-agent): add startup.quiet setting to silence startup output

Adds a new setting `startup.quiet` that when set to `true` hides:
- Version and keybinding hints header
- Loaded context/skills/templates/extensions discovery info
- Model scope line

Changelog notifications are still shown so users know about updates.

Usage in ~/.pi/agent/settings.json:
{
  "startup": {
    "quiet": true
  }
}

* refactor: flatten startup.quiet to quietStartup on Settings
This commit is contained in:
Rafał Krzyważnia 2026-01-16 22:03:29 +01:00 committed by GitHub
parent 3326b8f521
commit d2f9ab110c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 105 additions and 75 deletions

View file

@ -60,6 +60,7 @@ export interface Settings {
retry?: RetrySettings; retry?: RetrySettings;
hideThinkingBlock?: boolean; hideThinkingBlock?: boolean;
shellPath?: string; // Custom shell path (e.g., for Cygwin users on Windows) shellPath?: string; // Custom shell path (e.g., for Cygwin users on Windows)
quietStartup?: boolean;
collapseChangelog?: boolean; // Show condensed changelog after update (use /changelog for full) collapseChangelog?: boolean; // Show condensed changelog after update (use /changelog for full)
extensions?: string[]; // Array of extension file paths extensions?: string[]; // Array of extension file paths
skills?: SkillsSettings; skills?: SkillsSettings;
@ -346,6 +347,15 @@ export class SettingsManager {
this.save(); this.save();
} }
getQuietStartup(): boolean {
return this.settings.quietStartup ?? false;
}
setQuietStartup(quiet: boolean): void {
this.globalSettings.quietStartup = quiet;
this.save();
}
getCollapseChangelog(): boolean { getCollapseChangelog(): boolean {
return this.settings.collapseChangelog ?? false; return this.settings.collapseChangelog ?? false;
} }

View file

@ -503,7 +503,7 @@ export async function main(args: string[]) {
if (mode === "rpc") { if (mode === "rpc") {
await runRpcMode(session); await runRpcMode(session);
} else if (isInteractive) { } else if (isInteractive) {
if (scopedModels.length > 0) { if (scopedModels.length > 0 && !settingsManager.getQuietStartup()) {
const modelList = scopedModels const modelList = scopedModels
.map((sm) => { .map((sm) => {
const thinkingStr = sm.thinkingLevel ? `:${sm.thinkingLevel}` : ""; const thinkingStr = sm.thinkingLevel ? `:${sm.thinkingLevel}` : "";

View file

@ -357,7 +357,8 @@ export class InteractiveMode {
this.fdPath = await ensureTool("fd"); this.fdPath = await ensureTool("fd");
this.setupAutocomplete(this.fdPath); this.setupAutocomplete(this.fdPath);
// Add header with keybindings from config // Add header with keybindings from config (unless silenced)
if (!this.settingsManager.getQuietStartup()) {
const logo = theme.bold(theme.fg("accent", APP_NAME)) + theme.fg("dim", ` v${this.version}`); const logo = theme.bold(theme.fg("accent", APP_NAME)) + theme.fg("dim", ` v${this.version}`);
// Build startup instructions using keybinding hint helpers // Build startup instructions using keybinding hint helpers
@ -408,6 +409,18 @@ export class InteractiveMode {
} }
this.ui.addChild(new DynamicBorder()); this.ui.addChild(new DynamicBorder());
} }
} else {
// Minimal header when silenced
this.builtInHeader = new Text("", 0, 0);
if (this.changelogMarkdown) {
// Still show changelog notification even in silent mode
this.ui.addChild(new Spacer(1));
const versionMatch = this.changelogMarkdown.match(/##\s+\[?(\d+\.\d+\.\d+)\]?/);
const latestVersion = versionMatch ? versionMatch[1] : this.version;
const condensedText = `Updated to v${latestVersion}. Use ${theme.bold("/changelog")} to view full changelog.`;
this.ui.addChild(new Text(condensedText, 1, 0));
}
}
this.ui.addChild(this.chatContainer); this.ui.addChild(this.chatContainer);
this.ui.addChild(this.pendingMessagesContainer); this.ui.addChild(this.pendingMessagesContainer);
@ -573,6 +586,8 @@ export class InteractiveMode {
* Initialize the extension system with TUI-based UI context. * Initialize the extension system with TUI-based UI context.
*/ */
private async initExtensions(): Promise<void> { private async initExtensions(): Promise<void> {
// Show discovery info unless silenced
if (!this.settingsManager.getQuietStartup()) {
// Show loaded project context files // Show loaded project context files
const contextFiles = loadProjectContextFiles(); const contextFiles = loadProjectContextFiles();
if (contextFiles.length > 0) { if (contextFiles.length > 0) {
@ -592,7 +607,9 @@ export class InteractiveMode {
// Show skill warnings if any // Show skill warnings if any
const skillWarnings = this.session.skillWarnings; const skillWarnings = this.session.skillWarnings;
if (skillWarnings.length > 0) { if (skillWarnings.length > 0) {
const warningList = skillWarnings.map((w) => theme.fg("warning", ` ${w.skillPath}: ${w.message}`)).join("\n"); const warningList = skillWarnings
.map((w) => theme.fg("warning", ` ${w.skillPath}: ${w.message}`))
.join("\n");
this.chatContainer.addChild(new Text(theme.fg("warning", "Skill warnings:\n") + warningList, 0, 0)); this.chatContainer.addChild(new Text(theme.fg("warning", "Skill warnings:\n") + warningList, 0, 0));
this.chatContainer.addChild(new Spacer(1)); this.chatContainer.addChild(new Spacer(1));
} }
@ -604,6 +621,7 @@ export class InteractiveMode {
this.chatContainer.addChild(new Text(theme.fg("muted", "Loaded prompt templates:\n") + templateList, 0, 0)); this.chatContainer.addChild(new Text(theme.fg("muted", "Loaded prompt templates:\n") + templateList, 0, 0));
this.chatContainer.addChild(new Spacer(1)); this.chatContainer.addChild(new Spacer(1));
} }
}
const extensionRunner = this.session.extensionRunner; const extensionRunner = this.session.extensionRunner;
if (!extensionRunner) { if (!extensionRunner) {
@ -748,13 +766,15 @@ export class InteractiveMode {
// Set up extension-registered shortcuts // Set up extension-registered shortcuts
this.setupExtensionShortcuts(extensionRunner); this.setupExtensionShortcuts(extensionRunner);
// Show loaded extensions // Show loaded extensions (unless silenced)
if (!this.settingsManager.getQuietStartup()) {
const extensionPaths = extensionRunner.getExtensionPaths(); const extensionPaths = extensionRunner.getExtensionPaths();
if (extensionPaths.length > 0) { if (extensionPaths.length > 0) {
const extList = extensionPaths.map((p) => theme.fg("dim", ` ${p}`)).join("\n"); const extList = extensionPaths.map((p) => theme.fg("dim", ` ${p}`)).join("\n");
this.chatContainer.addChild(new Text(theme.fg("muted", "Loaded extensions:\n") + extList, 0, 0)); this.chatContainer.addChild(new Text(theme.fg("muted", "Loaded extensions:\n") + extList, 0, 0));
this.chatContainer.addChild(new Spacer(1)); this.chatContainer.addChild(new Spacer(1));
} }
}
// Emit session_start event // Emit session_start event
await extensionRunner.emit({ await extensionRunner.emit({