mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-06 23:44:36 +00:00
Merge remote-tracking branch 'origin/main' into dev
This commit is contained in:
commit
fd389abc46
17 changed files with 244 additions and 58 deletions
|
|
@ -222,7 +222,7 @@ export function createFindToolDefinition(
|
|||
}
|
||||
|
||||
// Default implementation uses fd.
|
||||
const fdPath = await ensureTool("fd", true);
|
||||
const fdPath = await ensureTool("fd");
|
||||
if (signal?.aborted) {
|
||||
settle(() => reject(new Error("Operation aborted")));
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ export function createGrepToolDefinition(
|
|||
|
||||
(async () => {
|
||||
try {
|
||||
const rgPath = await ensureTool("rg", true);
|
||||
const rgPath = await ensureTool("rg");
|
||||
if (!rgPath) {
|
||||
settle(() => reject(new Error("ripgrep (rg) is not available and could not be downloaded")));
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -107,7 +107,7 @@ import { openBrowser } from "../../utils/open-browser.ts";
|
|||
import { getCwdRelativePath } from "../../utils/paths.ts";
|
||||
import { getPiUserAgent } from "../../utils/pi-user-agent.ts";
|
||||
import { killTrackedDetachedChildren } from "../../utils/shell.ts";
|
||||
import { ensureTool } from "../../utils/tools-manager.ts";
|
||||
import { ensureTool, type ToolStatus } from "../../utils/tools-manager.ts";
|
||||
import { checkForNewPiVersion, type LatestPiRelease } from "../../utils/version-check.ts";
|
||||
import { ArminComponent } from "./components/armin.ts";
|
||||
import { AssistantMessageComponent } from "./components/assistant-message.ts";
|
||||
|
|
@ -436,6 +436,7 @@ export class InteractiveMode {
|
|||
// Status line tracking (for mutating immediately-sequential status updates)
|
||||
private lastStatusSpacer: Spacer | undefined = undefined;
|
||||
private lastStatusText: Text | undefined = undefined;
|
||||
private managedToolStatusStarted = false;
|
||||
|
||||
// Streaming message tracking
|
||||
private streamingComponent: AssistantMessageComponent | undefined = undefined;
|
||||
|
|
@ -851,11 +852,6 @@ export class InteractiveMode {
|
|||
// Load changelog (only show new entries, skip for resumed sessions)
|
||||
this.changelogMarkdown = this.getChangelogForDisplay();
|
||||
|
||||
// Ensure fd and rg are available (downloads if missing, adds to PATH via getBinDir)
|
||||
// Both are needed: fd for autocomplete, rg for grep tool and bash commands
|
||||
const [fdPath] = await Promise.all([ensureTool("fd"), ensureTool("rg")]);
|
||||
this.fdPath = fdPath;
|
||||
|
||||
if (this.session.scopedModels.length > 0 && (this.options.verbose || !this.settingsManager.getQuietStartup())) {
|
||||
const modelList = this.session.scopedModels
|
||||
.map((sm) => {
|
||||
|
|
@ -901,11 +897,12 @@ export class InteractiveMode {
|
|||
this.widgetContainerBelow,
|
||||
this.footerContainer,
|
||||
]);
|
||||
// Accept text while startup completes, but only enable interrupt, exit, and submission feedback.
|
||||
this.defaultEditor.onAction("app.clear", () => this.handleCtrlC());
|
||||
this.defaultEditor.onCtrlD = () => this.handleCtrlD();
|
||||
this.defaultEditor.onSubmit = (text) => this.handleStartupSubmit(text);
|
||||
this.ui.setFocus(this.editor);
|
||||
|
||||
this.setupKeyHandlers();
|
||||
this.setupEditorSubmitHandler();
|
||||
|
||||
// Start the UI before initializing extensions so session_start handlers can use interactive dialogs
|
||||
this.ui.start();
|
||||
this.isInitialized = true;
|
||||
|
|
@ -974,6 +971,20 @@ export class InteractiveMode {
|
|||
}
|
||||
this.ui.requestRender();
|
||||
|
||||
// Ensure fd and rg are available after mounting the TUI (downloads if missing, adds to PATH via getBinDir)
|
||||
// so slow downloads do not make startup appear frozen.
|
||||
// Both are needed: fd for autocomplete, rg for grep tool and bash commands.
|
||||
const [fdPath] = await Promise.all([
|
||||
ensureTool("fd", (status) => this.showManagedToolStatus(status)),
|
||||
ensureTool("rg", (status) => this.showManagedToolStatus(status)),
|
||||
]);
|
||||
this.fdPath = fdPath;
|
||||
|
||||
// Enable the remaining input handlers only after managed-tool setup completes.
|
||||
this.setupKeyHandlers();
|
||||
this.setupEditorSubmitHandler();
|
||||
this.ui.requestRender();
|
||||
|
||||
// Initialize extensions first so resources are shown before messages
|
||||
await this.rebindCurrentSession();
|
||||
|
||||
|
|
@ -2873,6 +2884,11 @@ export class InteractiveMode {
|
|||
}
|
||||
}
|
||||
|
||||
private handleStartupSubmit(text: string): void {
|
||||
this.editor.setText(text);
|
||||
this.showStatus("Startup is still in progress");
|
||||
}
|
||||
|
||||
private setupEditorSubmitHandler(): void {
|
||||
this.defaultEditor.onSubmit = async (text: string) => {
|
||||
text = text.trim();
|
||||
|
|
@ -3408,6 +3424,20 @@ export class InteractiveMode {
|
|||
return textBlocks.map((c) => (c as { text: string }).text).join("");
|
||||
}
|
||||
|
||||
/** Show a managed-tool status update in the chat. */
|
||||
private showManagedToolStatus(status: ToolStatus): void {
|
||||
if (!this.managedToolStatusStarted) {
|
||||
this.chatContainer.addChild(new Spacer(1));
|
||||
this.managedToolStatusStarted = true;
|
||||
}
|
||||
const message = status.type === "warning" ? `Warning: ${status.message}` : status.message;
|
||||
const color = status.type === "warning" ? "warning" : "dim";
|
||||
this.chatContainer.addChild(new Text(theme.fg(color, message), 1, 0));
|
||||
this.lastStatusSpacer = undefined;
|
||||
this.lastStatusText = undefined;
|
||||
this.ui.requestRender();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show a status message in the chat.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import chalk from "chalk";
|
||||
import { type SpawnSyncReturns, spawnSync } from "child_process";
|
||||
import { chmodSync, createWriteStream, existsSync, mkdirSync, readdirSync, renameSync, rmSync } from "fs";
|
||||
import { arch, platform } from "os";
|
||||
|
|
@ -323,9 +322,20 @@ const TERMUX_PACKAGES: Record<string, string> = {
|
|||
rg: "ripgrep",
|
||||
};
|
||||
|
||||
// Ensure a tool is available, downloading if necessary
|
||||
// Returns the path to the tool, or null if unavailable
|
||||
export async function ensureTool(tool: "fd" | "rg", silent: boolean = false): Promise<string | undefined> {
|
||||
export interface ToolStatus {
|
||||
type: "info" | "warning";
|
||||
message: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure a tool is available, downloading if necessary.
|
||||
* Reports progress through `onStatus`; status messages are otherwise silent.
|
||||
* Returns the tool path, or undefined if unavailable.
|
||||
*/
|
||||
export async function ensureTool(
|
||||
tool: "fd" | "rg",
|
||||
onStatus?: (status: ToolStatus) => void,
|
||||
): Promise<string | undefined> {
|
||||
const existingPath = getToolPath(tool);
|
||||
if (existingPath) {
|
||||
return existingPath;
|
||||
|
|
@ -335,9 +345,7 @@ export async function ensureTool(tool: "fd" | "rg", silent: boolean = false): Pr
|
|||
if (!config) return undefined;
|
||||
|
||||
if (isOfflineModeEnabled()) {
|
||||
if (!silent) {
|
||||
console.log(chalk.yellow(`${config.name} not found. Offline mode enabled, skipping download.`));
|
||||
}
|
||||
onStatus?.({ type: "warning", message: `${config.name} not found. Offline mode enabled, skipping download.` });
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
|
@ -345,27 +353,22 @@ export async function ensureTool(tool: "fd" | "rg", silent: boolean = false): Pr
|
|||
// Users must install via pkg.
|
||||
if (platform() === "android") {
|
||||
const pkgName = TERMUX_PACKAGES[tool] ?? tool;
|
||||
if (!silent) {
|
||||
console.log(chalk.yellow(`${config.name} not found. Install with: pkg install ${pkgName}`));
|
||||
}
|
||||
onStatus?.({ type: "warning", message: `${config.name} not found. Install with: pkg install ${pkgName}` });
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Tool not found - download it
|
||||
if (!silent) {
|
||||
console.log(chalk.dim(`${config.name} not found. Downloading...`));
|
||||
}
|
||||
onStatus?.({ type: "info", message: `${config.name} not found. Downloading...` });
|
||||
|
||||
try {
|
||||
const path = await downloadTool(tool);
|
||||
if (!silent) {
|
||||
console.log(chalk.dim(`${config.name} installed to ${path}`));
|
||||
}
|
||||
onStatus?.({ type: "info", message: `${config.name} installed to ${path}` });
|
||||
return path;
|
||||
} catch (e) {
|
||||
if (!silent) {
|
||||
console.log(chalk.yellow(`Failed to download ${config.name}: ${e instanceof Error ? e.message : e}`));
|
||||
}
|
||||
onStatus?.({
|
||||
type: "warning",
|
||||
message: `Failed to download ${config.name}: ${e instanceof Error ? e.message : e}`,
|
||||
});
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue