chore: migrate to oxlint and oxfmt

Co-authored-by: Christoph Nakazawa <christoph.pojer@gmail.com>
This commit is contained in:
Peter Steinberger
2026-01-14 14:31:43 +00:00
parent 912ebffc63
commit c379191f80
1480 changed files with 28608 additions and 43547 deletions

View File

@@ -1,8 +1,5 @@
import type { SlashCommand } from "@mariozechner/pi-tui";
import {
formatThinkingLevels,
listThinkingLevels,
} from "../auto-reply/thinking.js";
import { formatThinkingLevels, listThinkingLevels } from "../auto-reply/thinking.js";
const VERBOSE_LEVELS = ["on", "off"];
const REASONING_LEVELS = ["on", "off"];
@@ -35,9 +32,7 @@ export function parseCommand(input: string): ParsedCommand {
};
}
export function getSlashCommands(
options: SlashCommandOptions = {},
): SlashCommand[] {
export function getSlashCommands(options: SlashCommandOptions = {}): SlashCommand[] {
const thinkLevels = listThinkingLevels(options.provider, options.model);
return [
{ name: "help", description: "Show slash command help" },
@@ -63,49 +58,55 @@ export function getSlashCommands(
name: "verbose",
description: "Set verbose on/off",
getArgumentCompletions: (prefix) =>
VERBOSE_LEVELS.filter((v) => v.startsWith(prefix.toLowerCase())).map(
(value) => ({ value, label: value }),
),
VERBOSE_LEVELS.filter((v) => v.startsWith(prefix.toLowerCase())).map((value) => ({
value,
label: value,
})),
},
{
name: "reasoning",
description: "Set reasoning on/off",
getArgumentCompletions: (prefix) =>
REASONING_LEVELS.filter((v) => v.startsWith(prefix.toLowerCase())).map(
(value) => ({ value, label: value }),
),
REASONING_LEVELS.filter((v) => v.startsWith(prefix.toLowerCase())).map((value) => ({
value,
label: value,
})),
},
{
name: "cost",
description: "Toggle per-response usage line",
getArgumentCompletions: (prefix) =>
TOGGLE.filter((v) => v.startsWith(prefix.toLowerCase())).map(
(value) => ({ value, label: value }),
),
TOGGLE.filter((v) => v.startsWith(prefix.toLowerCase())).map((value) => ({
value,
label: value,
})),
},
{
name: "elevated",
description: "Set elevated on/off",
getArgumentCompletions: (prefix) =>
ELEVATED_LEVELS.filter((v) => v.startsWith(prefix.toLowerCase())).map(
(value) => ({ value, label: value }),
),
ELEVATED_LEVELS.filter((v) => v.startsWith(prefix.toLowerCase())).map((value) => ({
value,
label: value,
})),
},
{
name: "elev",
description: "Alias for /elevated",
getArgumentCompletions: (prefix) =>
ELEVATED_LEVELS.filter((v) => v.startsWith(prefix.toLowerCase())).map(
(value) => ({ value, label: value }),
),
ELEVATED_LEVELS.filter((v) => v.startsWith(prefix.toLowerCase())).map((value) => ({
value,
label: value,
})),
},
{
name: "activation",
description: "Set group activation",
getArgumentCompletions: (prefix) =>
ACTIVATION_LEVELS.filter((v) => v.startsWith(prefix.toLowerCase())).map(
(value) => ({ value, label: value }),
),
ACTIVATION_LEVELS.filter((v) => v.startsWith(prefix.toLowerCase())).map((value) => ({
value,
label: value,
})),
},
{ name: "abort", description: "Abort active run" },
{ name: "new", description: "Reset the session" },
@@ -117,11 +118,7 @@ export function getSlashCommands(
}
export function helpText(options: SlashCommandOptions = {}): string {
const thinkLevels = formatThinkingLevels(
options.provider,
options.model,
"|",
);
const thinkLevels = formatThinkingLevels(options.provider, options.model, "|");
return [
"Slash commands:",
"/help",

View File

@@ -41,11 +41,7 @@ export class CustomEditor extends Editor {
this.onShiftTab();
return;
}
if (
matchesKey(data, Key.escape) &&
this.onEscape &&
!this.isShowingAutocomplete()
) {
if (matchesKey(data, Key.escape) && this.onEscape && !this.isShowingAutocomplete()) {
this.onEscape();
return;
}

View File

@@ -1,9 +1,4 @@
import {
type SelectItem,
SelectList,
type SettingItem,
SettingsList,
} from "@mariozechner/pi-tui";
import { type SelectItem, SelectList, type SettingItem, SettingsList } from "@mariozechner/pi-tui";
import { selectListTheme, settingsListTheme } from "../theme/theme.js";
export function createSelectList(items: SelectItem[], maxVisible = 7) {
@@ -16,11 +11,5 @@ export function createSettingsList(
onCancel: () => void,
maxVisible = 7,
) {
return new SettingsList(
items,
maxVisible,
settingsListTheme,
onChange,
onCancel,
);
return new SettingsList(items, maxVisible, settingsListTheme, onChange, onCancel);
}

View File

@@ -1,8 +1,5 @@
import { Box, Container, Markdown, Spacer, Text } from "@mariozechner/pi-tui";
import {
formatToolDetail,
resolveToolDisplay,
} from "../../agents/tool-display.js";
import { formatToolDetail, resolveToolDisplay } from "../../agents/tool-display.js";
import { markdownTheme, theme } from "../theme/theme.js";
type ToolResultContent = {
@@ -124,9 +121,7 @@ export class ToolExecutionComponent extends Container {
if (!this.expanded && text) {
const lines = text.split("\n");
const preview =
lines.length > PREVIEW_LINES
? `${lines.slice(0, PREVIEW_LINES).join("\n")}\n…`
: text;
lines.length > PREVIEW_LINES ? `${lines.slice(0, PREVIEW_LINES).join("\n")}\n…` : text;
this.output.setText(preview);
} else {
this.output.setText(text);

View File

@@ -7,10 +7,7 @@ import {
type SessionsListParams,
type SessionsPatchParams,
} from "../gateway/protocol/index.js";
import {
GATEWAY_CLIENT_MODES,
GATEWAY_CLIENT_NAMES,
} from "../utils/message-channel.js";
import { GATEWAY_CLIENT_MODES, GATEWAY_CLIENT_NAMES } from "../utils/message-channel.js";
import { VERSION } from "../version.js";
export type GatewayConnectionOptions = {
@@ -163,13 +160,10 @@ export class GatewayChatClient {
}
async abortChat(opts: { sessionKey: string; runId: string }) {
return await this.client.request<{ ok: boolean; aborted: boolean }>(
"chat.abort",
{
sessionKey: opts.sessionKey,
runId: opts.runId,
},
);
return await this.client.request<{ ok: boolean; aborted: boolean }>("chat.abort", {
sessionKey: opts.sessionKey,
runId: opts.runId,
});
}
async loadHistory(opts: { sessionKey: string; limit?: number }) {
@@ -206,9 +200,7 @@ export class GatewayChatClient {
}
async listModels(): Promise<GatewayModelChoice[]> {
const res = await this.client.request<{ models?: GatewayModelChoice[] }>(
"models.list",
);
const res = await this.client.request<{ models?: GatewayModelChoice[] }>("models.list");
return Array.isArray(res?.models) ? res.models : [];
}
}
@@ -221,9 +213,7 @@ export function resolveGatewayConnection(opts: GatewayConnectionOptions) {
const localPort = resolveGatewayPort(config);
const url =
(typeof opts.url === "string" && opts.url.trim().length > 0
? opts.url.trim()
: undefined) ||
(typeof opts.url === "string" && opts.url.trim().length > 0 ? opts.url.trim() : undefined) ||
(typeof remote?.url === "string" && remote.url.trim().length > 0
? remote.url.trim()
: undefined) ||

View File

@@ -82,8 +82,7 @@ export const selectListTheme: SelectListTheme = {
export const settingsListTheme: SettingsListTheme = {
label: (text, selected) =>
selected ? chalk.bold(fg(palette.accent)(text)) : fg(palette.text)(text),
value: (text, selected) =>
selected ? fg(palette.accentSoft)(text) : fg(palette.dim)(text),
value: (text, selected) => (selected ? fg(palette.accentSoft)(text) : fg(palette.dim)(text)),
description: (text) => fg(palette.systemText)(text),
cursor: fg(palette.accent)("→ "),
hint: (text) => fg(palette.dim)(text),

View File

@@ -1,15 +1,9 @@
import type { Component, TUI } from "@mariozechner/pi-tui";
import {
formatThinkingLevels,
normalizeUsageDisplay,
} from "../auto-reply/thinking.js";
import { formatThinkingLevels, normalizeUsageDisplay } from "../auto-reply/thinking.js";
import { normalizeAgentId } from "../routing/session-key.js";
import { helpText, parseCommand } from "./commands.js";
import type { ChatLog } from "./components/chat-log.js";
import {
createSelectList,
createSettingsList,
} from "./components/selectors.js";
import { createSelectList, createSettingsList } from "./components/selectors.js";
import type { GatewayChatClient } from "./gateway-chat.js";
import { formatStatusSummary } from "./tui-status-summary.js";
import type {
@@ -143,9 +137,7 @@ export function createCommandHandlers(context: CommandHandlerContext) {
label: session.displayName
? `${session.displayName} (${formatSessionKey(session.key)})`
: formatSessionKey(session.key),
description: session.updatedAt
? new Date(session.updatedAt).toLocaleString()
: "",
description: session.updatedAt ? new Date(session.updatedAt).toLocaleString() : "",
}));
const selector = createSelectList(items, 9);
selector.onSelect = (item) => {
@@ -338,9 +330,7 @@ export function createCommandHandlers(context: CommandHandlerContext) {
key: state.currentSessionKey,
responseUsage: next === "off" ? null : next,
});
chatLog.addSystem(
next === "on" ? "usage line enabled" : "usage line disabled",
);
chatLog.addSystem(next === "on" ? "usage line enabled" : "usage line disabled");
await refreshSessionInfo();
} catch (err) {
chatLog.addSystem(`cost failed: ${String(err)}`);

View File

@@ -1,10 +1,6 @@
import type { TUI } from "@mariozechner/pi-tui";
import type { ChatLog } from "./components/chat-log.js";
import {
asString,
extractTextFromMessage,
resolveFinalAssistantText,
} from "./tui-formatters.js";
import { asString, extractTextFromMessage, resolveFinalAssistantText } from "./tui-formatters.js";
import type { AgentEvent, ChatEvent, TuiStateAccess } from "./tui-types.js";
type EventHandlerContext = {

View File

@@ -11,10 +11,7 @@ export function resolveFinalAssistantText(params: {
return "(no output)";
}
function extractTextBlocks(
content: unknown,
opts?: { includeThinking?: boolean },
): string {
function extractTextBlocks(content: unknown, opts?: { includeThinking?: boolean }): string {
if (typeof content === "string") return content.trim();
if (!Array.isArray(content)) return "";
const parts: string[] = [];
@@ -52,9 +49,7 @@ export function formatTokens(total?: number | null, context?: number | null) {
typeof total === "number" && context > 0
? Math.min(999, Math.round((total / context) * 100))
: null;
return `tokens ${totalLabel}/${formatTokenCount(context)}${
pct !== null ? ` (${pct}%)` : ""
}`;
return `tokens ${totalLabel}/${formatTokenCount(context)}${pct !== null ? ` (${pct}%)` : ""}`;
}
export function formatContextUsageLine(params: {
@@ -63,18 +58,11 @@ export function formatContextUsageLine(params: {
remaining?: number | null;
percent?: number | null;
}) {
const totalLabel =
typeof params.total === "number" ? formatTokenCount(params.total) : "?";
const ctxLabel =
typeof params.context === "number" ? formatTokenCount(params.context) : "?";
const pct =
typeof params.percent === "number"
? Math.min(999, Math.round(params.percent))
: null;
const totalLabel = typeof params.total === "number" ? formatTokenCount(params.total) : "?";
const ctxLabel = typeof params.context === "number" ? formatTokenCount(params.context) : "?";
const pct = typeof params.percent === "number" ? Math.min(999, Math.round(params.percent)) : null;
const remainingLabel =
typeof params.remaining === "number"
? `${formatTokenCount(params.remaining)} left`
: null;
typeof params.remaining === "number" ? `${formatTokenCount(params.remaining)} left` : null;
const pctLabel = pct !== null ? `${pct}%` : null;
const extra = [remainingLabel, pctLabel].filter(Boolean).join(", ");
return `tokens ${totalLabel}/${ctxLabel}${extra ? ` (${extra})` : ""}`;

View File

@@ -59,24 +59,18 @@ export function createSessionActions(context: SessionActionContext) {
if (state.agents.some((agent) => agent.id === initialSessionAgentId)) {
state.currentAgentId = initialSessionAgentId;
}
} else if (
!state.agents.some((agent) => agent.id === state.currentAgentId)
) {
} else if (!state.agents.some((agent) => agent.id === state.currentAgentId)) {
state.currentAgentId =
state.agents[0]?.id ??
normalizeAgentId(result.defaultId ?? state.currentAgentId);
state.agents[0]?.id ?? normalizeAgentId(result.defaultId ?? state.currentAgentId);
}
const nextSessionKey = resolveSessionKey(initialSessionInput);
if (nextSessionKey !== state.currentSessionKey) {
state.currentSessionKey = nextSessionKey;
}
state.initialSessionApplied = true;
} else if (
!state.agents.some((agent) => agent.id === state.currentAgentId)
) {
} else if (!state.agents.some((agent) => agent.id === state.currentAgentId)) {
state.currentAgentId =
state.agents[0]?.id ??
normalizeAgentId(result.defaultId ?? state.currentAgentId);
state.agents[0]?.id ?? normalizeAgentId(result.defaultId ?? state.currentAgentId);
}
updateHeader();
updateFooter();
@@ -103,8 +97,7 @@ export function createSessionActions(context: SessionActionContext) {
const refreshSessionInfo = async () => {
try {
const listAgentId =
state.currentSessionKey === "global" ||
state.currentSessionKey === "unknown"
state.currentSessionKey === "global" || state.currentSessionKey === "unknown"
? undefined
: state.currentAgentId;
const result = await client.listSessions({
@@ -152,10 +145,8 @@ export function createSessionActions(context: SessionActionContext) {
sessionId?: string;
thinkingLevel?: string;
};
state.currentSessionId =
typeof record.sessionId === "string" ? record.sessionId : null;
state.sessionInfo.thinkingLevel =
record.thinkingLevel ?? state.sessionInfo.thinkingLevel;
state.currentSessionId = typeof record.sessionId === "string" ? record.sessionId : null;
state.sessionInfo.thinkingLevel = record.thinkingLevel ?? state.sessionInfo.thinkingLevel;
chatLog.clearAll();
chatLog.addSystem(`session ${state.currentSessionKey}`);
for (const entry of record.messages ?? []) {

View File

@@ -19,9 +19,7 @@ export function formatStatusSummary(summary: GatewayStatusSummary) {
lines.push(`${linkLabel}: ${linked ? "linked" : "not linked"}${authAge}`);
}
const providerSummary = Array.isArray(summary.providerSummary)
? summary.providerSummary
: [];
const providerSummary = Array.isArray(summary.providerSummary) ? summary.providerSummary : [];
if (providerSummary.length > 0) {
lines.push("");
lines.push("System:");
@@ -49,14 +47,11 @@ export function formatStatusSummary(summary: GatewayStatusSummary) {
const sessionCount = summary.sessions?.count ?? 0;
lines.push(`Active sessions: ${sessionCount}`);
const recent = Array.isArray(summary.sessions?.recent)
? summary.sessions?.recent
: [];
const recent = Array.isArray(summary.sessions?.recent) ? summary.sessions?.recent : [];
if (recent.length > 0) {
lines.push("Recent sessions:");
for (const entry of recent) {
const ageLabel =
typeof entry.age === "number" ? formatAge(entry.age) : "no activity";
const ageLabel = typeof entry.age === "number" ? formatAge(entry.age) : "no activity";
const model = entry.model ?? "unknown";
const usage = formatContextUsageLine({
total: entry.totalTokens ?? null,
@@ -64,18 +59,14 @@ export function formatStatusSummary(summary: GatewayStatusSummary) {
remaining: entry.remainingTokens ?? null,
percent: entry.percentUsed ?? null,
});
const flags = entry.flags?.length
? ` | flags: ${entry.flags.join(", ")}`
: "";
const flags = entry.flags?.length ? ` | flags: ${entry.flags.join(", ")}` : "";
lines.push(
`- ${entry.key}${entry.kind ? ` [${entry.kind}]` : ""} | ${ageLabel} | model ${model} | ${usage}${flags}`,
);
}
}
const queued = Array.isArray(summary.queuedSystemEvents)
? summary.queuedSystemEvents
: [];
const queued = Array.isArray(summary.queuedSystemEvents) ? summary.queuedSystemEvents : [];
if (queued.length > 0) {
const preview = queued.slice(0, 3).join(" | ");
lines.push(`Queued system events (${queued.length}): ${preview}`);

View File

@@ -4,9 +4,7 @@ import { resolveFinalAssistantText } from "./tui.js";
describe("resolveFinalAssistantText", () => {
it("falls back to streamed text when final text is empty", () => {
expect(
resolveFinalAssistantText({ finalText: "", streamedText: "Hello" }),
).toBe("Hello");
expect(resolveFinalAssistantText({ finalText: "", streamedText: "Hello" })).toBe("Hello");
});
it("prefers the final text when present", () => {

View File

@@ -37,8 +37,7 @@ export type { TuiOptions } from "./tui-types.js";
export async function runTui(opts: TuiOptions) {
const config = loadConfig();
const initialSessionInput = (opts.session ?? "").trim();
let sessionScope: SessionScope = (config.session?.scope ??
"per-sender") as SessionScope;
let sessionScope: SessionScope = (config.session?.scope ?? "per-sender") as SessionScope;
let sessionMainKey = normalizeMainKey(config.session?.mainKey);
let agentDefaultId = resolveDefaultAgentId(config);
let currentAgentId = agentDefaultId;
@@ -256,9 +255,7 @@ export async function runTui(opts: TuiOptions) {
};
const renderStatus = () => {
const text = activityStatus
? `${connectionStatus} | ${activityStatus}`
: connectionStatus;
const text = activityStatus ? `${connectionStatus} | ${activityStatus}` : connectionStatus;
setStatus(text);
};
@@ -290,19 +287,12 @@ export async function runTui(opts: TuiOptions) {
? `${sessionInfo.modelProvider}/${sessionInfo.model}`
: sessionInfo.model
: "unknown";
const tokens = formatTokens(
sessionInfo.totalTokens ?? null,
sessionInfo.contextTokens ?? null,
);
const tokens = formatTokens(sessionInfo.totalTokens ?? null, sessionInfo.contextTokens ?? null);
const think = sessionInfo.thinkingLevel ?? "off";
const verbose = sessionInfo.verboseLevel ?? "off";
const reasoning = sessionInfo.reasoningLevel ?? "off";
const reasoningLabel =
reasoning === "on"
? "reasoning"
: reasoning === "stream"
? "reasoning:stream"
: null;
reasoning === "on" ? "reasoning" : reasoning === "stream" ? "reasoning:stream" : null;
footer.setText(
theme.dim(
`agent ${agentLabel} | session ${sessionLabel} | ${modelLabel} | think ${think} | verbose ${verbose}${reasoningLabel ? ` | ${reasoningLabel}` : ""} | ${tokens}`,
@@ -342,13 +332,8 @@ export async function runTui(opts: TuiOptions) {
updateAutocompleteProvider,
setActivityStatus,
});
const {
refreshAgents,
refreshSessionInfo,
loadHistory,
setSession,
abortActive,
} = sessionActions;
const { refreshAgents, refreshSessionInfo, loadHistory, setSession, abortActive } =
sessionActions;
const { handleChatEvent, handleAgentEvent } = createEventHandlers({
chatLog,
@@ -357,29 +342,24 @@ export async function runTui(opts: TuiOptions) {
setActivityStatus,
});
const {
handleCommand,
sendMessage,
openModelSelector,
openAgentSelector,
openSessionSelector,
} = createCommandHandlers({
client,
chatLog,
tui,
opts,
state,
deliverDefault,
openOverlay,
closeOverlay,
refreshSessionInfo,
loadHistory,
setSession,
refreshAgents,
abortActive,
setActivityStatus,
formatSessionKey,
});
const { handleCommand, sendMessage, openModelSelector, openAgentSelector, openSessionSelector } =
createCommandHandlers({
client,
chatLog,
tui,
opts,
state,
deliverDefault,
openOverlay,
closeOverlay,
refreshSessionInfo,
loadHistory,
setSession,
refreshAgents,
abortActive,
setActivityStatus,
formatSessionKey,
});
updateAutocompleteProvider();
editor.onSubmit = (text) => {
@@ -475,10 +455,7 @@ export async function runTui(opts: TuiOptions) {
};
client.onGap = (info) => {
setConnectionStatus(
`event gap: expected ${info.expected}, got ${info.received}`,
5000,
);
setConnectionStatus(`event gap: expected ${info.expected}, got ${info.received}`, 5000);
tui.requestRender();
};