pi-remote-control/extensions/remote-control/pi/autoname.ts

74 lines
1.9 KiB
TypeScript

/**
* Auto-naming via `pi -p` (S-09a).
*
* After a configurable number of user messages, spawn a cheap `pi -p` call
* to generate a short session name from the conversation context.
* The result is stored as the tmux session's @description.
*
* Gated by [autoname] enabled in config.toml (T-1.7 wires the config;
* until then defaults are used).
*
* Owner: T-1.4
*/
import { execFile } from "node:child_process";
import { promisify } from "node:util";
import { setDescription } from "../tmux/manager.js";
const execFileAsync = promisify(execFile);
export interface AutonameConfig {
enabled: boolean;
triggerAfter: number; // number of user messages before naming
model: string; // e.g. "claude-haiku-4-5"
}
export const DEFAULT_AUTONAME_CONFIG: AutonameConfig = {
enabled: true,
triggerAfter: 3,
model: "claude-haiku-4-5",
};
/**
* Attempt to auto-name a session using `pi -p`.
* If pi is not on PATH or the call fails, silently no-ops.
*
* @param sessionId tmux session name to set @description on
* @param context recent conversation context (short excerpt)
* @param cfg autoname configuration
*/
export async function autoname(
sessionId: string,
context: string,
cfg: AutonameConfig = DEFAULT_AUTONAME_CONFIG,
): Promise<void> {
if (!cfg.enabled) return;
const prompt = `Give a 2-4 word title for this conversation. Reply with only the title, no punctuation.\n\n${context.slice(0, 800)}`;
try {
const { stdout } = await execFileAsync(
"pi",
[
"-p",
"--model",
cfg.model,
"--no-session",
"--no-tools",
"--no-extensions",
"--no-skills",
"--offline",
prompt,
],
{ timeout: 15_000 },
);
const name = stdout.trim().slice(0, 60); // cap at 60 chars
if (name) {
await setDescription(sessionId, name);
}
} catch {
// Autoname failures are non-fatal
}
}