feat: add /thinking slash command for toggling thinking level (#129)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Lex Christopherson 2026-03-13 10:50:19 -06:00
parent d9548cdf26
commit bb10aacb23
3 changed files with 61 additions and 4 deletions

View file

@ -34,5 +34,6 @@ export const BUILTIN_SLASH_COMMANDS: ReadonlyArray<BuiltinSlashCommand> = [
{ name: "compact", description: "Manually compact the session context" },
{ name: "resume", description: "Resume a different session" },
{ name: "reload", description: "Reload extensions, skills, prompts, and themes" },
{ name: "thinking", description: "Set thinking level (off/minimal/low/medium/high/xhigh)" },
{ name: "quit", description: "Quit pi" },
];

View file

@ -13,7 +13,7 @@ import {
import { getSelectListTheme, getSettingsListTheme, theme } from "../theme/theme.js";
import { DynamicBorder } from "./dynamic-border.js";
const THINKING_DESCRIPTIONS: Record<ThinkingLevel, string> = {
export const THINKING_DESCRIPTIONS: Record<ThinkingLevel, string> = {
off: "No reasoning",
minimal: "Very brief reasoning (~1k tokens)",
low: "Light reasoning (~2k tokens)",
@ -73,7 +73,7 @@ export interface SettingsCallbacks {
/**
* A submenu component for selecting from a list of options.
*/
class SelectSubmenu extends Container {
export class SelectSubmenu extends Container {
private selectList: SelectList;
constructor(

View file

@ -7,7 +7,7 @@ import * as crypto from "node:crypto";
import * as fs from "node:fs";
import * as os from "node:os";
import * as path from "node:path";
import type { AgentMessage } from "@gsd/pi-agent-core";
import type { AgentMessage, ThinkingLevel } from "@gsd/pi-agent-core";
import type { AssistantMessage, ImageContent, Message, Model, OAuthProviderId } from "@gsd/pi-ai";
import type {
AutocompleteItem,
@ -85,7 +85,7 @@ import { ModelSelectorComponent } from "./components/model-selector.js";
import { OAuthSelectorComponent } from "./components/oauth-selector.js";
import { ScopedModelsSelectorComponent } from "./components/scoped-models-selector.js";
import { SessionSelectorComponent } from "./components/session-selector.js";
import { SettingsSelectorComponent } from "./components/settings-selector.js";
import { SelectSubmenu, SettingsSelectorComponent, THINKING_DESCRIPTIONS } from "./components/settings-selector.js";
import { SkillInvocationMessageComponent } from "./components/skill-invocation-message.js";
import { ToolExecutionComponent } from "./components/tool-execution.js";
import { TreeSelectorComponent } from "./components/tree-selector.js";
@ -2015,6 +2015,12 @@ export class InteractiveMode {
await this.handleReloadCommand();
return;
}
if (text === "/thinking" || text.startsWith("/thinking ")) {
const arg = text.startsWith("/thinking ") ? text.slice(10).trim() : undefined;
this.editor.setText("");
this.handleThinkingCommand(arg);
return;
}
if (text === "/debug") {
this.handleDebugCommand();
this.editor.setText("");
@ -2745,6 +2751,56 @@ export class InteractiveMode {
}
}
private handleThinkingCommand(arg?: string): void {
const availableLevels = this.session.getAvailableThinkingLevels();
if (availableLevels.length === 0) {
this.showStatus("Current model does not support thinking");
return;
}
if (arg) {
const level = arg.toLowerCase() as ThinkingLevel;
if (!availableLevels.includes(level)) {
this.showStatus(`Invalid thinking level "${arg}". Available: ${availableLevels.join(", ")}`);
return;
}
this.session.setThinkingLevel(level);
this.footer.invalidate();
this.updateEditorBorderColor();
this.showStatus(`Thinking level: ${level}`);
return;
}
this.showThinkingSelector();
}
private showThinkingSelector(): void {
const availableLevels = this.session.getAvailableThinkingLevels();
this.showSelector((done) => {
const selector = new SelectSubmenu(
"Thinking Level",
"Select reasoning depth for thinking-capable models",
availableLevels.map((level) => ({
value: level,
label: level,
description: THINKING_DESCRIPTIONS[level],
})),
this.session.thinkingLevel,
(value) => {
this.session.setThinkingLevel(value as ThinkingLevel);
this.footer.invalidate();
this.updateEditorBorderColor();
done();
this.showStatus(`Thinking level: ${value}`);
},
() => {
done();
},
);
return { component: selector, focus: selector };
});
}
private async cycleModel(direction: "forward" | "backward"): Promise<void> {
try {
const result = await this.session.cycleModel(direction);