diff --git a/.prettierignore b/.prettierignore deleted file mode 100644 index 9c6fd3365..000000000 --- a/.prettierignore +++ /dev/null @@ -1,13 +0,0 @@ -# Vendor and dependencies -**/vendor/** -**/node_modules/** - -# Generated files -**/test-sessions.ts -**/models.generated.ts -**/template.generated.ts -**/gen/agent_pb.ts - -# Build outputs -**/dist/** -**/build/** diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index 824a60285..000000000 --- a/.prettierrc +++ /dev/null @@ -1,11 +0,0 @@ -{ - "useTabs": true, - "tabWidth": 3, - "printWidth": 120, - "endOfLine": "lf", - "semi": true, - "singleQuote": false, - "trailingComma": "all", - "bracketSpacing": true, - "arrowParens": "always" -} diff --git a/biome.json b/biome.json index abe534405..c5715eac2 100644 --- a/biome.json +++ b/biome.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/2.3.11/schema.json", + "$schema": "https://biomejs.dev/schemas/2.3.12/schema.json", "linter": { "enabled": true, "rules": { @@ -29,14 +29,14 @@ "quoteStyle": "double", "trailingCommas": "all", "bracketSpacing": true, - "arrowParentheses": "always" + "arrowParentheses": "asNeeded" } }, "files": { "includes": [ "packages/*/src/**/*.ts", "packages/*/test/**/*.ts", - "packages/coding-agent/examples/**/*.ts", + "packages/*/examples/**/*.ts", "!**/vendor/**/*", "!**/node_modules/**/*", "!**/test-sessions.ts", diff --git a/bun.lock b/bun.lock index 84344bfb9..a88134b44 100644 --- a/bun.lock +++ b/bun.lock @@ -9,7 +9,7 @@ "get-east-asian-width": "^1.4.0", }, "devDependencies": { - "@biomejs/biome": "^2.3.12", + "@biomejs/biome": "2.3.12", "@bufbuild/protoc-gen-es": "^2.10.2", "@types/bun": "^1.2.17", "@types/node": "^25.0.10", diff --git a/package.json b/package.json index c4b73354a..f7698e80f 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "release": "bun scripts/release.ts" }, "devDependencies": { - "@biomejs/biome": "^2.3.12", + "@biomejs/biome": "2.3.12", "@bufbuild/protoc-gen-es": "^2.10.2", "@types/bun": "^1.2.17", "@types/node": "^25.0.10", diff --git a/packages/agent/src/agent-loop.ts b/packages/agent/src/agent-loop.ts index f112661cc..8ad3f37cd 100644 --- a/packages/agent/src/agent-loop.ts +++ b/packages/agent/src/agent-loop.ts @@ -2,7 +2,6 @@ * Agent loop that works with AgentMessage throughout. * Transforms to Message[] only at the LLM call boundary. */ - import { type AssistantMessage, type Context, @@ -107,12 +106,12 @@ function normalizeMessagesForProvider( } let changed = false; - const normalized = messages.map((message) => { + const normalized = messages.map(message => { if (message.role !== "assistant" || !Array.isArray(message.content)) { return message; } - const filtered = message.content.filter((block) => block.type !== "thinking"); + const filtered = message.content.filter(block => block.type !== "thinking"); if (filtered.length === message.content.length) { return message; } @@ -186,7 +185,7 @@ async function runLoop( } // Check for tool calls - const toolCalls = message.content.filter((c) => c.type === "toolCall"); + const toolCalls = message.content.filter(c => c.type === "toolCall"); hasMoreToolCalls = toolCalls.length > 0; const toolResults: ToolResultMessage[] = []; @@ -373,16 +372,16 @@ async function executeToolCalls( getToolContext?: AgentLoopConfig["getToolContext"], interruptMode: AgentLoopConfig["interruptMode"] = "immediate", ): Promise<{ toolResults: ToolResultMessage[]; steeringMessages?: AgentMessage[] }> { - const toolCalls = assistantMessage.content.filter((c) => c.type === "toolCall"); + const toolCalls = assistantMessage.content.filter(c => c.type === "toolCall"); const results: ToolResultMessage[] = []; let steeringMessages: AgentMessage[] | undefined; const shouldInterruptImmediately = interruptMode !== "wait"; - const toolCallInfos = toolCalls.map((call) => ({ id: call.id, name: call.name })); + const toolCallInfos = toolCalls.map(call => ({ id: call.id, name: call.name })); const batchId = `${assistantMessage.timestamp ?? Date.now()}_${toolCalls[0]?.id ?? "batch"}`; for (let index = 0; index < toolCalls.length; index++) { const toolCall = toolCalls[index]; - const tool = tools?.find((t) => t.name === toolCall.name); + const tool = tools?.find(t => t.name === toolCall.name); stream.push({ type: "tool_execution_start", @@ -411,7 +410,7 @@ async function executeToolCalls( toolCall.id, validatedArgs, tool.nonAbortable ? undefined : signal, - (partialResult) => { + partialResult => { stream.push({ type: "tool_execution_update", toolCallId: toolCall.id, diff --git a/packages/agent/src/agent.ts b/packages/agent/src/agent.ts index cd8e75e93..891f7b6e7 100644 --- a/packages/agent/src/agent.ts +++ b/packages/agent/src/agent.ts @@ -2,7 +2,6 @@ * Agent class that uses the agent-loop directly. * No transport abstraction - calls streamSimple via the loop. */ - import { type AssistantMessage, type CursorExecHandlers, @@ -34,7 +33,7 @@ import type { * Default convertToLlm: Keep only LLM-compatible messages, convert attachments. */ function defaultConvertToLlm(messages: AgentMessage[]): Message[] { - return messages.filter((m) => m.role === "user" || m.role === "assistant" || m.role === "toolResult"); + return messages.filter(m => m.role === "user" || m.role === "assistant" || m.role === "toolResult"); } export interface AgentOptions { @@ -420,7 +419,7 @@ export class Agent { const model = this._state.model; if (!model) throw new Error("No model configured"); - this.runningPrompt = new Promise((resolve) => { + this.runningPrompt = new Promise(resolve => { this.resolveRunningPrompt = resolve; }); @@ -569,7 +568,7 @@ export class Agent { // Handle any remaining partial message if (partial && partial.role === "assistant" && partial.content.length > 0) { const onlyEmpty = !partial.content.some( - (c) => + c => (c.type === "thinking" && c.thinking.trim().length > 0) || (c.type === "text" && c.text.trim().length > 0) || (c.type === "toolCall" && c.name.trim().length > 0), @@ -655,7 +654,7 @@ export class Agent { } // Find the split point: minimum text length at first tool call - const splitPoint = Math.min(...buffer.map((r) => r.textLengthAtCall)); + const splitPoint = Math.min(...buffer.map(r => r.textLengthAtCall)); // Extract text content from assistant message const content = assistantMessage.content; @@ -687,7 +686,7 @@ export class Agent { const continuationText = fullText.slice(splitPoint); // Create preamble message (text before tools) - const preambleContent = content.map((block) => { + const preambleContent = content.map(block => { if (block.type === "text") { return { ...block, text: preambleText }; } diff --git a/packages/agent/src/proxy.ts b/packages/agent/src/proxy.ts index c75748abe..da8403d9b 100644 --- a/packages/agent/src/proxy.ts +++ b/packages/agent/src/proxy.ts @@ -2,7 +2,6 @@ * Proxy stream function for apps that route LLM calls through a server. * The server manages auth and proxies requests to LLM providers. */ - import { type AssistantMessage, type AssistantMessageEvent, @@ -20,8 +19,8 @@ import { readSseEvents } from "@oh-my-pi/pi-utils"; class ProxyMessageEventStream extends EventStream { constructor() { super( - (event) => event.type === "done" || event.type === "error", - (event) => { + event => event.type === "done" || event.type === "error", + event => { if (event.type === "done") return event.message; if (event.type === "error") return event.error; throw new Error("Unexpected event type"); diff --git a/packages/agent/test/agent-loop.test.ts b/packages/agent/test/agent-loop.test.ts index 17796506f..a594d69c9 100644 --- a/packages/agent/test/agent-loop.test.ts +++ b/packages/agent/test/agent-loop.test.ts @@ -23,8 +23,8 @@ import { Type } from "@sinclair/typebox"; class MockAssistantStream extends EventStream { constructor() { super( - (event) => event.type === "done" || event.type === "error", - (event) => { + event => event.type === "done" || event.type === "error", + event => { if (event.type === "done") return event.message; if (event.type === "error") return event.error; throw new Error("Unexpected event type"); @@ -85,7 +85,7 @@ function createUserMessage(text: string): UserMessage { // Simple identity converter for tests - just passes through standard messages function identityConverter(messages: AgentMessage[]): Message[] { - return messages.filter((m) => m.role === "user" || m.role === "assistant" || m.role === "toolResult") as Message[]; + return messages.filter(m => m.role === "user" || m.role === "assistant" || m.role === "toolResult") as Message[]; } describe("agentLoop with AgentMessage", () => { @@ -127,7 +127,7 @@ describe("agentLoop with AgentMessage", () => { expect(messages[1].role).toBe("assistant"); // Verify event sequence - const eventTypes = events.map((e) => e.type); + const eventTypes = events.map(e => e.type); expect(eventTypes).toContain("agent_start"); expect(eventTypes).toContain("turn_start"); expect(eventTypes).toContain("message_start"); @@ -161,11 +161,11 @@ describe("agentLoop with AgentMessage", () => { let convertedMessages: Message[] = []; const config: AgentLoopConfig = { model: createModel(), - convertToLlm: (messages) => { + convertToLlm: messages => { // Filter out notifications, convert rest convertedMessages = messages - .filter((m) => (m as { role: string }).role !== "notification") - .filter((m) => m.role === "user" || m.role === "assistant" || m.role === "toolResult") as Message[]; + .filter(m => (m as { role: string }).role !== "notification") + .filter(m => m.role === "user" || m.role === "assistant" || m.role === "toolResult") as Message[]; return convertedMessages; }, }; @@ -210,14 +210,14 @@ describe("agentLoop with AgentMessage", () => { const config: AgentLoopConfig = { model: createModel(), - transformContext: async (messages) => { + transformContext: async messages => { // Keep only last 2 messages (prune old ones) transformedMessages = messages.slice(-2); return transformedMessages; }, - convertToLlm: (messages) => { + convertToLlm: messages => { convertedMessages = messages.filter( - (m) => m.role === "user" || m.role === "assistant" || m.role === "toolResult", + m => m.role === "user" || m.role === "assistant" || m.role === "toolResult", ) as Message[]; return convertedMessages; }, @@ -275,7 +275,7 @@ describe("agentLoop with AgentMessage", () => { const config: AgentLoopConfig = { model: createModel(), convertToLlm: identityConverter, - getToolContext: (toolCall) => ({ toolCall }) as AgentToolContext, + getToolContext: toolCall => ({ toolCall }) as AgentToolContext, }; let callIndex = 0; @@ -379,8 +379,8 @@ describe("agentLoop with AgentMessage", () => { expect(executed).toEqual(["hello"]); // Should have tool execution events - const toolStart = events.find((e) => e.type === "tool_execution_start"); - const toolEnd = events.find((e) => e.type === "tool_execution_end"); + const toolStart = events.find(e => e.type === "tool_execution_start"); + const toolEnd = events.find(e => e.type === "tool_execution_end"); expect(toolStart).toBeDefined(); expect(toolEnd).toBeDefined(); if (toolEnd?.type === "tool_execution_end") { @@ -436,7 +436,7 @@ describe("agentLoop with AgentMessage", () => { // Check if interrupt message is in context on second call if (callIndex === 1) { sawInterruptInContext = ctx.messages.some( - (m) => m.role === "user" && typeof m.content === "string" && m.content === "interrupt", + m => m.role === "user" && typeof m.content === "string" && m.content === "interrupt", ); } @@ -482,7 +482,7 @@ describe("agentLoop with AgentMessage", () => { // Queued message should appear in events const queuedMessageEvent = events.find( - (e) => + e => e.type === "message_start" && e.message.role === "user" && typeof e.message.content === "string" && @@ -548,7 +548,7 @@ describe("agentLoopContinue with AgentMessage", () => { expect(messages[0].role).toBe("assistant"); // Should NOT have user message events (that's the key difference from agentLoop) - const messageEndEvents = events.filter((e) => e.type === "message_end"); + const messageEndEvents = events.filter(e => e.type === "message_end"); expect(messageEndEvents.length).toBe(1); expect((messageEndEvents[0] as any).message.role).toBe("assistant"); }); @@ -575,10 +575,10 @@ describe("agentLoopContinue with AgentMessage", () => { const config: AgentLoopConfig = { model: createModel(), - convertToLlm: (messages) => { + convertToLlm: messages => { // Convert hookMessage to user message return messages - .map((m) => { + .map(m => { if ((m as any).role === "hookMessage") { return { role: "user" as const, @@ -588,7 +588,7 @@ describe("agentLoopContinue with AgentMessage", () => { } return m; }) - .filter((m) => m.role === "user" || m.role === "assistant" || m.role === "toolResult") as Message[]; + .filter(m => m.role === "user" || m.role === "assistant" || m.role === "toolResult") as Message[]; }, }; diff --git a/packages/agent/test/agent.test.ts b/packages/agent/test/agent.test.ts index d62771b7e..4c08cd57a 100644 --- a/packages/agent/test/agent.test.ts +++ b/packages/agent/test/agent.test.ts @@ -12,8 +12,8 @@ import { class MockAssistantStream extends EventStream { constructor() { super( - (event) => event.type === "done" || event.type === "error", - (event) => { + event => event.type === "done" || event.type === "error", + event => { if (event.type === "done") return event.message; if (event.type === "error") return event.error; throw new Error("Unexpected event type"); @@ -84,7 +84,7 @@ describe("Agent", () => { const agent = new Agent(); let eventCount = 0; - const unsubscribe = agent.subscribe((_event) => { + const unsubscribe = agent.subscribe(_event => { eventCount++; }); diff --git a/packages/agent/test/e2e.test.ts b/packages/agent/test/e2e.test.ts index 051fbf742..77ed13320 100644 --- a/packages/agent/test/e2e.test.ts +++ b/packages/agent/test/e2e.test.ts @@ -25,7 +25,7 @@ async function basicPrompt(model: Model) { if (assistantMessage.role !== "assistant") throw new Error("Expected assistant message"); expect(assistantMessage.content.length).toBeGreaterThan(0); - const textContent = assistantMessage.content.find((c) => c.type === "text"); + const textContent = assistantMessage.content.find(c => c.type === "text"); expect(textContent).toBeDefined(); if (textContent?.type !== "text") throw new Error("Expected text content"); expect(textContent.text).toContain("4"); @@ -46,12 +46,12 @@ async function toolExecution(model: Model) { expect(agent.state.isStreaming).toBe(false); expect(agent.state.messages.length).toBeGreaterThanOrEqual(3); - const toolResultMsg = agent.state.messages.find((m) => m.role === "toolResult"); + const toolResultMsg = agent.state.messages.find(m => m.role === "toolResult"); expect(toolResultMsg).toBeDefined(); if (toolResultMsg?.role !== "toolResult") throw new Error("Expected tool result message"); const textContent = toolResultMsg.content - ?.filter((c) => c.type === "text") + ?.filter(c => c.type === "text") .map((c: any) => c.text) .join("\n") || ""; expect(textContent).toBeDefined(); @@ -61,7 +61,7 @@ async function toolExecution(model: Model) { const finalMessage = agent.state.messages[agent.state.messages.length - 1]; if (finalMessage.role !== "assistant") throw new Error("Expected final assistant message"); - const finalText = finalMessage.content.find((c) => c.type === "text"); + const finalText = finalMessage.content.find(c => c.type === "text"); expect(finalText).toBeDefined(); if (finalText?.type !== "text") throw new Error("Expected text content"); // Check for number with or without comma formatting @@ -113,7 +113,7 @@ async function stateUpdates(model: Model) { const events: Array = []; - agent.subscribe((event) => { + agent.subscribe(event => { events.push(event.type); }); @@ -125,7 +125,7 @@ async function stateUpdates(model: Model) { expect(events).toContain("message_start"); expect(events).toContain("message_end"); // May have message_update events during streaming - const hasMessageUpdates = events.some((e) => e === "message_update"); + const hasMessageUpdates = events.some(e => e === "message_update"); expect(hasMessageUpdates).toBe(true); // Check final state @@ -151,7 +151,7 @@ async function multiTurnConversation(model: Model) { const lastMessage = agent.state.messages[3]; if (lastMessage.role !== "assistant") throw new Error("Expected assistant message"); - const lastText = lastMessage.content.find((c) => c.type === "text"); + const lastText = lastMessage.content.find(c => c.type === "text"); if (lastText?.type !== "text") throw new Error("Expected text content"); expect(lastText.text.toLowerCase()).toContain("alice"); } @@ -400,7 +400,7 @@ describe("Agent.continue()", () => { expect(agent.state.messages[1].role).toBe("assistant"); const assistantMsg = agent.state.messages[1] as AssistantMessage; - const textContent = assistantMsg.content.find((c) => c.type === "text"); + const textContent = assistantMsg.content.find(c => c.type === "text"); expect(textContent).toBeDefined(); if (textContent?.type === "text") { expect(textContent.text.toUpperCase()).toContain("HELLO WORLD"); @@ -473,8 +473,8 @@ describe("Agent.continue()", () => { if (lastMessage.role === "assistant") { const textContent = lastMessage.content - .filter((c) => c.type === "text") - .map((c) => (c as { type: "text"; text: string }).text) + .filter(c => c.type === "text") + .map(c => (c as { type: "text"; text: string }).text) .join(" "); // Should mention 8 in the response expect(textContent).toMatch(/8/); diff --git a/packages/ai/src/cli.ts b/packages/ai/src/cli.ts index 5194dfb06..9ee2a3839 100755 --- a/packages/ai/src/cli.ts +++ b/packages/ai/src/cli.ts @@ -91,7 +91,7 @@ async function login(provider: OAuthProvider): Promise { case "cursor": credentials = await loginCursor( - (url) => { + url => { console.log(`\nOpen this URL in your browser:\n${url}\n`); }, () => { @@ -243,7 +243,7 @@ Examples: provider = PROVIDERS[index].id; } - if (!PROVIDERS.some((p) => p.id === provider)) { + if (!PROVIDERS.some(p => p.id === provider)) { console.error(`Unknown provider: ${provider}`); console.error(`Use 'bunx @oh-my-pi/pi-ai list' to see available providers`); process.exit(1); @@ -259,7 +259,7 @@ Examples: process.exit(1); } -main().catch((err) => { +main().catch(err => { console.error("Error:", err.message); process.exit(1); }); diff --git a/packages/ai/src/providers/amazon-bedrock.ts b/packages/ai/src/providers/amazon-bedrock.ts index acffc172d..1b9856fd2 100644 --- a/packages/ai/src/providers/amazon-bedrock.ts +++ b/packages/ai/src/providers/amazon-bedrock.ts @@ -17,7 +17,6 @@ import { type ToolConfiguration, ToolResultStatus, } from "@aws-sdk/client-bedrock-runtime"; - import { calculateCost } from "../models"; import type { Api, @@ -200,7 +199,7 @@ function handleContentBlockDelta( ): void { const contentBlockIndex = event.contentBlockIndex!; const delta = event.delta; - let index = blocks.findIndex((b) => b.index === contentBlockIndex); + let index = blocks.findIndex(b => b.index === contentBlockIndex); let block = blocks[index]; if (delta?.text !== undefined) { @@ -271,7 +270,7 @@ function handleContentBlockStop( output: AssistantMessage, stream: AssistantMessageEventStream, ): void { - const index = blocks.findIndex((b) => b.index === event.contentBlockIndex); + const index = blocks.findIndex(b => b.index === event.contentBlockIndex); const block = blocks[index]; if (!block) return; delete (block as Block).index; @@ -351,7 +350,7 @@ function convertMessages(context: Context, model: Model<"bedrock-converse-stream }); } else { const contentBlocks = m.content - .map((c) => { + .map(c => { switch (c.type) { case "text": return { text: sanitizeSurrogates(c.text) }; @@ -361,7 +360,7 @@ function convertMessages(context: Context, model: Model<"bedrock-converse-stream throw new Error("Unknown user content type"); } }) - .filter((block) => { + .filter(block => { // Filter out empty text blocks if ("text" in block && block.text) { return block.text.trim().length > 0; @@ -442,7 +441,7 @@ function convertMessages(context: Context, model: Model<"bedrock-converse-stream toolResults.push({ toolResult: { toolUseId: sanitizeToolCallId(m.toolCallId), - content: m.content.map((c) => + content: m.content.map(c => c.type === "image" ? { image: createImageBlock(c.mimeType, c.data) } : { text: sanitizeSurrogates(c.text) }, @@ -458,7 +457,7 @@ function convertMessages(context: Context, model: Model<"bedrock-converse-stream toolResults.push({ toolResult: { toolUseId: sanitizeToolCallId(nextMsg.toolCallId), - content: nextMsg.content.map((c) => + content: nextMsg.content.map(c => c.type === "image" ? { image: createImageBlock(c.mimeType, c.data) } : { text: sanitizeSurrogates(c.text) }, @@ -500,7 +499,7 @@ function convertToolConfig( ): ToolConfiguration | undefined { if (!tools?.length || toolChoice === "none") return undefined; - const bedrockTools: BedrockTool[] = tools.map((tool) => ({ + const bedrockTools: BedrockTool[] = tools.map(tool => ({ toolSpec: { name: tool.name, description: tool.description, diff --git a/packages/ai/src/providers/anthropic.ts b/packages/ai/src/providers/anthropic.ts index 4c72ef266..5ae7df5af 100644 --- a/packages/ai/src/providers/anthropic.ts +++ b/packages/ai/src/providers/anthropic.ts @@ -26,7 +26,6 @@ import { AssistantMessageEventStream } from "../utils/event-stream"; import { parseStreamingJson } from "../utils/json-parse"; import { formatErrorMessageWithRetryAfter } from "../utils/retry-after"; import { sanitizeSurrogates } from "../utils/sanitize-unicode"; - import { transformMessages } from "./transform-messages"; // Stealth mode: Mimic Claude Code headers and tool prefixing. @@ -89,13 +88,13 @@ function convertContentBlocks(content: (TextContent | ImageContent)[]): } > { // If only text blocks, return as concatenated string for simplicity - const hasImages = content.some((c) => c.type === "image"); + const hasImages = content.some(c => c.type === "image"); if (!hasImages) { - return sanitizeSurrogates(content.map((c) => (c as TextContent).text).join("\n")); + return sanitizeSurrogates(content.map(c => (c as TextContent).text).join("\n")); } // If we have images, convert to content block array - const blocks = content.map((block) => { + const blocks = content.map(block => { if (block.type === "text") { return { type: "text" as const, @@ -113,7 +112,7 @@ function convertContentBlocks(content: (TextContent | ImageContent)[]): }); // If only images (no text), add placeholder text block - const hasText = blocks.some((b) => b.type === "text"); + const hasText = blocks.some(b => b.type === "text"); if (!hasText) { blocks.unshift({ type: "text" as const, @@ -218,7 +217,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = ( } } else if (event.type === "content_block_delta") { if (event.delta.type === "text_delta") { - const index = blocks.findIndex((b) => b.index === event.index); + const index = blocks.findIndex(b => b.index === event.index); const block = blocks[index]; if (block && block.type === "text") { block.text += event.delta.text; @@ -230,7 +229,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = ( }); } } else if (event.delta.type === "thinking_delta") { - const index = blocks.findIndex((b) => b.index === event.index); + const index = blocks.findIndex(b => b.index === event.index); const block = blocks[index]; if (block && block.type === "thinking") { block.thinking += event.delta.thinking; @@ -242,7 +241,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = ( }); } } else if (event.delta.type === "input_json_delta") { - const index = blocks.findIndex((b) => b.index === event.index); + const index = blocks.findIndex(b => b.index === event.index); const block = blocks[index]; if (block && block.type === "toolCall") { block.partialJson += event.delta.partial_json; @@ -255,7 +254,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = ( }); } } else if (event.delta.type === "signature_delta") { - const index = blocks.findIndex((b) => b.index === event.index); + const index = blocks.findIndex(b => b.index === event.index); const block = blocks[index]; if (block && block.type === "thinking") { block.thinkingSignature = block.thinkingSignature || ""; @@ -263,7 +262,7 @@ export const streamAnthropic: StreamFunction<"anthropic-messages"> = ( } } } else if (event.type === "content_block_stop") { - const index = blocks.findIndex((b) => b.index === event.index); + const index = blocks.findIndex(b => b.index === event.index); const block = blocks[index]; if (block) { delete (block as any).index; @@ -360,7 +359,7 @@ function isAnthropicBaseUrl(baseUrl?: string): boolean { export function normalizeExtraBetas(betas?: string[] | string): string[] { if (!betas) return []; const raw = Array.isArray(betas) ? betas : betas.split(","); - return raw.map((beta) => beta.trim()).filter((beta) => beta.length > 0); + return raw.map(beta => beta.trim()).filter(beta => beta.length > 0); } // Build deduplicated beta header string @@ -406,7 +405,7 @@ export function buildAnthropicHeaders(options: AnthropicHeaderOptions): Record key.toLowerCase()), + ].map(key => key.toLowerCase()), ); const modelHeaders = Object.fromEntries( Object.entries(options.modelHeaders ?? {}).filter(([key]) => !enforcedHeaderKeys.has(key.toLowerCase())), @@ -687,7 +686,7 @@ function applyPromptCaching(params: MessageCreateParamsStreaming): void { // 3. Cache penultimate user message for conversation history caching const userIndexes = params.messages .map((message, index) => (message.role === "user" ? index : -1)) - .filter((index) => index >= 0); + .filter(index => index >= 0); if (userIndexes.length >= 2) { const penultimateUserIndex = userIndexes[userIndexes.length - 2]; @@ -748,7 +747,7 @@ function convertMessages( }); } } else if (Array.isArray(msg.content)) { - const blocks: Array = msg.content.map((item) => { + const blocks: Array = msg.content.map(item => { if (item.type === "text") { return { type: "text", @@ -764,8 +763,8 @@ function convertMessages( }, }; }); - let filteredBlocks = !model?.input.includes("image") ? blocks.filter((b) => b.type !== "image") : blocks; - filteredBlocks = filteredBlocks.filter((b) => { + let filteredBlocks = !model?.input.includes("image") ? blocks.filter(b => b.type !== "image") : blocks; + filteredBlocks = filteredBlocks.filter(b => { if (b.type === "text") { return b.text.trim().length > 0; } @@ -786,7 +785,7 @@ function convertMessages( // block with a missing/invalid signature (e.g., from aborted stream), we must skip // the entire message to avoid API rejection. Checking the first non-empty block. const firstContentBlock = msg.content.find( - (b) => + b => (b.type === "text" && b.text.trim().length > 0) || (b.type === "thinking" && b.thinking.trim().length > 0) || b.type === "toolCall", @@ -889,7 +888,7 @@ function convertMessages( } // Final validation: filter out any messages with invalid content - return params.filter((msg) => { + return params.filter(msg => { if (!msg.content) return false; if (typeof msg.content === "string") return msg.content.length > 0; if (Array.isArray(msg.content)) return msg.content.length > 0; @@ -900,7 +899,7 @@ function convertMessages( function convertTools(tools: Tool[], isOAuthToken: boolean): Anthropic.Messages.Tool[] { if (!tools) return []; - return tools.map((tool) => { + return tools.map(tool => { const jsonSchema = tool.parameters as any; // TypeBox already generates JSON Schema return { diff --git a/packages/ai/src/providers/cursor.ts b/packages/ai/src/providers/cursor.ts index 17ea1e76e..18dcd60b8 100644 --- a/packages/ai/src/providers/cursor.ts +++ b/packages/ai/src/providers/cursor.ts @@ -245,7 +245,7 @@ function decodeLogData(value: unknown): unknown { return value; } if (Array.isArray(value)) { - return value.map((entry) => decodeLogData(entry)); + return value.map(entry => decodeLogData(entry)); } const record = value as Record; const typeName = record.$typeName; @@ -375,13 +375,13 @@ export const streamCursor: StreamFunction<"cursor-agent"> = ( get firstTokenTime() { return firstTokenTime; }, - setTextBlock: (b) => { + setTextBlock: b => { currentTextBlock = b; }, - setThinkingBlock: (b) => { + setThinkingBlock: b => { currentThinkingBlock = b; }, - setToolCall: (t) => { + setToolCall: t => { currentToolCall = t; }, setFirstTokenTime: () => { @@ -427,7 +427,7 @@ export const streamCursor: StreamFunction<"cursor-agent"> = ( usageState, requestContextTools, onConversationCheckpoint, - ).catch((error) => { + ).catch(error => { log("error", "handleServerMessage", { error: String(error) }); }); } catch (e) { @@ -452,7 +452,7 @@ export const streamCursor: StreamFunction<"cursor-agent"> = ( heartbeatTimer = setInterval(sendHeartbeat, 5000); await new Promise((resolve, reject) => { - h2Request!.on("trailers", (trailers) => { + h2Request!.on("trailers", trailers => { const status = trailers["grpc-status"]; const msg = trailers["grpc-message"]; if (status && status !== "0") { @@ -662,9 +662,9 @@ async function handleShellStreamArgs( args as any, execHandlers?.shell, onToolResult, - (toolResult) => buildShellResultFromToolResult(args as any, toolResult), - (reason) => buildShellRejectedResult((args as any).command, (args as any).workingDirectory, reason), - (error) => buildShellFailureResult((args as any).command, (args as any).workingDirectory, error), + toolResult => buildShellResultFromToolResult(args as any, toolResult), + reason => buildShellRejectedResult((args as any).command, (args as any).workingDirectory, reason), + error => buildShellFailureResult((args as any).command, (args as any).workingDirectory, error), ); sendShellStreamEvent(h2Request, execMsg, { case: "start", value: create(ShellStreamStartSchema, {}) }); @@ -810,9 +810,9 @@ async function handleExecServerMessage( args, execHandlers?.read, onToolResult, - (toolResult) => buildReadResultFromToolResult(args.path, toolResult), - (reason) => buildReadRejectedResult(args.path, reason), - (error) => buildReadErrorResult(args.path, error), + toolResult => buildReadResultFromToolResult(args.path, toolResult), + reason => buildReadRejectedResult(args.path, reason), + error => buildReadErrorResult(args.path, error), ); sendExecClientMessage(h2Request, execMsg, "readResult", execResult); return; @@ -823,9 +823,9 @@ async function handleExecServerMessage( args, execHandlers?.ls, onToolResult, - (toolResult) => buildLsResultFromToolResult(args.path, toolResult), - (reason) => buildLsRejectedResult(args.path, reason), - (error) => buildLsErrorResult(args.path, error), + toolResult => buildLsResultFromToolResult(args.path, toolResult), + reason => buildLsRejectedResult(args.path, reason), + error => buildLsErrorResult(args.path, error), ); sendExecClientMessage(h2Request, execMsg, "lsResult", execResult); return; @@ -836,9 +836,9 @@ async function handleExecServerMessage( args, execHandlers?.grep, onToolResult, - (toolResult) => buildGrepResultFromToolResult(args, toolResult), - (reason) => buildGrepErrorResult(reason), - (error) => buildGrepErrorResult(error), + toolResult => buildGrepResultFromToolResult(args, toolResult), + reason => buildGrepErrorResult(reason), + error => buildGrepErrorResult(error), ); sendExecClientMessage(h2Request, execMsg, "grepResult", execResult); return; @@ -849,7 +849,7 @@ async function handleExecServerMessage( args, execHandlers?.write, onToolResult, - (toolResult) => + toolResult => buildWriteResultFromToolResult( { path: args.path, @@ -859,8 +859,8 @@ async function handleExecServerMessage( }, toolResult, ), - (reason) => buildWriteRejectedResult(args.path, reason), - (error) => buildWriteErrorResult(args.path, error), + reason => buildWriteRejectedResult(args.path, reason), + error => buildWriteErrorResult(args.path, error), ); sendExecClientMessage(h2Request, execMsg, "writeResult", execResult); return; @@ -871,9 +871,9 @@ async function handleExecServerMessage( args, execHandlers?.delete, onToolResult, - (toolResult) => buildDeleteResultFromToolResult(args.path, toolResult), - (reason) => buildDeleteRejectedResult(args.path, reason), - (error) => buildDeleteErrorResult(args.path, error), + toolResult => buildDeleteResultFromToolResult(args.path, toolResult), + reason => buildDeleteRejectedResult(args.path, reason), + error => buildDeleteErrorResult(args.path, error), ); sendExecClientMessage(h2Request, execMsg, "deleteResult", execResult); return; @@ -884,9 +884,9 @@ async function handleExecServerMessage( args, execHandlers?.shell, onToolResult, - (toolResult) => buildShellResultFromToolResult(args, toolResult), - (reason) => buildShellRejectedResult(args.command, args.workingDirectory, reason), - (error) => buildShellFailureResult(args.command, args.workingDirectory, error), + toolResult => buildShellResultFromToolResult(args, toolResult), + reason => buildShellRejectedResult(args.command, args.workingDirectory, reason), + error => buildShellFailureResult(args.command, args.workingDirectory, error), ); sendExecClientMessage(h2Request, execMsg, "shellResult", execResult); return; @@ -944,9 +944,9 @@ async function handleExecServerMessage( args, execHandlers?.diagnostics, onToolResult, - (toolResult) => buildDiagnosticsResultFromToolResult(args.path, toolResult), - (reason) => buildDiagnosticsRejectedResult(args.path, reason), - (error) => buildDiagnosticsErrorResult(args.path, error), + toolResult => buildDiagnosticsResultFromToolResult(args.path, toolResult), + reason => buildDiagnosticsRejectedResult(args.path, reason), + error => buildDiagnosticsErrorResult(args.path, error), ); sendExecClientMessage(h2Request, execMsg, "diagnosticsResult", execResult); return; @@ -958,9 +958,9 @@ async function handleExecServerMessage( mcpCall, execHandlers?.mcp, onToolResult, - (toolResult) => buildMcpResultFromToolResult(mcpCall, toolResult), - (_reason) => buildMcpToolNotFoundResult(mcpCall), - (error) => buildMcpErrorResult(error), + toolResult => buildMcpResultFromToolResult(mcpCall, toolResult), + _reason => buildMcpToolNotFoundResult(mcpCall), + error => buildMcpErrorResult(error), ); sendExecClientMessage(h2Request, execMsg, "mcpResult", execResult); return; @@ -1075,7 +1075,7 @@ async function applyToolResultHandler( } function toolResultToText(toolResult: ToolResultMessage): string { - return toolResult.content.map((item) => (item.type === "text" ? item.text : `[${item.mimeType} image]`)).join("\n"); + return toolResult.content.map(item => (item.type === "text" ? item.text : `[${item.mimeType} image]`)).join("\n"); } function toolResultWasTruncated(toolResult: ToolResultMessage): boolean { @@ -1274,8 +1274,8 @@ function buildLsResultFromToolResult(path: string, toolResult: ToolResultMessage const rootPath = path || "."; const entries = text .split("\n") - .map((line) => line.trim()) - .filter((line) => line.length > 0 && !line.startsWith("[")); + .map(line => line.trim()) + .filter(line => line.length > 0 && !line.startsWith("[")); const childrenDirs: LsDirectoryTreeNode[] = []; const childrenFiles: LsDirectoryTreeNode_File[] = []; @@ -1346,8 +1346,8 @@ function buildGrepResultFromToolResult( const clientTruncated = toolResultDetailBoolean(toolResult, "truncated"); const lines = text .split("\n") - .map((line) => line.trimEnd()) - .filter((line) => line.length > 0 && !line.startsWith("[") && !line.toLowerCase().startsWith("no matches")); + .map(line => line.trimEnd()) + .filter(line => line.length > 0 && !line.startsWith("[") && !line.toLowerCase().startsWith("no matches")); const workspaceKey = args.path || "."; let unionResult: GrepUnionResult; @@ -1367,7 +1367,7 @@ function buildGrepResultFromToolResult( }); } else if (outputMode === "count") { const counts = lines - .map((line) => { + .map(line => { const separatorIndex = line.lastIndexOf(":"); if (separatorIndex === -1) { return null; @@ -1417,7 +1417,7 @@ function buildGrepResultFromToolResult( const matches = Array.from(matchMap.entries()).map(([file, matches]) => create(GrepFileMatchSchema, { file, - matches: matches.map((entry) => + matches: matches.map(entry => create(GrepContentMatchSchema, { lineNumber: entry.line, content: entry.content, @@ -1586,7 +1586,7 @@ function buildTodoWriteArgs(toolCall: CursorUpdateTodosToolCall): { const todos = toolCall.updateTodosToolCall?.args?.todos; if (!todos) return null; return { - todos: todos.map((todo) => ({ + todos: todos.map(todo => ({ id: typeof todo.id === "string" && todo.id.length > 0 ? todo.id : undefined, content: typeof todo.content === "string" ? todo.content : "", activeForm: typeof todo.content === "string" ? todo.content : "", @@ -1599,7 +1599,7 @@ function buildMcpResultFromToolResult(_mcpCall: CursorMcpCall, toolResult: ToolR if (toolResult.isError) { return buildMcpErrorResult(toolResultToText(toolResult) || "MCP tool failed"); } - const content = toolResult.content.map((item) => { + const content = toolResult.content.map(item => { if (item.type === "image") { return create(McpToolResultContentItemSchema, { content: { @@ -1810,12 +1810,12 @@ function buildMcpToolDefinitions(tools: Tool[] | undefined): McpToolDefinition[] return []; } - const advertisedTools = tools.filter((tool) => !CURSOR_NATIVE_TOOL_NAMES.has(tool.name)); + const advertisedTools = tools.filter(tool => !CURSOR_NATIVE_TOOL_NAMES.has(tool.name)); if (advertisedTools.length === 0) { return []; } - return advertisedTools.map((tool) => { + return advertisedTools.map(tool => { const jsonSchema = tool.parameters as Record | undefined; const schemaValue: JsonValue = jsonSchema && typeof jsonSchema === "object" @@ -1841,7 +1841,7 @@ function extractUserMessageText(msg: Message): string { if (typeof content === "string") return content.trim(); const text = content .filter((c): c is TextContent => c.type === "text") - .map((c) => c.text) + .map(c => c.text) .join("\n"); return text.trim(); } @@ -1854,7 +1854,7 @@ function extractAssistantMessageText(msg: Message): string { if (!Array.isArray(msg.content)) return ""; return msg.content .filter((c): c is TextContent => c.type === "text") - .map((c) => c.text) + .map(c => c.text) .join("\n"); } @@ -2007,7 +2007,7 @@ function buildGrpcRequest( // Build conversation turns from prior messages (excluding the last user message) const turns = buildConversationTurns(context.messages); - const hasMatchingPrompt = state.conversationState?.rootPromptMessagesJson?.some((entry) => + const hasMatchingPrompt = state.conversationState?.rootPromptMessagesJson?.some(entry => Buffer.from(entry).equals(systemPromptId), ); @@ -2064,7 +2064,7 @@ function buildGrpcRequest( const requestBytes = toBinary(AgentClientMessageSchema, clientMessage); - const toolNames = context.tools?.map((tool) => tool.name) ?? []; + const toolNames = context.tools?.map(tool => tool.name) ?? []; const detail = process.env.DEBUG_CURSOR === "2" ? ` ${JSON.stringify(clientMessage.message.value, debugReplacer, 2)?.slice(0, 2000)}` @@ -2082,6 +2082,6 @@ function buildGrpcRequest( function extractText(content: (TextContent | ImageContent)[]): string { return content .filter((c): c is TextContent => c.type === "text") - .map((c) => c.text) + .map(c => c.text) .join("\n"); } diff --git a/packages/ai/src/providers/google-gemini-cli.ts b/packages/ai/src/providers/google-gemini-cli.ts index e8d8d30d6..0d12930f6 100644 --- a/packages/ai/src/providers/google-gemini-cli.ts +++ b/packages/ai/src/providers/google-gemini-cli.ts @@ -3,7 +3,6 @@ * Shared implementation for both google-gemini-cli and google-antigravity providers. * Uses the Cloud Code Assist API endpoint to access Gemini and Claude models. */ - import { createHash } from "node:crypto"; import type { Content, ThinkingConfig } from "@google/genai"; import { abortableSleep } from "@oh-my-pi/pi-utils"; @@ -660,8 +659,7 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = ( const providedId = part.functionCall.id; const needsNewId = - !providedId || - output.content.some((b) => b.type === "toolCall" && b.id === providedId); + !providedId || output.content.some(b => b.type === "toolCall" && b.id === providedId); const toolCallId = needsNewId ? `${part.functionCall.name}_${Date.now()}_${++toolCallCounter}` : providedId; @@ -695,7 +693,7 @@ export const streamGoogleGeminiCli: StreamFunction<"google-gemini-cli"> = ( if (candidate?.finishReason) { output.stopReason = mapStopReasonString(candidate.finishReason); - if (output.content.some((b) => b.type === "toolCall")) { + if (output.content.some(b => b.type === "toolCall")) { output.stopReason = "toolUse"; } } @@ -840,7 +838,7 @@ function deriveSessionId(context: Context): string | undefined { } else if (Array.isArray(message.content)) { text = message.content .filter((item): item is TextContent => item.type === "text") - .map((item) => item.text) + .map(item => item.text) .join("\n"); } diff --git a/packages/ai/src/providers/google-shared.ts b/packages/ai/src/providers/google-shared.ts index 7c65378bd..4d10772b6 100644 --- a/packages/ai/src/providers/google-shared.ts +++ b/packages/ai/src/providers/google-shared.ts @@ -1,7 +1,6 @@ /** * Shared utilities for Google Generative AI and Google Cloud Code Assist providers. */ - import { type Content, FinishReason, FunctionCallingConfigMode, type Part, type Schema } from "@google/genai"; import type { Context, ImageContent, Model, StopReason, TextContent, Tool } from "../types"; import { sanitizeSurrogates } from "../utils/sanitize-unicode"; @@ -86,7 +85,7 @@ export function convertMessages(model: Model, contex parts: [{ text: sanitizeSurrogates(msg.content) }], }); } else { - const parts: Part[] = msg.content.map((item) => { + const parts: Part[] = msg.content.map(item => { if (item.type === "text") { return { text: sanitizeSurrogates(item.text) }; } else { @@ -99,8 +98,8 @@ export function convertMessages(model: Model, contex } }); // Filter out images if model doesn't support them, and empty text blocks - let filteredParts = !model.input.includes("image") ? parts.filter((p) => p.text !== undefined) : parts; - filteredParts = filteredParts.filter((p) => { + let filteredParts = !model.input.includes("image") ? parts.filter(p => p.text !== undefined) : parts; + filteredParts = filteredParts.filter(p => { if (p.text !== undefined) { return p.text.trim().length > 0; } @@ -180,7 +179,7 @@ export function convertMessages(model: Model, contex } else if (msg.role === "toolResult") { // Extract text and image content const textContent = msg.content.filter((c): c is TextContent => c.type === "text"); - const textResult = textContent.map((c) => c.text).join("\n"); + const textResult = textContent.map(c => c.text).join("\n"); const imageContent = model.input.includes("image") ? msg.content.filter((c): c is ImageContent => c.type === "image") : []; @@ -196,7 +195,7 @@ export function convertMessages(model: Model, contex // Use "output" key for success, "error" key for errors as per SDK documentation const responseValue = hasText ? sanitizeSurrogates(textResult) : hasImages ? "(see attached image)" : ""; - const imageParts: Part[] = imageContent.map((imageBlock) => ({ + const imageParts: Part[] = imageContent.map(imageBlock => ({ inlineData: { mimeType: imageBlock.mimeType, data: imageBlock.data, @@ -221,7 +220,7 @@ export function convertMessages(model: Model, contex // Cloud Code Assist API requires all function responses to be in a single user turn. // Check if the last content is already a user turn with function responses and merge. const lastContent = contents[contents.length - 1]; - if (lastContent?.role === "user" && lastContent.parts?.some((p) => p.functionResponse)) { + if (lastContent?.role === "user" && lastContent.parts?.some(p => p.functionResponse)) { lastContent.parts.push(functionResponsePart); } else { contents.push({ @@ -270,7 +269,7 @@ const UNSUPPORTED_SCHEMA_FIELDS = new Set([ function sanitizeSchemaImpl(value: unknown, isInsideProperties: boolean): unknown { if (Array.isArray(value)) { - return value.map((entry) => sanitizeSchemaImpl(entry, isInsideProperties)); + return value.map(entry => sanitizeSchemaImpl(entry, isInsideProperties)); } if (!value || typeof value !== "object") { @@ -286,11 +285,11 @@ function sanitizeSchemaImpl(value: unknown, isInsideProperties: boolean): unknow const variants = obj[combiner] as Record[]; // Check if ALL variants have a const field - const allHaveConst = variants.every((v) => v && typeof v === "object" && "const" in v); + const allHaveConst = variants.every(v => v && typeof v === "object" && "const" in v); if (allHaveConst && variants.length > 0) { // Extract all const values into enum - result.enum = variants.map((v) => v.const); + result.enum = variants.map(v => v.const); // Inherit type from first variant if present const firstType = variants[0]?.type; @@ -327,7 +326,7 @@ function sanitizeSchemaImpl(value: unknown, isInsideProperties: boolean): unknow if (constValue !== undefined) { // Convert const to enum, merging with existing enum if present const existingEnum = Array.isArray(result.enum) ? result.enum : []; - if (!existingEnum.some((item) => Object.is(item, constValue))) { + if (!existingEnum.some(item => Object.is(item, constValue))) { existingEnum.push(constValue); } result.enum = existingEnum; diff --git a/packages/ai/src/providers/google-vertex.ts b/packages/ai/src/providers/google-vertex.ts index 9be089621..2574c413a 100644 --- a/packages/ai/src/providers/google-vertex.ts +++ b/packages/ai/src/providers/google-vertex.ts @@ -183,7 +183,7 @@ export const streamGoogleVertex: StreamFunction<"google-vertex"> = ( const providedId = part.functionCall.id; const needsNewId = - !providedId || output.content.some((b) => b.type === "toolCall" && b.id === providedId); + !providedId || output.content.some(b => b.type === "toolCall" && b.id === providedId); const toolCallId = needsNewId ? `${part.functionCall.name}_${Date.now()}_${++toolCallCounter}` : providedId; @@ -211,7 +211,7 @@ export const streamGoogleVertex: StreamFunction<"google-vertex"> = ( if (candidate?.finishReason) { output.stopReason = mapStopReason(candidate.finishReason); - if (output.content.some((b) => b.type === "toolCall")) { + if (output.content.some(b => b.type === "toolCall")) { output.stopReason = "toolUse"; } } diff --git a/packages/ai/src/providers/google.ts b/packages/ai/src/providers/google.ts index 102d62450..45e681fc4 100644 --- a/packages/ai/src/providers/google.ts +++ b/packages/ai/src/providers/google.ts @@ -170,7 +170,7 @@ export const streamGoogle: StreamFunction<"google-generative-ai"> = ( // Generate unique ID if not provided or if it's a duplicate const providedId = part.functionCall.id; const needsNewId = - !providedId || output.content.some((b) => b.type === "toolCall" && b.id === providedId); + !providedId || output.content.some(b => b.type === "toolCall" && b.id === providedId); const toolCallId = needsNewId ? `${part.functionCall.name}_${Date.now()}_${++toolCallCounter}` : providedId; @@ -198,7 +198,7 @@ export const streamGoogle: StreamFunction<"google-generative-ai"> = ( if (candidate?.finishReason) { output.stopReason = mapStopReason(candidate.finishReason); - if (output.content.some((b) => b.type === "toolCall")) { + if (output.content.some(b => b.type === "toolCall")) { output.stopReason = "toolUse"; } } diff --git a/packages/ai/src/providers/openai-codex-responses.ts b/packages/ai/src/providers/openai-codex-responses.ts index dac24e20c..31c427035 100644 --- a/packages/ai/src/providers/openai-codex-responses.ts +++ b/packages/ai/src/providers/openai-codex-responses.ts @@ -341,7 +341,7 @@ export const streamOpenAICodexResponses: StreamFunction<"openai-codex-responses" } else if (eventType === "response.output_item.done") { const item = rawEvent.item as ResponseReasoningItem | ResponseOutputMessage | ResponseFunctionToolCall; if (item.type === "reasoning" && currentBlock?.type === "thinking") { - currentBlock.thinking = item.summary?.map((s) => s.text).join("\n\n") || ""; + currentBlock.thinking = item.summary?.map(s => s.text).join("\n\n") || ""; currentBlock.thinkingSignature = JSON.stringify(item); stream.push({ type: "thinking_end", @@ -351,7 +351,7 @@ export const streamOpenAICodexResponses: StreamFunction<"openai-codex-responses" }); currentBlock = null; } else if (item.type === "message" && currentBlock?.type === "text") { - currentBlock.text = item.content.map((c) => (c.type === "output_text" ? c.text : c.refusal)).join(""); + currentBlock.text = item.content.map(c => (c.type === "output_text" ? c.text : c.refusal)).join(""); currentBlock.textSignature = item.id; stream.push({ type: "text_end", @@ -396,7 +396,7 @@ export const streamOpenAICodexResponses: StreamFunction<"openai-codex-responses" } calculateCost(model, output.usage); output.stopReason = mapStopReason(response?.status); - if (output.content.some((b) => b.type === "toolCall") && output.stopReason === "stop") { + if (output.content.some(b => b.type === "toolCall") && output.stopReason === "stop") { output.stopReason = "toolUse"; } } else if (eventType === "error") { @@ -593,9 +593,9 @@ function convertMessages(model: Model<"openai-codex-responses">, context: Contex }); // Filter out images if model doesn't support them, and empty text blocks let filteredContent = !model.input.includes("image") - ? content.filter((c) => c.type !== "input_image") + ? content.filter(c => c.type !== "input_image") : content; - filteredContent = filteredContent.filter((c) => { + filteredContent = filteredContent.filter(c => { if (c.type === "input_text") { return c.text.trim().length > 0; } @@ -647,10 +647,10 @@ function convertMessages(model: Model<"openai-codex-responses">, context: Contex messages.push(...output); } else if (msg.role === "toolResult") { const textResult = msg.content - .filter((c) => c.type === "text") - .map((c) => (c as { text: string }).text) + .filter(c => c.type === "text") + .map(c => (c as { text: string }).text) .join("\n"); - const hasImages = msg.content.some((c) => c.type === "image"); + const hasImages = msg.content.some(c => c.type === "image"); const normalized = normalizeResponsesToolCallId(msg.toolCallId); const hasText = textResult.length > 0; @@ -692,7 +692,7 @@ function convertMessages(model: Model<"openai-codex-responses">, context: Contex function convertTools( tools: Tool[], ): Array<{ type: "function"; name: string; description: string; parameters: Record; strict: null }> { - return tools.map((tool) => ({ + return tools.map(tool => ({ type: "function", name: tool.name, description: tool.description, diff --git a/packages/ai/src/providers/openai-codex/request-transformer.ts b/packages/ai/src/providers/openai-codex/request-transformer.ts index db1b67def..b5c847e52 100644 --- a/packages/ai/src/providers/openai-codex/request-transformer.ts +++ b/packages/ai/src/providers/openai-codex/request-transformer.ts @@ -73,8 +73,8 @@ function filterInput(input: InputItem[] | undefined): InputItem[] | undefined { if (!Array.isArray(input)) return input; return input - .filter((item) => item.type !== "item_reference") - .map((item) => { + .filter(item => item.type !== "item_reference") + .map(item => { if (item.id != null) { const { id: _id, ...rest } = item; return rest as InputItem; @@ -97,11 +97,11 @@ export async function transformRequestBody( if (body.input) { const functionCallIds = new Set( body.input - .filter((item) => item.type === "function_call" && typeof item.call_id === "string") - .map((item) => item.call_id as string), + .filter(item => item.type === "function_call" && typeof item.call_id === "string") + .map(item => item.call_id as string), ); - body.input = body.input.map((item) => { + body.input = body.input.map(item => { if (item.type === "function_call_output" && typeof item.call_id === "string") { const callId = item.call_id as string; if (!functionCallIds.has(callId)) { @@ -131,7 +131,7 @@ export async function transformRequestBody( if (prompt?.developerMessages && prompt.developerMessages.length > 0 && Array.isArray(body.input)) { const developerMessages = prompt.developerMessages.map( - (text) => + text => ({ type: "message", role: "developer", diff --git a/packages/ai/src/providers/openai-completions.ts b/packages/ai/src/providers/openai-completions.ts index 628490de9..763171afe 100644 --- a/packages/ai/src/providers/openai-completions.ts +++ b/packages/ai/src/providers/openai-completions.ts @@ -61,7 +61,7 @@ function hasToolHistory(messages: Message[]): boolean { return true; } if (msg.role === "assistant") { - if (msg.content.some((block) => block.type === "toolCall")) { + if (msg.content.some(block => block.type === "toolCall")) { return true; } } @@ -287,7 +287,7 @@ export const streamOpenAICompletions: StreamFunction<"openai-completions"> = ( for (const detail of reasoningDetails) { if (detail.type === "reasoning.encrypted" && detail.id && detail.data) { const matchingToolCall = output.content.find( - (b) => b.type === "toolCall" && b.id === detail.id, + b => b.type === "toolCall" && b.id === detail.id, ) as ToolCall | undefined; if (matchingToolCall) { matchingToolCall.thoughtSignature = JSON.stringify(detail); @@ -356,12 +356,12 @@ function createClient( headers["Openai-Intent"] = "conversation-edits"; // Copilot requires this header when sending images - const hasImages = messages.some((msg) => { + const hasImages = messages.some(msg => { if (msg.role === "user" && Array.isArray(msg.content)) { - return msg.content.some((c) => c.type === "image"); + return msg.content.some(c => c.type === "image"); } if (msg.role === "toolResult" && Array.isArray(msg.content)) { - return msg.content.some((c) => c.type === "image"); + return msg.content.some(c => c.type === "image"); } return false; }); @@ -516,7 +516,7 @@ export function convertMessages( } }); const filteredContent = !model.input.includes("image") - ? content.filter((c) => c.type !== "image_url") + ? content.filter(c => c.type !== "image_url") : content; if (filteredContent.length === 0) continue; params.push({ @@ -531,29 +531,29 @@ export function convertMessages( content: compat.requiresAssistantAfterToolResult ? "" : null, }; - const textBlocks = msg.content.filter((b) => b.type === "text") as TextContent[]; + const textBlocks = msg.content.filter(b => b.type === "text") as TextContent[]; // Filter out empty text blocks to avoid API validation errors - const nonEmptyTextBlocks = textBlocks.filter((b) => b.text && b.text.trim().length > 0); + const nonEmptyTextBlocks = textBlocks.filter(b => b.text && b.text.trim().length > 0); if (nonEmptyTextBlocks.length > 0) { // GitHub Copilot requires assistant content as a string, not an array. // Sending as array causes Claude models to re-answer all previous prompts. if (model.provider === "github-copilot") { - assistantMsg.content = nonEmptyTextBlocks.map((b) => sanitizeSurrogates(b.text)).join(""); + assistantMsg.content = nonEmptyTextBlocks.map(b => sanitizeSurrogates(b.text)).join(""); } else { - assistantMsg.content = nonEmptyTextBlocks.map((b) => { + assistantMsg.content = nonEmptyTextBlocks.map(b => { return { type: "text", text: sanitizeSurrogates(b.text) }; }); } } // Handle thinking blocks - const thinkingBlocks = msg.content.filter((b) => b.type === "thinking") as ThinkingContent[]; + const thinkingBlocks = msg.content.filter(b => b.type === "thinking") as ThinkingContent[]; // Filter out empty thinking blocks to avoid API validation errors - const nonEmptyThinkingBlocks = thinkingBlocks.filter((b) => b.thinking && b.thinking.trim().length > 0); + const nonEmptyThinkingBlocks = thinkingBlocks.filter(b => b.thinking && b.thinking.trim().length > 0); if (nonEmptyThinkingBlocks.length > 0) { if (compat.requiresThinkingAsText) { // Convert thinking blocks to plain text (no tags to avoid model mimicking them) - const thinkingText = nonEmptyThinkingBlocks.map((b) => b.thinking).join("\n\n"); + const thinkingText = nonEmptyThinkingBlocks.map(b => b.thinking).join("\n\n"); const textContent = assistantMsg.content as Array<{ type: "text"; text: string }> | null; if (textContent) { textContent.unshift({ type: "text", text: thinkingText }); @@ -564,14 +564,14 @@ export function convertMessages( // Use the signature from the first thinking block if available (for llama.cpp server + gpt-oss) const signature = nonEmptyThinkingBlocks[0].thinkingSignature; if (signature && signature.length > 0) { - (assistantMsg as any)[signature] = nonEmptyThinkingBlocks.map((b) => b.thinking).join("\n"); + (assistantMsg as any)[signature] = nonEmptyThinkingBlocks.map(b => b.thinking).join("\n"); } } } - const toolCalls = msg.content.filter((b) => b.type === "toolCall") as ToolCall[]; + const toolCalls = msg.content.filter(b => b.type === "toolCall") as ToolCall[]; if (toolCalls.length > 0) { - assistantMsg.tool_calls = toolCalls.map((tc) => ({ + assistantMsg.tool_calls = toolCalls.map(tc => ({ id: normalizeMistralToolId(tc.id, compat.requiresMistralToolIds), type: "function" as const, function: { @@ -580,8 +580,8 @@ export function convertMessages( }, })); const reasoningDetails = toolCalls - .filter((tc) => tc.thoughtSignature) - .map((tc) => { + .filter(tc => tc.thoughtSignature) + .map(tc => { try { return JSON.parse(tc.thoughtSignature!); } catch { @@ -616,10 +616,10 @@ export function convertMessages( // Extract text and image content const textResult = toolMsg.content - .filter((c) => c.type === "text") - .map((c) => (c as any).text) + .filter(c => c.type === "text") + .map(c => (c as any).text) .join("\n"); - const hasImages = toolMsg.content.some((c) => c.type === "image"); + const hasImages = toolMsg.content.some(c => c.type === "image"); // Always send tool result with text (or placeholder if only images) const hasText = textResult.length > 0; @@ -683,7 +683,7 @@ export function convertMessages( } function convertTools(tools: Tool[]): OpenAI.Chat.Completions.ChatCompletionTool[] { - return tools.map((tool) => ({ + return tools.map(tool => ({ type: "function", function: { name: tool.name, diff --git a/packages/ai/src/providers/openai-responses.ts b/packages/ai/src/providers/openai-responses.ts index 423ebf03f..4ff367af5 100644 --- a/packages/ai/src/providers/openai-responses.ts +++ b/packages/ai/src/providers/openai-responses.ts @@ -245,7 +245,7 @@ export const streamOpenAIResponses: StreamFunction<"openai-responses"> = ( const item = event.item; if (item.type === "reasoning" && currentBlock && currentBlock.type === "thinking") { - currentBlock.thinking = item.summary?.map((s) => s.text).join("\n\n") || ""; + currentBlock.thinking = item.summary?.map(s => s.text).join("\n\n") || ""; currentBlock.thinkingSignature = JSON.stringify(item); stream.push({ type: "thinking_end", @@ -255,7 +255,7 @@ export const streamOpenAIResponses: StreamFunction<"openai-responses"> = ( }); currentBlock = null; } else if (item.type === "message" && currentBlock && currentBlock.type === "text") { - currentBlock.text = item.content.map((c) => (c.type === "output_text" ? c.text : c.refusal)).join(""); + currentBlock.text = item.content.map(c => (c.type === "output_text" ? c.text : c.refusal)).join(""); currentBlock.textSignature = item.id; stream.push({ type: "text_end", @@ -293,7 +293,7 @@ export const streamOpenAIResponses: StreamFunction<"openai-responses"> = ( calculateCost(model, output.usage); // Map status to stop reason output.stopReason = mapStopReason(response?.status); - if (output.content.some((b) => b.type === "toolCall") && output.stopReason === "stop") { + if (output.content.some(b => b.type === "toolCall") && output.stopReason === "stop") { output.stopReason = "toolUse"; } } @@ -358,12 +358,12 @@ function createClient( headers["Openai-Intent"] = "conversation-edits"; // Copilot requires this header when sending images - const hasImages = messages.some((msg) => { + const hasImages = messages.some(msg => { if (msg.role === "user" && Array.isArray(msg.content)) { - return msg.content.some((c) => c.type === "image"); + return msg.content.some(c => c.type === "image"); } if (msg.role === "toolResult" && Array.isArray(msg.content)) { - return msg.content.some((c) => c.type === "image"); + return msg.content.some(c => c.type === "image"); } return false; }); @@ -491,9 +491,9 @@ function convertMessages( }); // Filter out images if model doesn't support them, and empty text blocks let filteredContent = !model.input.includes("image") - ? content.filter((c) => c.type !== "input_image") + ? content.filter(c => c.type !== "input_image") : content; - filteredContent = filteredContent.filter((c) => { + filteredContent = filteredContent.filter(c => { if (c.type === "input_text") { return c.text.trim().length > 0; } @@ -567,10 +567,10 @@ function convertMessages( } else if (msg.role === "toolResult") { // Extract text and image content const textResult = msg.content - .filter((c) => c.type === "text") - .map((c) => (c as any).text) + .filter(c => c.type === "text") + .map(c => (c as any).text) .join("\n"); - const hasImages = msg.content.some((c) => c.type === "image"); + const hasImages = msg.content.some(c => c.type === "image"); const normalized = normalizeResponsesToolCallId(msg.toolCallId); if (strictResponsesPairing && !knownCallIds.has(normalized.callId)) { continue; @@ -618,7 +618,7 @@ function convertMessages( } function convertTools(tools: Tool[]): OpenAITool[] { - return tools.map((tool) => ({ + return tools.map(tool => ({ type: "function", name: tool.name, description: tool.description, diff --git a/packages/ai/src/providers/transform-messages.ts b/packages/ai/src/providers/transform-messages.ts index 43c6fc473..0b35081bc 100644 --- a/packages/ai/src/providers/transform-messages.ts +++ b/packages/ai/src/providers/transform-messages.ts @@ -98,7 +98,7 @@ export function transformMessages(messages: Message[], model: const needsToolCallIdNormalization = targetRequiresStrictIds && (crossProviderSwitch || copilotCrossApiSwitch); // Transform message from different provider/model - const transformedContent = assistantMsg.content.flatMap((block) => { + const transformedContent = assistantMsg.content.flatMap(block => { if (block.type === "thinking") { // Skip empty thinking blocks, convert others to plain text if (!block.thinking || block.thinking.trim() === "") return []; @@ -173,7 +173,7 @@ export function transformMessages(messages: Message[], model: const assistantMsg = msg as AssistantMessage; const isErroredAssistant = assistantMsg.stopReason === "error" || assistantMsg.stopReason === "aborted"; - const toolCalls = assistantMsg.content.filter((b) => b.type === "toolCall") as ToolCall[]; + const toolCalls = assistantMsg.content.filter(b => b.type === "toolCall") as ToolCall[]; result.push(msg); diff --git a/packages/ai/src/storage.ts b/packages/ai/src/storage.ts index cd5ecc983..911784102 100644 --- a/packages/ai/src/storage.ts +++ b/packages/ai/src/storage.ts @@ -105,7 +105,7 @@ export class CliAuthStorage { const dir = path.dirname(dbPath); const dirExists = await fs .stat(dir) - .then((s) => s.isDirectory()) + .then(s => s.isDirectory()) .catch(() => false); if (!dirExists) { await fs.mkdir(dir, { recursive: true, mode: 0o700 }); diff --git a/packages/ai/src/types.ts b/packages/ai/src/types.ts index 5b8713b7e..dafc6a5d7 100644 --- a/packages/ai/src/types.ts +++ b/packages/ai/src/types.ts @@ -1,3 +1,4 @@ +import type { TSchema } from "@sinclair/typebox"; import type { BedrockOptions } from "./providers/amazon-bedrock"; import type { AnthropicOptions } from "./providers/anthropic"; import type { CursorOptions } from "./providers/cursor"; @@ -237,8 +238,6 @@ export interface CursorExecHandlers { onToolResult?: CursorToolResultHandler; } -import type { TSchema } from "@sinclair/typebox"; - export interface Tool { name: string; description: string; diff --git a/packages/ai/src/usage.ts b/packages/ai/src/usage.ts index 8c424a472..b0ed1fc5f 100644 --- a/packages/ai/src/usage.ts +++ b/packages/ai/src/usage.ts @@ -4,7 +4,6 @@ * Provides a normalized schema to represent multiple limit windows, model tiers, * and shared quotas across providers. */ - import type { Provider } from "./types"; export type UsageUnit = "percent" | "tokens" | "requests" | "usd" | "minutes" | "bytes" | "unknown"; diff --git a/packages/ai/src/usage/claude.ts b/packages/ai/src/usage/claude.ts index 6becd9d10..4720de764 100644 --- a/packages/ai/src/usage/claude.ts +++ b/packages/ai/src/usage/claude.ts @@ -241,10 +241,10 @@ function buildCacheKey(params: UsageFetchParams): string { function resolveCacheExpiry(now: number, limits: UsageLimit[]): number { const earliestReset = limits - .map((limit) => limit.window?.resetsAt) + .map(limit => limit.window?.resetsAt) .filter((value): value is number => typeof value === "number" && Number.isFinite(value)) .reduce((min, value) => (min === undefined ? value : Math.min(min, value)), undefined as number | undefined); - const exhausted = limits.some((limit) => limit.status === "exhausted"); + const exhausted = limits.some(limit => limit.status === "exhausted"); if (earliestReset === undefined) return now + DEFAULT_CACHE_TTL_MS; if (exhausted) return earliestReset; return Math.min(now + DEFAULT_CACHE_TTL_MS, earliestReset); @@ -351,5 +351,5 @@ async function fetchClaudeUsage(params: UsageFetchParams, ctx: UsageFetchContext export const claudeUsageProvider: UsageProvider = { id: "anthropic", fetchUsage: fetchClaudeUsage, - supports: (params) => params.provider === "anthropic" && params.credential.type === "oauth", + supports: params => params.provider === "anthropic" && params.credential.type === "oauth", }; diff --git a/packages/ai/src/usage/github-copilot.ts b/packages/ai/src/usage/github-copilot.ts index e71c739a2..7300d8d39 100644 --- a/packages/ai/src/usage/github-copilot.ts +++ b/packages/ai/src/usage/github-copilot.ts @@ -3,7 +3,6 @@ * * Normalizes Copilot quota usage into the shared UsageReport schema. */ - import type { UsageAmount, UsageCacheEntry, @@ -316,7 +315,7 @@ function normalizeBillingUsage(data: BillingUsageResponse): UsageLimit[] { }; const premiumItems = data.usageItems.filter( - (item) => item.sku === "Copilot Premium Request" || item.sku.includes("Premium"), + item => item.sku === "Copilot Premium Request" || item.sku.includes("Premium"), ); const totalUsed = premiumItems.reduce((sum, item) => sum + item.grossQuantity, 0); const totalLimit = premiumItems.reduce((sum, item) => sum + (item.limit ?? 0), 0) || undefined; @@ -359,7 +358,7 @@ function normalizeBillingUsage(data: BillingUsageResponse): UsageLimit[] { function resolveCacheTtl(now: number, report: UsageReport | null): UsageCacheEntry["expiresAt"] { if (!report) return now + DEFAULT_CACHE_TTL_MS; const resetInMs = report.limits - .map((limit) => limit.window?.resetInMs) + .map(limit => limit.window?.resetInMs) .find((value): value is number => typeof value === "number" && Number.isFinite(value)); if (!resetInMs || resetInMs <= 0) return now + DEFAULT_CACHE_TTL_MS; return now + Math.min(MAX_CACHE_TTL_MS, resetInMs); diff --git a/packages/ai/src/usage/google-antigravity.ts b/packages/ai/src/usage/google-antigravity.ts index c9b78086b..8d0aedd26 100644 --- a/packages/ai/src/usage/google-antigravity.ts +++ b/packages/ai/src/usage/google-antigravity.ts @@ -214,5 +214,5 @@ async function fetchAntigravityUsage(params: UsageFetchParams, ctx: UsageFetchCo export const antigravityUsageProvider: UsageProvider = { id: "google-antigravity", fetchUsage: fetchAntigravityUsage, - supports: (params) => params.provider === "google-antigravity", + supports: params => params.provider === "google-antigravity", }; diff --git a/packages/ai/src/usage/openai-codex.ts b/packages/ai/src/usage/openai-codex.ts index ddfb6293d..c4afd1cf0 100644 --- a/packages/ai/src/usage/openai-codex.ts +++ b/packages/ai/src/usage/openai-codex.ts @@ -264,9 +264,9 @@ function buildUsageLimit(args: { function resolveCacheExpiry(args: { report: UsageReport | null; nowMs: number }): number { const { report, nowMs } = args; if (!report) return nowMs + DEFAULT_CACHE_TTL_MS; - const exhausted = report.limits.some((limit) => limit.status === "exhausted"); + const exhausted = report.limits.some(limit => limit.status === "exhausted"); const resetCandidates = report.limits - .map((limit) => limit.window?.resetsAt) + .map(limit => limit.window?.resetsAt) .filter((value): value is number => typeof value === "number" && Number.isFinite(value)); const earliestReset = resetCandidates.length > 0 ? Math.min(...resetCandidates) : undefined; if (exhausted && earliestReset) return earliestReset; diff --git a/packages/ai/src/usage/zai.ts b/packages/ai/src/usage/zai.ts index 47531b546..7725aeac3 100644 --- a/packages/ai/src/usage/zai.ts +++ b/packages/ai/src/usage/zai.ts @@ -133,7 +133,7 @@ function buildCacheKey(params: UsageFetchParams): string { function resolveCacheExpiry(now: number, limits: UsageLimit[]): number { const earliestReset = limits - .map((limit) => limit.window?.resetsAt) + .map(limit => limit.window?.resetsAt) .filter((value): value is number => typeof value === "number" && Number.isFinite(value)) .reduce((min, value) => (min === undefined ? value : Math.min(min, value)), undefined as number | undefined); if (!earliestReset) return now + DEFAULT_CACHE_TTL_MS; @@ -288,5 +288,5 @@ async function fetchZaiUsage(params: UsageFetchParams, ctx: UsageFetchContext): export const zaiUsageProvider: UsageProvider = { id: "zai", fetchUsage: fetchZaiUsage, - supports: (params) => params.provider === "zai" && params.credential.type === "api_key", + supports: params => params.provider === "zai" && params.credential.type === "api_key", }; diff --git a/packages/ai/src/utils/event-stream.ts b/packages/ai/src/utils/event-stream.ts index 6fb17fe2d..97026ad8b 100644 --- a/packages/ai/src/utils/event-stream.ts +++ b/packages/ai/src/utils/event-stream.ts @@ -53,7 +53,7 @@ export class EventStream implements AsyncIterable { } else if (this.done) { return; } else { - const result = await new Promise>((resolve) => this.waiting.push(resolve)); + const result = await new Promise>(resolve => this.waiting.push(resolve)); if (result.done) return; yield result.value; } @@ -68,8 +68,8 @@ export class EventStream implements AsyncIterable { export class AssistantMessageEventStream extends EventStream { constructor() { super( - (event) => event.type === "done" || event.type === "error", - (event) => { + event => event.type === "done" || event.type === "error", + event => { if (event.type === "done") { return event.message; } else if (event.type === "error") { diff --git a/packages/ai/src/utils/oauth/anthropic.ts b/packages/ai/src/utils/oauth/anthropic.ts index 199f7a31d..291076dd5 100644 --- a/packages/ai/src/utils/oauth/anthropic.ts +++ b/packages/ai/src/utils/oauth/anthropic.ts @@ -1,7 +1,6 @@ /** * Anthropic OAuth flow (Claude Pro/Max) */ - import { OAuthCallbackFlow } from "./callback-server"; import { generatePKCE } from "./pkce"; import type { OAuthController, OAuthCredentials } from "./types"; diff --git a/packages/ai/src/utils/oauth/callback-server.ts b/packages/ai/src/utils/oauth/callback-server.ts index ea037064d..f32323b7c 100644 --- a/packages/ai/src/utils/oauth/callback-server.ts +++ b/packages/ai/src/utils/oauth/callback-server.ts @@ -10,7 +10,6 @@ * - generateAuthUrl(): Build provider-specific authorization URL * - exchangeToken(): Exchange authorization code for tokens */ - import templateHtml from "./oauth.html" with { type: "text" }; import type { OAuthController, OAuthCredentials } from "./types"; @@ -63,7 +62,7 @@ export abstract class OAuthCallbackFlow { const bytes = new Uint8Array(16); crypto.getRandomValues(bytes); return Array.from(bytes) - .map((value) => value.toString(16).padStart(2, "0")) + .map(value => value.toString(16).padStart(2, "0")) .join(""); } @@ -125,7 +124,7 @@ export abstract class OAuthCallbackFlow { hostname: DEFAULT_HOSTNAME, port, reusePort: false, - fetch: (req) => this.handleCallback(req, expectedState), + fetch: req => this.handleCallback(req, expectedState), }); } diff --git a/packages/ai/src/utils/oauth/github-copilot.ts b/packages/ai/src/utils/oauth/github-copilot.ts index 670242b4b..e726bfba5 100644 --- a/packages/ai/src/utils/oauth/github-copilot.ts +++ b/packages/ai/src/utils/oauth/github-copilot.ts @@ -1,7 +1,6 @@ /** * GitHub Copilot OAuth flow */ - import { abortableSleep } from "@oh-my-pi/pi-utils"; import { getModels } from "../../models"; import type { OAuthCredentials } from "./types"; @@ -279,7 +278,7 @@ async function enableAllGitHubCopilotModels( ): Promise { const models = getModels("github-copilot"); await Promise.all( - models.map(async (model) => { + models.map(async model => { const success = await enableGitHubCopilotModel(token, model.id, enterpriseDomain); onProgress?.(model.id, success); }), diff --git a/packages/ai/src/utils/oauth/google-antigravity.ts b/packages/ai/src/utils/oauth/google-antigravity.ts index e4b9276c0..cfdc4a65e 100644 --- a/packages/ai/src/utils/oauth/google-antigravity.ts +++ b/packages/ai/src/utils/oauth/google-antigravity.ts @@ -2,7 +2,6 @@ * Antigravity OAuth flow (Gemini 3, Claude, GPT-OSS via Google Cloud) * Uses different OAuth credentials than google-gemini-cli for access to additional models. */ - import { OAuthCallbackFlow } from "./callback-server"; import { generatePKCE } from "./pkce"; import type { OAuthController, OAuthCredentials } from "./types"; diff --git a/packages/ai/src/utils/oauth/google-gemini-cli.ts b/packages/ai/src/utils/oauth/google-gemini-cli.ts index 1cbb518ff..bbbe90eb0 100644 --- a/packages/ai/src/utils/oauth/google-gemini-cli.ts +++ b/packages/ai/src/utils/oauth/google-gemini-cli.ts @@ -2,7 +2,6 @@ * Gemini CLI OAuth flow (Google Cloud Code Assist) * Standard Gemini models only (gemini-2.0-flash, gemini-2.5-*) */ - import { OAuthCallbackFlow } from "./callback-server"; import { generatePKCE } from "./pkce"; import type { OAuthController, OAuthCredentials } from "./types"; @@ -49,7 +48,7 @@ interface GoogleRpcErrorResponse { function getDefaultTier(allowedTiers?: Array<{ id?: string; isDefault?: boolean }>): { id?: string } { if (!allowedTiers || allowedTiers.length === 0) return { id: TIER_LEGACY }; - const defaultTier = allowedTiers.find((t) => t.isDefault); + const defaultTier = allowedTiers.find(t => t.isDefault); return defaultTier ?? { id: TIER_LEGACY }; } @@ -58,7 +57,7 @@ function isVpcScAffectedUser(payload: unknown): boolean { if (!("error" in payload)) return false; const error = (payload as GoogleRpcErrorResponse).error; if (!error?.details || !Array.isArray(error.details)) return false; - return error.details.some((detail) => detail.reason === "SECURITY_POLICY_VIOLATED"); + return error.details.some(detail => detail.reason === "SECURITY_POLICY_VIOLATED"); } async function pollOperation( diff --git a/packages/ai/src/utils/oauth/index.ts b/packages/ai/src/utils/oauth/index.ts index f5ec0bec7..ee0023c7f 100644 --- a/packages/ai/src/utils/oauth/index.ts +++ b/packages/ai/src/utils/oauth/index.ts @@ -1,3 +1,14 @@ +// ============================================================================ +// High-level API +// ============================================================================ +import { refreshAnthropicToken } from "./anthropic"; +import { refreshCursorToken } from "./cursor"; +import { refreshGitHubCopilotToken } from "./github-copilot"; +import { refreshAntigravityToken } from "./google-antigravity"; +import { refreshGoogleCloudToken } from "./google-gemini-cli"; +import { refreshOpenAICodexToken } from "./openai-codex"; +import type { OAuthCredentials, OAuthProvider, OAuthProviderInfo } from "./types"; + /** * OAuth credential management for AI providers. * @@ -36,18 +47,6 @@ export { loginOpenAICodex, refreshOpenAICodexToken } from "./openai-codex"; export * from "./types"; -// ============================================================================ -// High-level API -// ============================================================================ - -import { refreshAnthropicToken } from "./anthropic"; -import { refreshCursorToken } from "./cursor"; -import { refreshGitHubCopilotToken } from "./github-copilot"; -import { refreshAntigravityToken } from "./google-antigravity"; -import { refreshGoogleCloudToken } from "./google-gemini-cli"; -import { refreshOpenAICodexToken } from "./openai-codex"; -import type { OAuthCredentials, OAuthProvider, OAuthProviderInfo } from "./types"; - /** * Refresh token for any OAuth provider. * Saves the new credentials and returns the new access token. diff --git a/packages/ai/src/utils/oauth/openai-codex.ts b/packages/ai/src/utils/oauth/openai-codex.ts index 3ffd2b749..b80ee7f9b 100644 --- a/packages/ai/src/utils/oauth/openai-codex.ts +++ b/packages/ai/src/utils/oauth/openai-codex.ts @@ -1,7 +1,6 @@ /** * OpenAI Codex (ChatGPT OAuth) flow */ - import { OAuthCallbackFlow } from "./callback-server"; import { generatePKCE } from "./pkce"; import type { OAuthController, OAuthCredentials } from "./types"; diff --git a/packages/ai/src/utils/overflow.ts b/packages/ai/src/utils/overflow.ts index e21fe5474..b2f84ac7e 100644 --- a/packages/ai/src/utils/overflow.ts +++ b/packages/ai/src/utils/overflow.ts @@ -85,7 +85,7 @@ export function isContextOverflow(message: AssistantMessage, contextWindow?: num // Case 1: Check error message patterns if (message.stopReason === "error" && message.errorMessage) { // Check known patterns - if (OVERFLOW_PATTERNS.some((p) => p.test(message.errorMessage!))) { + if (OVERFLOW_PATTERNS.some(p => p.test(message.errorMessage!))) { return true; } diff --git a/packages/ai/src/utils/validation.ts b/packages/ai/src/utils/validation.ts index 8edc69f39..e2492c1ff 100644 --- a/packages/ai/src/utils/validation.ts +++ b/packages/ai/src/utils/validation.ts @@ -1,12 +1,11 @@ import AjvModule from "ajv"; import addFormatsModule from "ajv-formats"; +import type { Tool, ToolCall } from "../types"; // Handle both default and named exports (ESM/CJS interop) const Ajv = (AjvModule as any).default || AjvModule; const addFormats = (addFormatsModule as any).default || addFormatsModule; -import type { Tool, ToolCall } from "../types"; - // ============================================================================ // Type Coercion Utilities // ============================================================================ @@ -50,7 +49,7 @@ function normalizeExpectedTypes(typeParam: unknown): string[] { * Used to verify that a parsed JSON value is actually what the schema wants. */ function matchesExpectedType(value: unknown, expectedTypes: string[]): boolean { - return expectedTypes.some((type) => { + return expectedTypes.some(type => { switch (type) { case "string": return typeof value === "string"; @@ -157,7 +156,7 @@ function decodeJsonPointer(pointer: string): string[] { return pointer .split("/") .slice(1) // Remove leading empty segment from initial "/" - .map((segment) => segment.replace(/~1/g, "/").replace(/~0/g, "~")); + .map(segment => segment.replace(/~1/g, "/").replace(/~0/g, "~")); } /** @@ -310,7 +309,7 @@ if (!isBrowserExtension) { * @throws Error if tool is not found or validation fails */ export function validateToolCall(tools: Tool[], toolCall: ToolCall): any { - const tool = tools.find((t) => t.name === toolCall.name); + const tool = tools.find(t => t.name === toolCall.name); if (!tool) { throw new Error(`Tool "${toolCall.name}" not found`); } diff --git a/packages/ai/test/context-overflow.test.ts b/packages/ai/test/context-overflow.test.ts index a416eb658..91d16b1e5 100644 --- a/packages/ai/test/context-overflow.test.ts +++ b/packages/ai/test/context-overflow.test.ts @@ -481,7 +481,7 @@ describe("Context overflow error handling", () => { }); // Wait for server to be ready - await new Promise((resolve) => { + await new Promise(resolve => { const checkServer = async () => { try { const response = await fetch("http://localhost:11434/api/tags"); diff --git a/packages/ai/test/duplicate-tool-results.test.ts b/packages/ai/test/duplicate-tool-results.test.ts index aa969158c..ae10ad962 100644 --- a/packages/ai/test/duplicate-tool-results.test.ts +++ b/packages/ai/test/duplicate-tool-results.test.ts @@ -80,7 +80,7 @@ describe("Duplicate Tool Results Regression", () => { // Count tool results with the same ID const toolResults = transformed.filter( - (m) => m.role === "toolResult" && (m as ToolResultMessage).toolCallId === toolCallId, + m => m.role === "toolResult" && (m as ToolResultMessage).toolCallId === toolCallId, ); // Should have exactly ONE tool result, not two @@ -138,7 +138,7 @@ describe("Duplicate Tool Results Regression", () => { const transformed = transformMessages(messages, model); const toolResults = transformed.filter( - (m) => m.role === "toolResult" && (m as ToolResultMessage).toolCallId === toolCallId, + m => m.role === "toolResult" && (m as ToolResultMessage).toolCallId === toolCallId, ); expect(toolResults.length).toBe(1); @@ -187,7 +187,7 @@ describe("Duplicate Tool Results Regression", () => { const transformed = transformMessages(messages, model); const toolResults = transformed.filter( - (m) => m.role === "toolResult" && (m as ToolResultMessage).toolCallId === toolCallId, + m => m.role === "toolResult" && (m as ToolResultMessage).toolCallId === toolCallId, ); // Should have exactly ONE synthetic tool result added @@ -241,13 +241,13 @@ describe("Duplicate Tool Results Regression", () => { const transformed = transformMessages(messages, model); // Should have exactly 3 tool results total - const allToolResults = transformed.filter((m) => m.role === "toolResult"); + const allToolResults = transformed.filter(m => m.role === "toolResult"); expect(allToolResults.length).toBe(3); // Each tool call should have exactly one result - const result1 = allToolResults.filter((m) => (m as ToolResultMessage).toolCallId === toolCallId1); - const result2 = allToolResults.filter((m) => (m as ToolResultMessage).toolCallId === toolCallId2); - const result3 = allToolResults.filter((m) => (m as ToolResultMessage).toolCallId === toolCallId3); + const result1 = allToolResults.filter(m => (m as ToolResultMessage).toolCallId === toolCallId1); + const result2 = allToolResults.filter(m => (m as ToolResultMessage).toolCallId === toolCallId2); + const result3 = allToolResults.filter(m => (m as ToolResultMessage).toolCallId === toolCallId3); expect(result1.length).toBe(1); expect(result2.length).toBe(1); diff --git a/packages/ai/test/handoff.test.ts b/packages/ai/test/handoff.test.ts index b269de687..ddd8e9fec 100644 --- a/packages/ai/test/handoff.test.ts +++ b/packages/ai/test/handoff.test.ts @@ -319,16 +319,16 @@ async function testProviderHandoff( // Extract text from response const responseText = response.content - .filter((b) => b.type === "text") - .map((b) => b.text) + .filter(b => b.type === "text") + .map(b => b.text) .join(" ") .toLowerCase(); // For aborted messages, we don't expect to find the facts if (sourceContext.message.stopReason === "error") { - const hasToolCalls = response.content.some((b) => b.type === "toolCall"); - const hasThinking = response.content.some((b) => b.type === "thinking"); - const hasText = response.content.some((b) => b.type === "text"); + const hasToolCalls = response.content.some(b => b.type === "toolCall"); + const hasThinking = response.content.some(b => b.type === "thinking"); + const hasText = response.content.some(b => b.type === "text"); expect(response.stopReason === "stop" || response.stopReason === "toolUse").toBe(true); expect(hasThinking || hasText || hasToolCalls).toBe(true); diff --git a/packages/ai/test/image-limits.test.ts b/packages/ai/test/image-limits.test.ts index f7ba27170..5c0e8ce70 100644 --- a/packages/ai/test/image-limits.test.ts +++ b/packages/ai/test/image-limits.test.ts @@ -290,7 +290,7 @@ describe("Image Limits E2E Tests", () => { async () => { // Known limit: 100 images const { limit, lastError } = await findLimit( - (count) => testImageCount(model, count, smallImage), + count => testImageCount(model, count, smallImage), 20, 120, 20, @@ -380,7 +380,7 @@ describe("Image Limits E2E Tests", () => { async () => { // Known limit: 500 images const { limit, lastError } = await findLimit( - (count) => testImageCount(model, count, smallImage), + count => testImageCount(model, count, smallImage), 100, 600, 100, @@ -468,7 +468,7 @@ describe("Image Limits E2E Tests", () => { async () => { // Known to work up to ~2500, hits errors around 3000 const { limit, lastError } = await findLimit( - (count) => testImageCount(model, count, smallImage), + count => testImageCount(model, count, smallImage), 500, 3000, 500, @@ -554,7 +554,7 @@ describe("Image Limits E2E Tests", () => { "should find maximum image count limit", async () => { // Known to fail around 9 images - const { limit, lastError } = await findLimit((count) => testImageCount(model, count, smallImage), 5, 15, 1); + const { limit, lastError } = await findLimit(count => testImageCount(model, count, smallImage), 5, 15, 1); console.log(`\n Mistral max images: ~${limit} (last error: ${lastError})`); expect(limit).toBeGreaterThanOrEqual(5); }, @@ -635,12 +635,7 @@ describe("Image Limits E2E Tests", () => { "should find maximum image count limit", async () => { // Limited by context window, not explicit image limit - const { limit, lastError } = await findLimit( - (count) => testImageCount(model, count, smallImage), - 10, - 60, - 10, - ); + const { limit, lastError } = await findLimit(count => testImageCount(model, count, smallImage), 10, 60, 10); console.log(`\n OpenRouter max images: ~${limit} (last error: ${lastError})`); expect(limit).toBeGreaterThanOrEqual(10); }, @@ -720,7 +715,7 @@ describe("Image Limits E2E Tests", () => { "should find maximum image count limit", async () => { const { limit, lastError } = await findLimit( - (count) => testImageCount(model, count, smallImage), + count => testImageCount(model, count, smallImage), 10, 100, 10, @@ -803,7 +798,7 @@ describe("Image Limits E2E Tests", () => { it( "should find maximum image count limit", async () => { - const { limit, lastError } = await findLimit((count) => testImageCount(model, count, smallImage), 5, 50, 5); + const { limit, lastError } = await findLimit(count => testImageCount(model, count, smallImage), 5, 50, 5); console.log(`\n Groq max images: ~${limit} (last error: ${lastError})`); expect(limit).toBeGreaterThanOrEqual(5); }, @@ -883,7 +878,7 @@ describe("Image Limits E2E Tests", () => { "should find maximum image count limit", async () => { const { limit, lastError } = await findLimit( - (count) => testImageCount(model, count, smallImage), + count => testImageCount(model, count, smallImage), 10, 100, 10, diff --git a/packages/ai/test/image-tool-result.test.ts b/packages/ai/test/image-tool-result.test.ts index 17ec08760..1f58f152a 100644 --- a/packages/ai/test/image-tool-result.test.ts +++ b/packages/ai/test/image-tool-result.test.ts @@ -61,7 +61,7 @@ async function handleToolWithImageResult(model: Model, o expect(firstResponse.stopReason).toBe("toolUse"); // Find the tool call - const toolCall = firstResponse.content.find((b) => b.type === "toolCall"); + const toolCall = firstResponse.content.find(b => b.type === "toolCall"); expect(toolCall).toBeTruthy(); if (!toolCall || toolCall.type !== "toolCall") { throw new Error("Expected tool call"); @@ -95,7 +95,7 @@ async function handleToolWithImageResult(model: Model, o expect(secondResponse.errorMessage).toBeFalsy(); // Verify the LLM can see and describe the image - const textContent = secondResponse.content.find((b) => b.type === "text"); + const textContent = secondResponse.content.find(b => b.type === "text"); expect(textContent).toBeTruthy(); if (textContent && textContent.type === "text") { const lowerContent = textContent.text.toLowerCase(); @@ -150,7 +150,7 @@ async function handleToolWithTextAndImageResult(model: Model b.type === "toolCall"); + const toolCall = firstResponse.content.find(b => b.type === "toolCall"); expect(toolCall).toBeTruthy(); if (!toolCall || toolCall.type !== "toolCall") { throw new Error("Expected tool call"); @@ -188,7 +188,7 @@ async function handleToolWithTextAndImageResult(model: Model b.type === "text"); + const textContent = secondResponse.content.find(b => b.type === "text"); expect(textContent).toBeTruthy(); if (textContent && textContent.type === "text") { const lowerContent = textContent.text.toLowerCase(); diff --git a/packages/ai/test/oauth.ts b/packages/ai/test/oauth.ts index 5ccc415f6..cfa81da87 100644 --- a/packages/ai/test/oauth.ts +++ b/packages/ai/test/oauth.ts @@ -6,8 +6,11 @@ * * E2E tests are disabled by default. Set E2E=1 environment variable to enable. */ - import * as fs from "node:fs/promises"; +import * as os from "node:os"; +import * as path from "node:path"; +import { getOAuthApiKey } from "@oh-my-pi/pi-ai/utils/oauth"; +import type { OAuthCredentials, OAuthProvider } from "@oh-my-pi/pi-ai/utils/oauth/types"; import { isEnoent } from "@oh-my-pi/pi-utils"; /** @@ -25,11 +28,6 @@ export function e2eApiKey(envVar: string): string | undefined { return process.env[envVar]; } -import * as os from "node:os"; -import * as path from "node:path"; -import { getOAuthApiKey } from "@oh-my-pi/pi-ai/utils/oauth"; -import type { OAuthCredentials, OAuthProvider } from "@oh-my-pi/pi-ai/utils/oauth/types"; - const AUTH_PATH = path.join(os.homedir(), ".pi", "agent", "auth.json"); type ApiKeyCredential = { diff --git a/packages/ai/test/openai-codex-stream.test.ts b/packages/ai/test/openai-codex-stream.test.ts index c56cdb876..35d4e9693 100644 --- a/packages/ai/test/openai-codex-stream.test.ts +++ b/packages/ai/test/openai-codex-stream.test.ts @@ -120,7 +120,7 @@ describe("openai-codex streaming", () => { } if (event.type === "done") { sawDone = true; - expect(event.message.content.find((c) => c.type === "text")?.text).toBe("Hello"); + expect(event.message.content.find(c => c.type === "text")?.text).toBe("Hello"); } } diff --git a/packages/ai/test/openai-codex.test.ts b/packages/ai/test/openai-codex.test.ts index 222dbd76a..b5389bdc5 100644 --- a/packages/ai/test/openai-codex.test.ts +++ b/packages/ai/test/openai-codex.test.ts @@ -35,14 +35,14 @@ describe("openai-codex request transformer", () => { expect(transformed.include).toEqual(["reasoning.encrypted_content"]); const input = transformed.input || []; - expect(input.some((item) => item.type === "item_reference")).toBe(false); - expect(input.some((item) => "id" in item)).toBe(false); + expect(input.some(item => item.type === "item_reference")).toBe(false); + expect(input.some(item => "id" in item)).toBe(false); const first = input[0]; expect(first?.type).toBe("message"); expect(first?.role).toBe("developer"); expect(first?.content).toEqual([{ type: "input_text", text: `${DEFAULT_PROMPT_PREFIX}...` }]); - const orphaned = input.find((item) => item.type === "message" && item.role === "assistant"); + const orphaned = input.find(item => item.type === "message" && item.role === "assistant"); expect(orphaned?.content).toMatch(/Previous tool result/); }); }); diff --git a/packages/ai/test/openai-completions-tool-result-images.test.ts b/packages/ai/test/openai-completions-tool-result-images.test.ts index 3a9be5828..a63ad8a6d 100644 --- a/packages/ai/test/openai-completions-tool-result-images.test.ts +++ b/packages/ai/test/openai-completions-tool-result-images.test.ts @@ -73,16 +73,14 @@ describe("openai-completions convertMessages", () => { }; const messages = convertMessages(model, context, compat); - const roles = messages.map((message) => message.role); + const roles = messages.map(message => message.role); expect(roles).toEqual(["user", "assistant", "tool", "tool", "user"]); const imageMessage = messages[messages.length - 1]; expect(imageMessage.role).toBe("user"); expect(Array.isArray(imageMessage.content)).toBe(true); - const imageParts = (imageMessage.content as Array<{ type?: string }>).filter( - (part) => part?.type === "image_url", - ); + const imageParts = (imageMessage.content as Array<{ type?: string }>).filter(part => part?.type === "image_url"); expect(imageParts.length).toBe(2); }); }); diff --git a/packages/ai/test/stream.test.ts b/packages/ai/test/stream.test.ts index 53de23f42..ca6dde232 100644 --- a/packages/ai/test/stream.test.ts +++ b/packages/ai/test/stream.test.ts @@ -48,7 +48,7 @@ async function basicTextGeneration(model: Model, options expect(response.usage.input + response.usage.cacheRead).toBeGreaterThan(0); expect(response.usage.output).toBeGreaterThan(0); expect(response.errorMessage).toBeFalsy(); - expect(response.content.map((b) => (b.type === "text" ? b.text : "")).join("")).toContain("Hello test successful"); + expect(response.content.map(b => (b.type === "text" ? b.text : "")).join("")).toContain("Hello test successful"); context.messages.push(response); context.messages.push({ role: "user", content: "Now say 'Goodbye test successful'", timestamp: Date.now() }); @@ -60,7 +60,7 @@ async function basicTextGeneration(model: Model, options expect(secondResponse.usage.input + secondResponse.usage.cacheRead).toBeGreaterThan(0); expect(secondResponse.usage.output).toBeGreaterThan(0); expect(secondResponse.errorMessage).toBeFalsy(); - expect(secondResponse.content.map((b) => (b.type === "text" ? b.text : "")).join("")).toContain( + expect(secondResponse.content.map(b => (b.type === "text" ? b.text : "")).join("")).toContain( "Goodbye test successful", ); } @@ -133,8 +133,8 @@ async function handleToolCall(model: Model, options?: Op const response = await s.result(); expect(response.stopReason).toBe("toolUse"); - expect(response.content.some((b) => b.type === "toolCall")).toBeTruthy(); - const toolCall = response.content.find((b) => b.type === "toolCall"); + expect(response.content.some(b => b.type === "toolCall")).toBeTruthy(); + const toolCall = response.content.find(b => b.type === "toolCall"); if (toolCall && toolCall.type === "toolCall") { expect(toolCall.name).toBe("calculator"); expect(toolCall.id).toBeTruthy(); @@ -169,7 +169,7 @@ async function handleStreaming(model: Model, options?: O expect(textStarted).toBe(true); expect(textChunks.length).toBeGreaterThan(0); expect(textCompleted).toBe(true); - expect(response.content.some((b) => b.type === "text")).toBeTruthy(); + expect(response.content.some(b => b.type === "text")).toBeTruthy(); } async function handleThinking(model: Model, options?: OptionsForApi) { @@ -207,7 +207,7 @@ async function handleThinking(model: Model, options?: Op expect(thinkingStarted).toBe(true); expect(thinkingChunks.length).toBeGreaterThan(0); expect(thinkingCompleted).toBe(true); - expect(response.content.some((b) => b.type === "thinking")).toBeTruthy(); + expect(response.content.some(b => b.type === "thinking")).toBeTruthy(); } async function handleImage(model: Model, options?: OptionsForApi) { @@ -248,7 +248,7 @@ async function handleImage(model: Model, options?: Optio // Check the response mentions red and circle expect(response.content.length > 0).toBeTruthy(); - const textContent = response.content.find((b) => b.type === "text"); + const textContent = response.content.find(b => b.type === "text"); if (textContent && textContent.type === "text") { const lowerContent = textContent.text.toLowerCase(); expect(lowerContent).toContain("red"); @@ -1384,7 +1384,7 @@ describe("Generate E2E Tests", () => { }); // Wait for server to be ready - await new Promise((resolve) => { + await new Promise(resolve => { const checkServer = async () => { try { const response = await fetch("http://localhost:11434/api/tags"); diff --git a/packages/ai/test/tool-call-without-result.test.ts b/packages/ai/test/tool-call-without-result.test.ts index d0ec78d8b..474279281 100644 --- a/packages/ai/test/tool-call-without-result.test.ts +++ b/packages/ai/test/tool-call-without-result.test.ts @@ -51,7 +51,7 @@ async function testToolCallWithoutResult( console.log("First response:", JSON.stringify(firstResponse, null, 2)); // Verify the response contains a tool call - const hasToolCall = firstResponse.content.some((block) => block.type === "toolCall"); + const hasToolCall = firstResponse.content.some(block => block.type === "toolCall"); expect(hasToolCall).toBe(true); if (!hasToolCall) { @@ -79,10 +79,10 @@ async function testToolCallWithoutResult( // The LLM may choose to answer directly or make a new tool call - either is fine // The important thing is it didn't fail with the orphaned tool call error const textContent = secondResponse.content - .filter((block) => block.type === "text") - .map((block) => (block.type === "text" ? block.text : "")) + .filter(block => block.type === "text") + .map(block => (block.type === "text" ? block.text : "")) .join(" "); - const toolCalls = secondResponse.content.filter((block) => block.type === "toolCall").length; + const toolCalls = secondResponse.content.filter(block => block.type === "toolCall").length; expect(toolCalls || textContent.length).toBeGreaterThan(0); console.log("Answer:", textContent); diff --git a/packages/ai/test/unicode-surrogate.test.ts b/packages/ai/test/unicode-surrogate.test.ts index 82a49353d..a8eba5380 100644 --- a/packages/ai/test/unicode-surrogate.test.ts +++ b/packages/ai/test/unicode-surrogate.test.ts @@ -200,7 +200,7 @@ Unanswered Comments: 2 expect(response.stopReason).not.toBe("error"); expect(response.errorMessage).toBeFalsy(); - expect(response.content.some((b) => b.type === "text")).toBe(true); + expect(response.content.some(b => b.type === "text")).toBe(true); } async function testUnpairedHighSurrogate(llm: Model, options: OptionsForApi = {}) { diff --git a/packages/ai/test/xhigh.test.ts b/packages/ai/test/xhigh.test.ts index 4fd7796de..04fc01ea8 100644 --- a/packages/ai/test/xhigh.test.ts +++ b/packages/ai/test/xhigh.test.ts @@ -32,8 +32,8 @@ describe.skipIf(!e2eApiKey("OPENAI_API_KEY"))("xhigh reasoning", () => { const response = await s.result(); expect(response.stopReason, `Error: ${response.errorMessage}`).toBe("stop"); - expect(response.content.some((b) => b.type === "text")).toBe(true); - expect(hasThinking || response.content.some((b) => b.type === "thinking")).toBe(true); + expect(response.content.some(b => b.type === "text")).toBe(true); + expect(hasThinking || response.content.some(b => b.type === "thinking")).toBe(true); }); }); diff --git a/packages/ai/test/zen.test.ts b/packages/ai/test/zen.test.ts index 7ffa3a7a4..cc2f41aaf 100644 --- a/packages/ai/test/zen.test.ts +++ b/packages/ai/test/zen.test.ts @@ -7,7 +7,7 @@ import { e2eApiKey } from "./oauth"; describe.skipIf(!e2eApiKey("OPENCODE_API_KEY"))("OpenCode Zen Models Smoke Test", () => { const zenModels = Object.values(MODELS.opencode); - zenModels.forEach((model) => { + zenModels.forEach(model => { it(`${model.id}`, async () => { const response = await complete(model as Model, { messages: [{ role: "user", content: "Say hello.", timestamp: Date.now() }], diff --git a/packages/coding-agent/examples/custom-tools/hello/index.ts b/packages/coding-agent/examples/custom-tools/hello/index.ts index 926e98f84..28af32971 100644 --- a/packages/coding-agent/examples/custom-tools/hello/index.ts +++ b/packages/coding-agent/examples/custom-tools/hello/index.ts @@ -1,6 +1,6 @@ import type { CustomToolFactory } from "@oh-my-pi/pi-coding-agent"; -const factory: CustomToolFactory = (pi) => ({ +const factory: CustomToolFactory = pi => ({ name: "hello", label: "Hello", description: "A simple greeting tool", diff --git a/packages/coding-agent/examples/custom-tools/todo/index.ts b/packages/coding-agent/examples/custom-tools/todo/index.ts index 6ab76b7df..d9911c30b 100644 --- a/packages/coding-agent/examples/custom-tools/todo/index.ts +++ b/packages/coding-agent/examples/custom-tools/todo/index.ts @@ -7,7 +7,6 @@ * * The onSession callback reconstructs state by scanning past tool results. */ - import type { CustomTool, CustomToolContext, @@ -29,7 +28,7 @@ interface TodoDetails { error?: string; } -const factory: CustomToolFactory = (pi) => { +const factory: CustomToolFactory = pi => { const { Type } = pi.typebox; const { StringEnum, Text } = pi.pi; @@ -85,7 +84,7 @@ const factory: CustomToolFactory = (pi) => { { type: "text", text: todos.length - ? todos.map((t) => `[${t.done ? "x" : " "}] #${t.id}: ${t.text}`).join("\n") + ? todos.map(t => `[${t.done ? "x" : " "}] #${t.id}: ${t.text}`).join("\n") : "No todos", }, ], @@ -114,7 +113,7 @@ const factory: CustomToolFactory = (pi) => { details: { action: "toggle", todos: [...todos], nextId, error: "id required" }, }; } - const todo = todos.find((t) => t.id === params.id); + const todo = todos.find(t => t.id === params.id); if (!todo) { return { content: [{ type: "text", text: `Todo #${params.id} not found` }], diff --git a/packages/coding-agent/examples/extensions/api-demo.ts b/packages/coding-agent/examples/extensions/api-demo.ts index 7f1bdbd9c..b57c87afb 100644 --- a/packages/coding-agent/examples/extensions/api-demo.ts +++ b/packages/coding-agent/examples/extensions/api-demo.ts @@ -5,7 +5,6 @@ * These features are now exposed directly on the ExtensionAPI, matching * the CustomToolAPI interface. */ - import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent"; export default function (pi: ExtensionAPI) { diff --git a/packages/coding-agent/examples/extensions/chalk-logger.ts b/packages/coding-agent/examples/extensions/chalk-logger.ts index ac4ca9b95..a31209ca1 100644 --- a/packages/coding-agent/examples/extensions/chalk-logger.ts +++ b/packages/coding-agent/examples/extensions/chalk-logger.ts @@ -2,7 +2,6 @@ * Example extension that uses a 3rd party dependency (chalk). * Tests that jiti can resolve npm modules correctly. */ - import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent"; import chalk from "chalk"; @@ -14,12 +13,12 @@ export default function (pi: ExtensionAPI) { console.log(`${chalk.blue("[chalk-logger]")} Agent starting`); }); - pi.on("tool_call", async (event) => { + pi.on("tool_call", async event => { console.log(`${chalk.yellow("[chalk-logger]")} Tool: ${chalk.cyan(event.toolName)}`); return undefined; }); - pi.on("agent_end", async (event) => { + pi.on("agent_end", async event => { const count = event.messages.length; console.log(`${chalk.green("[chalk-logger]")} Done with ${chalk.bold(String(count))} messages`); }); diff --git a/packages/coding-agent/examples/extensions/hello.ts b/packages/coding-agent/examples/extensions/hello.ts index 75e79d855..fe910bce0 100644 --- a/packages/coding-agent/examples/extensions/hello.ts +++ b/packages/coding-agent/examples/extensions/hello.ts @@ -3,7 +3,6 @@ * * Demonstrates using ExtensionAPI's logger, typebox, and pi module access. */ - import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent"; export default function (pi: ExtensionAPI) { diff --git a/packages/coding-agent/examples/extensions/pirate.ts b/packages/coding-agent/examples/extensions/pirate.ts index 60979f052..11d02a574 100644 --- a/packages/coding-agent/examples/extensions/pirate.ts +++ b/packages/coding-agent/examples/extensions/pirate.ts @@ -9,7 +9,6 @@ * 2. Use /pirate to toggle pirate mode * 3. When enabled, the agent will respond like a pirate */ - import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent"; export default function pirateExtension(pi: ExtensionAPI) { diff --git a/packages/coding-agent/examples/extensions/plan-mode.ts b/packages/coding-agent/examples/extensions/plan-mode.ts index d86370a0e..5200df2c9 100644 --- a/packages/coding-agent/examples/extensions/plan-mode.ts +++ b/packages/coding-agent/examples/extensions/plan-mode.ts @@ -18,7 +18,6 @@ * 2. Use /plan to toggle plan mode on/off * 3. Or start in plan mode with --plan flag */ - import type { ExtensionAPI, ExtensionContext } from "@oh-my-pi/pi-coding-agent"; import { Key } from "@oh-my-pi/pi-tui"; @@ -120,12 +119,12 @@ const SAFE_COMMANDS = [ ]; function isSafeCommand(command: string): boolean { - if (SAFE_COMMANDS.some((pattern) => pattern.test(command))) { - if (!DESTRUCTIVE_PATTERNS.some((pattern) => pattern.test(command))) { + if (SAFE_COMMANDS.some(pattern => pattern.test(command))) { + if (!DESTRUCTIVE_PATTERNS.some(pattern => pattern.test(command))) { return true; } } - if (DESTRUCTIVE_PATTERNS.some((pattern) => pattern.test(command))) { + if (DESTRUCTIVE_PATTERNS.some(pattern => pattern.test(command))) { return false; } return true; @@ -223,7 +222,7 @@ export default function planModeExtension(pi: ExtensionAPI) { // Helper to update status displays function updateStatus(ctx: ExtensionContext) { if (executionMode && todoItems.length > 0) { - const completed = todoItems.filter((t) => t.completed).length; + const completed = todoItems.filter(t => t.completed).length; ctx.ui.setStatus("plan-mode", ctx.ui.theme.fg("accent", `📋 ${completed}/${todoItems.length}`)); } else if (planModeEnabled) { ctx.ui.setStatus("plan-mode", ctx.ui.theme.fg("warning", "⏸ plan")); @@ -293,13 +292,13 @@ export default function planModeExtension(pi: ExtensionAPI) { // Register Shift+P shortcut pi.registerShortcut(Key.shift("p"), { description: "Toggle plan mode", - handler: async (ctx) => { + handler: async ctx => { await togglePlanMode(ctx); }, }); // Block destructive bash in plan mode - pi.on("tool_call", async (event) => { + pi.on("tool_call", async event => { if (!planModeEnabled) return; if (event.toolName !== "bash") return; @@ -319,7 +318,7 @@ export default function planModeExtension(pi: ExtensionAPI) { if (!executionMode || todoItems.length === 0) return; // Mark the first uncompleted step as done when any tool succeeds - const nextStep = todoItems.find((t) => !t.completed); + const nextStep = todoItems.find(t => !t.completed); if (nextStep) { nextStep.completed = true; updateStatus(ctx); @@ -328,7 +327,7 @@ export default function planModeExtension(pi: ExtensionAPI) { // Filter out stale plan mode context messages from LLM context // This ensures the agent only sees the CURRENT state (plan mode on/off) - pi.on("context", async (event) => { + pi.on("context", async event => { // Only filter when NOT in plan mode (i.e., when executing) if (planModeEnabled) { return; @@ -336,7 +335,7 @@ export default function planModeExtension(pi: ExtensionAPI) { // Remove any previous plan-mode-context messages const _beforeCount = event.messages.length; - const filtered = event.messages.filter((m) => { + const filtered = event.messages.filter(m => { if (m.role === "user" && Array.isArray(m.content)) { const hasOldContext = m.content.some( (c: { type: string; text?: string }) => c.type === "text" && c.text?.includes("[PLAN MODE ACTIVE]"), @@ -381,8 +380,8 @@ Do NOT attempt to make changes - just describe what you would do.`, } if (executionMode && todoItems.length > 0) { - const remaining = todoItems.filter((t) => !t.completed); - const todoList = remaining.map((t) => `${t.step}. ${t.text}`).join("\n"); + const remaining = todoItems.filter(t => !t.completed); + const todoList = remaining.map(t => `${t.step}. ${t.text}`).join("\n"); return { message: { customType: "plan-execution-context", @@ -402,10 +401,10 @@ Execute each step in order.`, pi.on("agent_end", async (event, ctx) => { // In execution mode, check if all steps complete if (executionMode && todoItems.length > 0) { - const allComplete = todoItems.every((t) => t.completed); + const allComplete = todoItems.every(t => t.completed); if (allComplete) { // Show final completed list in chat - const completedList = todoItems.map((t) => `~~${t.text}~~`).join("\n"); + const completedList = todoItems.map(t => `~~${t.text}~~`).join("\n"); pi.sendMessage( { customType: "plan-complete", @@ -428,7 +427,7 @@ Execute each step in order.`, // Extract todos from last message const messages = event.messages; - const lastAssistant = [...messages].reverse().find((m) => m.role === "assistant"); + const lastAssistant = [...messages].reverse().find(m => m.role === "assistant"); if (lastAssistant && Array.isArray(lastAssistant.content)) { const textContent = lastAssistant.content .filter( @@ -541,7 +540,7 @@ Execute each step in order.`, // If no tools were called this turn, the agent was doing analysis/explanation // Mark the next uncompleted step as done if (!toolsCalledThisTurn) { - const nextStep = todoItems.find((t) => !t.completed); + const nextStep = todoItems.find(t => !t.completed); if (nextStep) { nextStep.completed = true; updateStatus(ctx); diff --git a/packages/coding-agent/examples/extensions/todo.ts b/packages/coding-agent/examples/extensions/todo.ts index 5f3e91617..ab811e3cc 100644 --- a/packages/coding-agent/examples/extensions/todo.ts +++ b/packages/coding-agent/examples/extensions/todo.ts @@ -9,7 +9,6 @@ * proper branching - when you branch, the todo state is automatically * correct for that point in history. */ - import { StringEnum } from "@oh-my-pi/pi-ai"; import type { ExtensionAPI, ExtensionContext, Theme } from "@oh-my-pi/pi-coding-agent"; import { matchesKey, Text, truncateToWidth } from "@oh-my-pi/pi-tui"; @@ -74,7 +73,7 @@ class TodoListComponent { if (this.todos.length === 0) { lines.push(truncateToWidth(` ${th.fg("dim", "No todos yet. Ask the agent to add some!")}`, width)); } else { - const done = this.todos.filter((t) => t.done).length; + const done = this.todos.filter(t => t.done).length; const total = this.todos.length; lines.push(truncateToWidth(` ${th.fg("muted", `${done}/${total} completed`)}`, width)); lines.push(""); @@ -149,7 +148,7 @@ export default function (pi: ExtensionAPI) { { type: "text", text: todos.length - ? todos.map((t) => `[${t.done ? "x" : " "}] #${t.id}: ${t.text}`).join("\n") + ? todos.map(t => `[${t.done ? "x" : " "}] #${t.id}: ${t.text}`).join("\n") : "No todos", }, ], @@ -178,7 +177,7 @@ export default function (pi: ExtensionAPI) { details: { action: "toggle", todos: [...todos], nextId, error: "id required" } as TodoDetails, }; } - const todo = todos.find((t) => t.id === params.id); + const todo = todos.find(t => t.id === params.id); if (!todo) { return { content: [{ type: "text", text: `Todo #${params.id} not found` }], diff --git a/packages/coding-agent/examples/extensions/tools.ts b/packages/coding-agent/examples/extensions/tools.ts index eaf6dbc56..0856d0702 100644 --- a/packages/coding-agent/examples/extensions/tools.ts +++ b/packages/coding-agent/examples/extensions/tools.ts @@ -8,7 +8,6 @@ * 1. Copy this file to ~/.omp/agent/extensions/ (legacy: ~/.pi/agent/extensions/) or your project's .omp/extensions/ * 2. Use /tools to open the tool selector */ - import type { ExtensionAPI, ExtensionContext } from "@oh-my-pi/pi-coding-agent"; import { getSettingsListTheme } from "@oh-my-pi/pi-coding-agent"; import { Container, type SettingItem, SettingsList } from "@oh-my-pi/pi-tui"; @@ -71,7 +70,7 @@ export default function toolsExtension(pi: ExtensionAPI) { await ctx.ui.custom((tui, theme, done) => { // Build settings items for each tool - const items: SettingItem[] = allTools.map((tool) => ({ + const items: SettingItem[] = allTools.map(tool => ({ id: tool, label: tool, currentValue: enabledTools.has(tool) ? "enabled" : "disabled", diff --git a/packages/coding-agent/examples/extensions/with-deps/index.ts b/packages/coding-agent/examples/extensions/with-deps/index.ts index 1300f735e..7143f5ccb 100644 --- a/packages/coding-agent/examples/extensions/with-deps/index.ts +++ b/packages/coding-agent/examples/extensions/with-deps/index.ts @@ -4,7 +4,6 @@ * * Requires: npm install in this directory */ - import type { ExtensionAPI } from "@oh-my-pi/pi-coding-agent"; import { Type } from "@sinclair/typebox"; import ms from "ms"; diff --git a/packages/coding-agent/examples/hooks/auto-commit-on-exit.ts b/packages/coding-agent/examples/hooks/auto-commit-on-exit.ts index 9c195c993..c115ff61f 100644 --- a/packages/coding-agent/examples/hooks/auto-commit-on-exit.ts +++ b/packages/coding-agent/examples/hooks/auto-commit-on-exit.ts @@ -4,7 +4,6 @@ * Automatically commits changes when the agent exits. * Uses the last assistant message to generate a commit message. */ - import type { HookAPI } from "@oh-my-pi/pi-coding-agent"; export default function (pi: HookAPI) { @@ -27,7 +26,7 @@ export default function (pi: HookAPI) { if (Array.isArray(content)) { lastAssistantText = content .filter((c): c is { type: "text"; text: string } => c.type === "text") - .map((c) => c.text) + .map(c => c.text) .join("\n"); } break; diff --git a/packages/coding-agent/examples/hooks/confirm-destructive.ts b/packages/coding-agent/examples/hooks/confirm-destructive.ts index 9da9b0a95..e0c8dfd99 100644 --- a/packages/coding-agent/examples/hooks/confirm-destructive.ts +++ b/packages/coding-agent/examples/hooks/confirm-destructive.ts @@ -4,7 +4,6 @@ * Prompts for confirmation before destructive session actions (clear, switch, branch). * Demonstrates how to cancel session events using the before_* events. */ - import type { HookAPI, SessionBeforeSwitchEvent, SessionMessageEntry } from "@oh-my-pi/pi-coding-agent"; export default function (pi: HookAPI) { diff --git a/packages/coding-agent/examples/hooks/custom-compaction.ts b/packages/coding-agent/examples/hooks/custom-compaction.ts index bb69098ae..0b87300e5 100644 --- a/packages/coding-agent/examples/hooks/custom-compaction.ts +++ b/packages/coding-agent/examples/hooks/custom-compaction.ts @@ -12,7 +12,6 @@ * Usage: * omp --hook examples/hooks/custom-compaction.ts */ - import { complete, getModel } from "@oh-my-pi/pi-ai"; import type { HookAPI } from "@oh-my-pi/pi-coding-agent"; import { convertToLlm, serializeConversation } from "@oh-my-pi/pi-coding-agent"; @@ -89,7 +88,7 @@ ${conversationText} const summary = response.content .filter((c): c is { type: "text"; text: string } => c.type === "text") - .map((c) => c.text) + .map(c => c.text) .join("\n"); if (!summary.trim()) { diff --git a/packages/coding-agent/examples/hooks/dirty-repo-guard.ts b/packages/coding-agent/examples/hooks/dirty-repo-guard.ts index 99f7692fc..346ef40d1 100644 --- a/packages/coding-agent/examples/hooks/dirty-repo-guard.ts +++ b/packages/coding-agent/examples/hooks/dirty-repo-guard.ts @@ -4,7 +4,6 @@ * Prevents session changes when there are uncommitted git changes. * Useful to ensure work is committed before switching context. */ - import type { HookAPI, HookContext } from "@oh-my-pi/pi-coding-agent"; async function checkDirtyRepo(pi: HookAPI, ctx: HookContext, action: string): Promise<{ cancel: boolean } | undefined> { diff --git a/packages/coding-agent/examples/hooks/file-trigger.ts b/packages/coding-agent/examples/hooks/file-trigger.ts index d226a00e0..11f316cc5 100644 --- a/packages/coding-agent/examples/hooks/file-trigger.ts +++ b/packages/coding-agent/examples/hooks/file-trigger.ts @@ -7,7 +7,6 @@ * Usage: * echo "Run the tests" > /tmp/agent-trigger.txt */ - import * as fs from "node:fs"; import type { HookAPI } from "@oh-my-pi/pi-coding-agent"; diff --git a/packages/coding-agent/examples/hooks/git-checkpoint.ts b/packages/coding-agent/examples/hooks/git-checkpoint.ts index 11d84cf03..b398ce157 100644 --- a/packages/coding-agent/examples/hooks/git-checkpoint.ts +++ b/packages/coding-agent/examples/hooks/git-checkpoint.ts @@ -4,7 +4,6 @@ * Creates git stash checkpoints at each turn so /branch can restore code state. * When branching, offers to restore code to that point in history. */ - import type { HookAPI } from "@oh-my-pi/pi-coding-agent"; export default function (pi: HookAPI) { diff --git a/packages/coding-agent/examples/hooks/handoff.ts b/packages/coding-agent/examples/hooks/handoff.ts index 4b3cc316c..96e404de4 100644 --- a/packages/coding-agent/examples/hooks/handoff.ts +++ b/packages/coding-agent/examples/hooks/handoff.ts @@ -11,7 +11,6 @@ * * The generated prompt appears as a draft in the editor for review/editing. */ - import { complete, type Message } from "@oh-my-pi/pi-ai"; import type { HookAPI, SessionEntry } from "@oh-my-pi/pi-coding-agent"; import { BorderedLoader, convertToLlm, serializeConversation } from "@oh-my-pi/pi-coding-agent"; @@ -62,7 +61,7 @@ export default function (pi: HookAPI) { const branch = ctx.sessionManager.getBranch(); const messages = branch .filter((entry): entry is SessionEntry & { type: "message" } => entry.type === "message") - .map((entry) => entry.message); + .map(entry => entry.message); if (messages.length === 0) { ctx.ui.notify("No conversation to hand off", "error"); @@ -105,13 +104,13 @@ export default function (pi: HookAPI) { return response.content .filter((c): c is { type: "text"; text: string } => c.type === "text") - .map((c) => c.text) + .map(c => c.text) .join("\n"); }; doGenerate() .then(done) - .catch((err) => { + .catch(err => { console.error("Handoff generation failed:", err); done(null); }); diff --git a/packages/coding-agent/examples/hooks/permission-gate.ts b/packages/coding-agent/examples/hooks/permission-gate.ts index 280f09f05..eb427ceb6 100644 --- a/packages/coding-agent/examples/hooks/permission-gate.ts +++ b/packages/coding-agent/examples/hooks/permission-gate.ts @@ -4,7 +4,6 @@ * Prompts for confirmation before running potentially dangerous bash commands. * Patterns checked: rm -rf, sudo, chmod/chown 777 */ - import type { HookAPI } from "@oh-my-pi/pi-coding-agent"; export default function (pi: HookAPI) { @@ -14,7 +13,7 @@ export default function (pi: HookAPI) { if (event.toolName !== "bash") return undefined; const command = event.input.command as string; - const isDangerous = dangerousPatterns.some((p) => p.test(command)); + const isDangerous = dangerousPatterns.some(p => p.test(command)); if (isDangerous) { if (!ctx.hasUI) { diff --git a/packages/coding-agent/examples/hooks/protected-paths.ts b/packages/coding-agent/examples/hooks/protected-paths.ts index e296bdf2c..182554c88 100644 --- a/packages/coding-agent/examples/hooks/protected-paths.ts +++ b/packages/coding-agent/examples/hooks/protected-paths.ts @@ -4,7 +4,6 @@ * Blocks write and edit operations to protected paths. * Useful for preventing accidental modifications to sensitive files. */ - import type { HookAPI } from "@oh-my-pi/pi-coding-agent"; export default function (pi: HookAPI) { @@ -16,7 +15,7 @@ export default function (pi: HookAPI) { } const path = event.input.path as string; - const isProtected = protectedPaths.some((p) => path.includes(p)); + const isProtected = protectedPaths.some(p => path.includes(p)); if (isProtected) { if (ctx.hasUI) { diff --git a/packages/coding-agent/examples/hooks/qna.ts b/packages/coding-agent/examples/hooks/qna.ts index 1200b0fb2..b936b7b2f 100644 --- a/packages/coding-agent/examples/hooks/qna.ts +++ b/packages/coding-agent/examples/hooks/qna.ts @@ -6,7 +6,6 @@ * 2. Shows a spinner while extracting (hides editor) * 3. Loads the result into the editor for user to fill in answers */ - import { complete, type UserMessage } from "@oh-my-pi/pi-ai"; import type { HookAPI } from "@oh-my-pi/pi-coding-agent"; import { BorderedLoader } from "@oh-my-pi/pi-coding-agent"; @@ -56,7 +55,7 @@ export default function (pi: HookAPI) { } const textParts = msg.content .filter((c): c is { type: "text"; text: string } => c.type === "text") - .map((c) => c.text); + .map(c => c.text); if (textParts.length > 0) { lastAssistantText = textParts.join("\n"); break; @@ -96,7 +95,7 @@ export default function (pi: HookAPI) { return response.content .filter((c): c is { type: "text"; text: string } => c.type === "text") - .map((c) => c.text) + .map(c => c.text) .join("\n"); }; diff --git a/packages/coding-agent/examples/hooks/snake.ts b/packages/coding-agent/examples/hooks/snake.ts index 0fa7dcf6e..282caa5c3 100644 --- a/packages/coding-agent/examples/hooks/snake.ts +++ b/packages/coding-agent/examples/hooks/snake.ts @@ -1,7 +1,6 @@ /** * Snake game hook - play snake with /snake command */ - import type { HookAPI } from "@oh-my-pi/pi-coding-agent"; import { matchesKey, visibleWidth } from "@oh-my-pi/pi-tui"; @@ -47,7 +46,7 @@ function spawnFood(snake: Point[]): Point { x: Math.floor(Math.random() * GAME_WIDTH), y: Math.floor(Math.random() * GAME_HEIGHT), }; - } while (snake.some((s) => s.x === food.x && s.y === food.y)); + } while (snake.some(s => s.x === food.x && s.y === food.y)); return food; } @@ -127,7 +126,7 @@ class SnakeComponent { } // Check self collision - if (this.state.snake.some((s) => s.x === newHead.x && s.y === newHead.y)) { + if (this.state.snake.some(s => s.x === newHead.x && s.y === newHead.y)) { this.state.gameOver = true; return; } @@ -249,7 +248,7 @@ class SnakeComponent { let row = ""; for (let x = 0; x < effectiveWidth; x++) { const isHead = this.state.snake[0].x === x && this.state.snake[0].y === y; - const isBody = this.state.snake.slice(1).some((s) => s.x === x && s.y === y); + const isBody = this.state.snake.slice(1).some(s => s.x === x && s.y === y); const isFood = this.state.food.x === x && this.state.food.y === y; if (isHead) { @@ -331,7 +330,7 @@ export default function (pi: HookAPI) { return new SnakeComponent( tui, () => done(undefined), - (state) => { + state => { // Save or clear state pi.appendEntry(SNAKE_SAVE_TYPE, state); }, diff --git a/packages/coding-agent/examples/hooks/status-line.ts b/packages/coding-agent/examples/hooks/status-line.ts index 5c9b643fc..271c3a02f 100644 --- a/packages/coding-agent/examples/hooks/status-line.ts +++ b/packages/coding-agent/examples/hooks/status-line.ts @@ -4,7 +4,6 @@ * Demonstrates ctx.ui.setStatus() for displaying persistent status text in the footer. * Shows turn progress with themed colors. */ - import type { HookAPI } from "@oh-my-pi/pi-coding-agent"; export default function (pi: HookAPI) { diff --git a/packages/coding-agent/examples/sdk/01-minimal.ts b/packages/coding-agent/examples/sdk/01-minimal.ts index 3d5598c7b..9cc1a3ff5 100644 --- a/packages/coding-agent/examples/sdk/01-minimal.ts +++ b/packages/coding-agent/examples/sdk/01-minimal.ts @@ -4,19 +4,18 @@ * Uses all defaults: discovers skills, hooks, tools, context files * from cwd and ~/.omp/agent. Model chosen from settings or first available. */ - import { createAgentSession } from "@oh-my-pi/pi-coding-agent"; const { session } = await createAgentSession(); -session.subscribe((event) => { +session.subscribe(event => { if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") { process.stdout.write(event.assistantMessageEvent.delta); } }); await session.prompt("What files are in the current directory?"); -session.state.messages.forEach((msg) => { +session.state.messages.forEach(msg => { console.log(msg); }); console.log(); diff --git a/packages/coding-agent/examples/sdk/02-custom-model.ts b/packages/coding-agent/examples/sdk/02-custom-model.ts index b32bf0f52..04e308b96 100644 --- a/packages/coding-agent/examples/sdk/02-custom-model.ts +++ b/packages/coding-agent/examples/sdk/02-custom-model.ts @@ -3,7 +3,6 @@ * * Shows how to select a specific model and thinking level. */ - import { getModel } from "@oh-my-pi/pi-ai"; import { createAgentSession, discoverAuthStorage, discoverModels } from "@oh-my-pi/pi-coding-agent"; @@ -27,7 +26,7 @@ if (customModel) { const available = modelRegistry.getAvailable(); console.log( "Available models:", - available.map((m) => `${m.provider}/${m.id}`), + available.map(m => `${m.provider}/${m.id}`), ); if (available.length > 0) { @@ -38,7 +37,7 @@ if (available.length > 0) { modelRegistry, }); - session.subscribe((event) => { + session.subscribe(event => { if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") { process.stdout.write(event.assistantMessageEvent.delta); } diff --git a/packages/coding-agent/examples/sdk/03-custom-prompt.ts b/packages/coding-agent/examples/sdk/03-custom-prompt.ts index 19aa9fd32..ceff8c0dd 100644 --- a/packages/coding-agent/examples/sdk/03-custom-prompt.ts +++ b/packages/coding-agent/examples/sdk/03-custom-prompt.ts @@ -3,7 +3,6 @@ * * Shows how to replace or modify the default system prompt. */ - import { createAgentSession, SessionManager } from "@oh-my-pi/pi-coding-agent"; // Option 1: Replace prompt entirely @@ -13,7 +12,7 @@ Always end responses with "Arrr!"`, sessionManager: SessionManager.inMemory(), }); -session1.subscribe((event) => { +session1.subscribe(event => { if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") { process.stdout.write(event.assistantMessageEvent.delta); } @@ -25,7 +24,7 @@ console.log("\n"); // Option 2: Modify default prompt (receives default, returns modified) const { session: session2 } = await createAgentSession({ - systemPrompt: (defaultPrompt) => `${defaultPrompt} + systemPrompt: defaultPrompt => `${defaultPrompt} ## Additional Instructions - Always be concise @@ -33,7 +32,7 @@ const { session: session2 } = await createAgentSession({ sessionManager: SessionManager.inMemory(), }); -session2.subscribe((event) => { +session2.subscribe(event => { if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") { process.stdout.write(event.assistantMessageEvent.delta); } diff --git a/packages/coding-agent/examples/sdk/04-skills.ts b/packages/coding-agent/examples/sdk/04-skills.ts index 4e38299e1..99a79e872 100644 --- a/packages/coding-agent/examples/sdk/04-skills.ts +++ b/packages/coding-agent/examples/sdk/04-skills.ts @@ -4,18 +4,17 @@ * Skills provide specialized instructions loaded into the system prompt. * Discover, filter, merge, or replace them. */ - import { createAgentSession, discoverSkills, SessionManager, type Skill } from "@oh-my-pi/pi-coding-agent"; // Discover all skills from cwd/.omp/skills, ~/.omp/agent/skills, etc. const { skills: allSkills } = await discoverSkills(); console.log( "Discovered skills:", - allSkills.map((s) => s.name), + allSkills.map(s => s.name), ); // Filter to specific skills -const filteredSkills = allSkills.filter((s) => s.name.includes("browser") || s.name.includes("search")); +const filteredSkills = allSkills.filter(s => s.name.includes("browser") || s.name.includes("search")); // Or define custom skills inline const customSkill: Skill = { diff --git a/packages/coding-agent/examples/sdk/06-extensions.ts b/packages/coding-agent/examples/sdk/06-extensions.ts index cefa23050..bd7bf279d 100644 --- a/packages/coding-agent/examples/sdk/06-extensions.ts +++ b/packages/coding-agent/examples/sdk/06-extensions.ts @@ -13,7 +13,6 @@ * An extension is a TypeScript file that exports a default function: * export default function (pi: ExtensionAPI) { ... } */ - import { createAgentSession, SessionManager } from "@oh-my-pi/pi-coding-agent"; // Extensions are loaded from disk, not passed inline to createAgentSession. @@ -28,7 +27,7 @@ const { session } = await createAgentSession({ sessionManager: SessionManager.inMemory(), }); -session.subscribe((event) => { +session.subscribe(event => { if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") { process.stdout.write(event.assistantMessageEvent.delta); } diff --git a/packages/coding-agent/examples/sdk/06-hooks.ts b/packages/coding-agent/examples/sdk/06-hooks.ts index 48fa565a4..6dc0d6da8 100644 --- a/packages/coding-agent/examples/sdk/06-hooks.ts +++ b/packages/coding-agent/examples/sdk/06-hooks.ts @@ -4,28 +4,27 @@ * Hooks intercept agent events for logging, blocking, or modification. * Note: "hooks" is now called "extensions" in the API. */ - import { createAgentSession, type ExtensionFactory, SessionManager } from "@oh-my-pi/pi-coding-agent"; // Logging hook (now called extension) -const loggingHook: ExtensionFactory = (api) => { +const loggingHook: ExtensionFactory = api => { api.on("agent_start", async () => { console.log("[Hook] Agent starting"); }); - api.on("tool_call", async (event) => { + api.on("tool_call", async event => { console.log(`[Hook] Tool: ${event.toolName}`); return undefined; // Don't block }); - api.on("agent_end", async (event) => { + api.on("agent_end", async event => { console.log(`[Hook] Done, ${event.messages.length} messages`); }); }; // Blocking extension (returns { block: true, reason: "..." }) -const safetyHook: ExtensionFactory = (api) => { - api.on("tool_call", async (event) => { +const safetyHook: ExtensionFactory = api => { + api.on("tool_call", async event => { if (event.toolName === "bash") { const cmd = (event.input as { command?: string }).command ?? ""; if (cmd.includes("rm -rf")) { @@ -42,7 +41,7 @@ const { session } = await createAgentSession({ sessionManager: SessionManager.inMemory(), }); -session.subscribe((event) => { +session.subscribe(event => { if (event.type === "message_update" && event.assistantMessageEvent.type === "text_delta") { process.stdout.write(event.assistantMessageEvent.delta); } diff --git a/packages/coding-agent/examples/sdk/07-context-files.ts b/packages/coding-agent/examples/sdk/07-context-files.ts index 0bf012963..11ca47a18 100644 --- a/packages/coding-agent/examples/sdk/07-context-files.ts +++ b/packages/coding-agent/examples/sdk/07-context-files.ts @@ -3,7 +3,6 @@ * * Context files provide project-specific instructions loaded into the system prompt. */ - import { createAgentSession, discoverContextFiles, SessionManager } from "@oh-my-pi/pi-coding-agent"; // Discover AGENTS.md files walking up from cwd diff --git a/packages/coding-agent/examples/sdk/08-prompt-templates.ts b/packages/coding-agent/examples/sdk/08-prompt-templates.ts index 84752816a..ff311a369 100644 --- a/packages/coding-agent/examples/sdk/08-prompt-templates.ts +++ b/packages/coding-agent/examples/sdk/08-prompt-templates.ts @@ -3,7 +3,6 @@ * * File-based templates that inject content when invoked with /templatename. */ - import { createAgentSession, discoverPromptTemplates, diff --git a/packages/coding-agent/examples/sdk/08-slash-commands.ts b/packages/coding-agent/examples/sdk/08-slash-commands.ts index 880dc6416..4e808cdf8 100644 --- a/packages/coding-agent/examples/sdk/08-slash-commands.ts +++ b/packages/coding-agent/examples/sdk/08-slash-commands.ts @@ -4,7 +4,6 @@ * File-based commands that inject content when invoked with /commandname. * Note: File-based slash commands are now called "prompt templates". */ - import { createAgentSession, discoverPromptTemplates, diff --git a/packages/coding-agent/examples/sdk/09-api-keys-and-oauth.ts b/packages/coding-agent/examples/sdk/09-api-keys-and-oauth.ts index 4793ef16d..83136eff7 100644 --- a/packages/coding-agent/examples/sdk/09-api-keys-and-oauth.ts +++ b/packages/coding-agent/examples/sdk/09-api-keys-and-oauth.ts @@ -3,7 +3,6 @@ * * Configure API key resolution via AuthStorage and ModelRegistry. */ - import { AuthStorage, createAgentSession, diff --git a/packages/coding-agent/examples/sdk/10-settings.ts b/packages/coding-agent/examples/sdk/10-settings.ts index 360ca77be..7bc40073f 100644 --- a/packages/coding-agent/examples/sdk/10-settings.ts +++ b/packages/coding-agent/examples/sdk/10-settings.ts @@ -3,7 +3,6 @@ * * Override settings using SettingsManager. */ - import { createAgentSession, loadSettings, SessionManager, SettingsManager } from "@oh-my-pi/pi-coding-agent"; // Load current settings (merged global + project) diff --git a/packages/coding-agent/examples/sdk/11-sessions.ts b/packages/coding-agent/examples/sdk/11-sessions.ts index 8255f4508..039618d95 100644 --- a/packages/coding-agent/examples/sdk/11-sessions.ts +++ b/packages/coding-agent/examples/sdk/11-sessions.ts @@ -3,7 +3,6 @@ * * Control session persistence: in-memory, new file, continue, or open specific. */ - import { createAgentSession, SessionManager } from "@oh-my-pi/pi-coding-agent"; // In-memory (no persistence) diff --git a/packages/coding-agent/scripts/format-prompts.ts b/packages/coding-agent/scripts/format-prompts.ts index c5c77520f..b70e0f495 100644 --- a/packages/coding-agent/scripts/format-prompts.ts +++ b/packages/coding-agent/scripts/format-prompts.ts @@ -10,7 +10,6 @@ * 5. Trim trailing whitespace (preserve indentation) * 6. Ensure single newline at EOF */ - import { Glob } from "bun"; const PROMPTS_DIR = new URL("../src/prompts/", import.meta.url).pathname; diff --git a/packages/coding-agent/src/capability/context-file.ts b/packages/coding-agent/src/capability/context-file.ts index 5b016f69e..079b0448e 100644 --- a/packages/coding-agent/src/capability/context-file.ts +++ b/packages/coding-agent/src/capability/context-file.ts @@ -4,7 +4,6 @@ * System instruction files (CLAUDE.md, AGENTS.md, GEMINI.md, etc.) that provide * persistent guidance to the agent. */ - import { defineCapability } from "."; import type { SourceMeta } from "./types"; @@ -30,8 +29,8 @@ export const contextFileCapability = defineCapability({ description: "Persistent instruction files (CLAUDE.md, AGENTS.md, etc.) that guide agent behavior", // Deduplicate by level: one user-level file, one project-level file // Higher-priority providers shadow lower-priority ones at the same scope - key: (file) => file.level, - validate: (file) => { + key: file => file.level, + validate: file => { if (!file.path) return "Missing path"; if (file.content === undefined) return "Missing content"; if (file.level !== "user" && file.level !== "project") return "Invalid level: must be 'user' or 'project'"; diff --git a/packages/coding-agent/src/capability/extension-module.ts b/packages/coding-agent/src/capability/extension-module.ts index b4dc82cd8..4aff4bd88 100644 --- a/packages/coding-agent/src/capability/extension-module.ts +++ b/packages/coding-agent/src/capability/extension-module.ts @@ -3,7 +3,6 @@ * * TypeScript/JavaScript extension modules loaded by the extension system. */ - import { defineCapability } from "."; import type { SourceMeta } from "./types"; @@ -25,8 +24,8 @@ export const extensionModuleCapability = defineCapability({ id: "extension-modules", displayName: "Extension Modules", description: "TypeScript/JavaScript extension modules loaded by the extension system", - key: (ext) => ext.name, - validate: (ext) => { + key: ext => ext.name, + validate: ext => { if (!ext.name) return "Missing name"; if (!ext.path) return "Missing path"; return undefined; diff --git a/packages/coding-agent/src/capability/extension.ts b/packages/coding-agent/src/capability/extension.ts index 48ba9fc89..45a4ecac9 100644 --- a/packages/coding-agent/src/capability/extension.ts +++ b/packages/coding-agent/src/capability/extension.ts @@ -3,7 +3,6 @@ * * Gemini-style extensions that provide MCP servers, tools, and context. */ - import { defineCapability } from "."; import type { MCPServer } from "./mcp"; import type { SourceMeta } from "./types"; @@ -39,8 +38,8 @@ export const extensionCapability = defineCapability({ id: "extensions", displayName: "Extensions", description: "Gemini-style extensions providing MCP servers, tools, and context", - key: (ext) => ext.name, - validate: (ext) => { + key: ext => ext.name, + validate: ext => { if (!ext.name) return "Missing extension name"; if (!ext.path) return "Missing extension path"; return undefined; diff --git a/packages/coding-agent/src/capability/fs.ts b/packages/coding-agent/src/capability/fs.ts index b43993c0b..c21dab5c8 100644 --- a/packages/coding-agent/src/capability/fs.ts +++ b/packages/coding-agent/src/capability/fs.ts @@ -42,7 +42,7 @@ export async function readDirEntries(dirPath: string): Promise { export async function readDir(dirPath: string): Promise { const entries = await readDirEntries(dirPath); - return entries.map((entry) => entry.name); + return entries.map(entry => entry.name); } export async function walkUp( @@ -55,7 +55,7 @@ export async function walkUp( while (true) { const entries = await readDirEntries(current); - const entry = entries.find((e) => e.name === name); + const entry = entries.find(e => e.name === name); if (entry) { if (file && entry.isFile()) return path.join(current, name); if (dir && entry.isDirectory()) return path.join(current, name); diff --git a/packages/coding-agent/src/capability/hook.ts b/packages/coding-agent/src/capability/hook.ts index d02fc7b0d..03b0ac661 100644 --- a/packages/coding-agent/src/capability/hook.ts +++ b/packages/coding-agent/src/capability/hook.ts @@ -3,7 +3,6 @@ * * Pre/post tool execution hooks defined as shell scripts. */ - import { defineCapability } from "."; import type { SourceMeta } from "./types"; @@ -29,8 +28,8 @@ export const hookCapability = defineCapability({ id: "hooks", displayName: "Hooks", description: "Pre/post tool execution hooks", - key: (hook) => `${hook.type}:${hook.tool}:${hook.name}`, - validate: (hook) => { + key: hook => `${hook.type}:${hook.tool}:${hook.name}`, + validate: hook => { if (!hook.name) return "Missing name"; if (!hook.path) return "Missing path"; if (hook.type !== "pre" && hook.type !== "post") return "Invalid type (must be 'pre' or 'post')"; diff --git a/packages/coding-agent/src/capability/index.ts b/packages/coding-agent/src/capability/index.ts index 792ffd428..113ee28fb 100644 --- a/packages/coding-agent/src/capability/index.ts +++ b/packages/coding-agent/src/capability/index.ts @@ -6,7 +6,6 @@ * - Registering providers (where to find it) * - Loading items for a capability across all providers */ - import * as os from "node:os"; import * as path from "node:path"; import { clearCache as clearFsCache, cacheStats as fsCacheStats, invalidate as invalidateFs } from "./fs"; @@ -81,7 +80,7 @@ export function registerProvider(capabilityId: string, provider: Provider) // Insert in priority order (highest first) const providers = capability.providers as Provider[]; - const idx = providers.findIndex((p) => p.priority < provider.priority); + const idx = providers.findIndex(p => p.priority < provider.priority); if (idx === -1) { providers.push(provider); } else { @@ -107,7 +106,7 @@ async function loadImpl( const contributingProviders: string[] = []; const results = await Promise.all( - providers.map(async (provider) => { + providers.map(async provider => { try { const result = await provider.load(ctx); return { provider, result }; @@ -128,7 +127,7 @@ async function loadImpl( if (!result) continue; if (result.warnings) { - allWarnings.push(...result.warnings.map((w) => `[${provider.displayName}] ${w}`)); + allWarnings.push(...result.warnings.map(w => `[${provider.displayName}] ${w}`)); } if (result.items.length > 0) { @@ -190,15 +189,15 @@ async function loadImpl( * Filter providers based on options and disabled state. */ function filterProviders(capability: Capability, options: LoadOptions): Provider[] { - let providers = (capability.providers as Provider[]).filter((p) => !disabledProviders.has(p.id)); + let providers = (capability.providers as Provider[]).filter(p => !disabledProviders.has(p.id)); if (options.providers) { const allowed = new Set(options.providers); - providers = providers.filter((p) => allowed.has(p.id)); + providers = providers.filter(p => allowed.has(p.id)); } if (options.excludeProviders) { const excluded = new Set(options.excludeProviders); - providers = providers.filter((p) => !excluded.has(p.id)); + providers = providers.filter(p => !excluded.has(p.id)); } return providers; @@ -321,7 +320,7 @@ export function getCapabilityInfo(capabilityId: string): CapabilityInfo | undefi id: capability.id, displayName: capability.displayName, description: capability.description, - providers: capability.providers.map((p) => ({ + providers: capability.providers.map(p => ({ id: p.id, displayName: p.displayName, description: p.description, @@ -335,7 +334,7 @@ export function getCapabilityInfo(capabilityId: string): CapabilityInfo | undefi * Get all capabilities info for UI display. */ export function getAllCapabilitiesInfo(): CapabilityInfo[] { - return listCapabilities().map((id) => getCapabilityInfo(id)!); + return listCapabilities().map(id => getCapabilityInfo(id)!); } /** @@ -350,7 +349,7 @@ export function getProviderInfo(providerId: string): ProviderInfo | undefined { let priority = 0; for (const capId of caps) { const cap = capabilities.get(capId); - const provider = cap?.providers.find((p) => p.id === providerId); + const provider = cap?.providers.find(p => p.id === providerId); if (provider) { priority = provider.priority; break; diff --git a/packages/coding-agent/src/capability/instruction.ts b/packages/coding-agent/src/capability/instruction.ts index 04b62a3e4..2122408da 100644 --- a/packages/coding-agent/src/capability/instruction.ts +++ b/packages/coding-agent/src/capability/instruction.ts @@ -3,7 +3,6 @@ * * GitHub Copilot-style instructions with optional file pattern matching. */ - import { defineCapability } from "."; import type { SourceMeta } from "./types"; @@ -27,8 +26,8 @@ export const instructionCapability = defineCapability({ id: "instructions", displayName: "Instructions", description: "File-specific instructions with glob pattern matching (GitHub Copilot format)", - key: (inst) => inst.name, - validate: (inst) => { + key: inst => inst.name, + validate: inst => { if (!inst.name) return "Missing name"; if (!inst.path) return "Missing path"; if (inst.content === undefined) return "Missing content"; diff --git a/packages/coding-agent/src/capability/mcp.ts b/packages/coding-agent/src/capability/mcp.ts index de4314bb2..e90ade79c 100644 --- a/packages/coding-agent/src/capability/mcp.ts +++ b/packages/coding-agent/src/capability/mcp.ts @@ -4,7 +4,6 @@ * Canonical shape for MCP server configurations, regardless of source format. * All providers translate their native format to this shape. */ - import { defineCapability } from "."; import type { SourceMeta } from "./types"; @@ -34,8 +33,8 @@ export const mcpCapability = defineCapability({ id: "mcps", displayName: "MCP Servers", description: "Model Context Protocol server configurations for external tool integrations", - key: (server) => server.name, - validate: (server) => { + key: server => server.name, + validate: server => { if (!server.name) return "Missing server name"; if (!server.command && !server.url) return "Must have command or url"; diff --git a/packages/coding-agent/src/capability/prompt.ts b/packages/coding-agent/src/capability/prompt.ts index 024fe939e..e338bd160 100644 --- a/packages/coding-agent/src/capability/prompt.ts +++ b/packages/coding-agent/src/capability/prompt.ts @@ -3,7 +3,6 @@ * * Reusable prompt templates (Codex format) available via /prompts: menu. */ - import { defineCapability } from "."; import type { SourceMeta } from "./types"; @@ -25,8 +24,8 @@ export const promptCapability = defineCapability({ id: "prompts", displayName: "Prompts", description: "Reusable prompt templates available via /prompts: menu", - key: (prompt) => prompt.name, - validate: (prompt) => { + key: prompt => prompt.name, + validate: prompt => { if (!prompt.name) return "Missing name"; if (!prompt.path) return "Missing path"; if (prompt.content === undefined) return "Missing content"; diff --git a/packages/coding-agent/src/capability/rule.ts b/packages/coding-agent/src/capability/rule.ts index 3da252d27..c8c430baa 100644 --- a/packages/coding-agent/src/capability/rule.ts +++ b/packages/coding-agent/src/capability/rule.ts @@ -4,7 +4,6 @@ * Project-specific rules from Cursor (.mdc), Windsurf (.md), and Cline formats. * Translated to a canonical shape regardless of source format. */ - import { defineCapability } from "."; import type { SourceMeta } from "./types"; @@ -46,8 +45,8 @@ export const ruleCapability = defineCapability({ id: "rules", displayName: "Rules", description: "Project-specific rules and constraints (Cursor MDC, Windsurf, Cline formats)", - key: (rule) => rule.name, - validate: (rule) => { + key: rule => rule.name, + validate: rule => { if (!rule.name) return "Missing rule name"; if (!rule.path) return "Missing rule path"; if (!rule.content || typeof rule.content !== "string") return "Rule must have content"; diff --git a/packages/coding-agent/src/capability/settings.ts b/packages/coding-agent/src/capability/settings.ts index ef95732ff..efbc16a7d 100644 --- a/packages/coding-agent/src/capability/settings.ts +++ b/packages/coding-agent/src/capability/settings.ts @@ -3,7 +3,6 @@ * * Configuration settings from various sources (JSON, TOML, etc.) */ - import { defineCapability } from "."; import type { SourceMeta } from "./types"; @@ -27,7 +26,7 @@ export const settingsCapability = defineCapability({ description: "Configuration settings from various sources", // Settings are merged, not deduplicated by key key: () => undefined, - validate: (settings) => { + validate: settings => { if (!settings.path) return "Missing path"; if (!settings.data || typeof settings.data !== "object") return "Missing or invalid data"; return undefined; diff --git a/packages/coding-agent/src/capability/skill.ts b/packages/coding-agent/src/capability/skill.ts index b03e26bd5..5be9734f4 100644 --- a/packages/coding-agent/src/capability/skill.ts +++ b/packages/coding-agent/src/capability/skill.ts @@ -3,7 +3,6 @@ * * Skills provide specialized knowledge or workflows that extend agent capabilities. */ - import { defineCapability } from "."; import type { SourceMeta } from "./types"; @@ -40,8 +39,8 @@ export const skillCapability = defineCapability({ id: "skills", displayName: "Skills", description: "Specialized knowledge and workflow files that extend agent capabilities", - key: (skill) => skill.name, - validate: (skill) => { + key: skill => skill.name, + validate: skill => { if (!skill.name) return "Missing skill name"; if (!skill.path) return "Missing skill path"; return undefined; diff --git a/packages/coding-agent/src/capability/slash-command.ts b/packages/coding-agent/src/capability/slash-command.ts index e84013cc3..99e9d8a1f 100644 --- a/packages/coding-agent/src/capability/slash-command.ts +++ b/packages/coding-agent/src/capability/slash-command.ts @@ -3,7 +3,6 @@ * * File-based slash commands defined as markdown files. */ - import { defineCapability } from "."; import type { SourceMeta } from "./types"; @@ -27,8 +26,8 @@ export const slashCommandCapability = defineCapability({ id: "slash-commands", displayName: "Slash Commands", description: "Custom slash commands defined as markdown files", - key: (cmd) => cmd.name, - validate: (cmd) => { + key: cmd => cmd.name, + validate: cmd => { if (!cmd.name) return "Missing name"; if (!cmd.path) return "Missing path"; if (cmd.content === undefined) return "Missing content"; diff --git a/packages/coding-agent/src/capability/ssh.ts b/packages/coding-agent/src/capability/ssh.ts index 4037915c5..aac8bc328 100644 --- a/packages/coding-agent/src/capability/ssh.ts +++ b/packages/coding-agent/src/capability/ssh.ts @@ -3,7 +3,6 @@ * * Canonical shape for SSH host entries, regardless of source format. */ - import { defineCapability } from "."; import type { SourceMeta } from "./types"; @@ -33,8 +32,8 @@ export const sshCapability = defineCapability({ id: "ssh", displayName: "SSH Hosts", description: "SSH host entries for remote command execution", - key: (host) => host.name, - validate: (host) => { + key: host => host.name, + validate: host => { if (!host.name) return "Missing name"; if (!host.host) return "Missing host"; return undefined; diff --git a/packages/coding-agent/src/capability/system-prompt.ts b/packages/coding-agent/src/capability/system-prompt.ts index f5ab8f247..514e1a1cd 100644 --- a/packages/coding-agent/src/capability/system-prompt.ts +++ b/packages/coding-agent/src/capability/system-prompt.ts @@ -4,7 +4,6 @@ * Custom system prompt files (SYSTEM.md) that modify the agent's base system prompt. * Distinct from context-files which are user instructions shown in conversation. */ - import { defineCapability } from "."; import type { SourceMeta } from "./types"; @@ -26,8 +25,8 @@ export const systemPromptCapability = defineCapability({ id: "system-prompt", displayName: "System Prompt", description: "Custom system prompt files (SYSTEM.md) that modify agent behavior", - key: (sp) => sp.level, - validate: (sp) => { + key: sp => sp.level, + validate: sp => { if (!sp.path) return "Missing path"; if (sp.content === undefined) return "Missing content"; return undefined; diff --git a/packages/coding-agent/src/capability/tool.ts b/packages/coding-agent/src/capability/tool.ts index 2d66c6780..823dee824 100644 --- a/packages/coding-agent/src/capability/tool.ts +++ b/packages/coding-agent/src/capability/tool.ts @@ -3,7 +3,6 @@ * * User-defined tools that extend agent capabilities. */ - import { defineCapability } from "."; import type { SourceMeta } from "./types"; @@ -29,8 +28,8 @@ export const toolCapability = defineCapability({ id: "tools", displayName: "Custom Tools", description: "User-defined tools that extend agent capabilities", - key: (tool) => tool.name, - validate: (tool) => { + key: tool => tool.name, + validate: tool => { if (!tool.name) return "Missing name"; if (!tool.path) return "Missing path"; return undefined; diff --git a/packages/coding-agent/src/cli/args.ts b/packages/coding-agent/src/cli/args.ts index dd9a71f75..735a88e48 100644 --- a/packages/coding-agent/src/cli/args.ts +++ b/packages/coding-agent/src/cli/args.ts @@ -1,7 +1,6 @@ /** * CLI argument parsing and help display */ - import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core"; import chalk from "chalk"; import { APP_NAME, CONFIG_DIR_NAME, ENV_AGENT_DIR } from "../config"; @@ -99,13 +98,13 @@ export function parseArgs(args: string[], extensionFlags?: Map s.trim()); + result.models = args[++i].split(",").map(s => s.trim()); } else if (arg === "--no-tools") { result.noTools = true; } else if (arg === "--no-lsp") { result.noLsp = true; } else if (arg === "--tools" && i + 1 < args.length) { - const toolNames = args[++i].split(",").map((s) => s.trim()); + const toolNames = args[++i].split(",").map(s => s.trim()); const validTools: string[] = []; for (const name of toolNames) { if (name in BUILTIN_TOOLS) { @@ -148,7 +147,7 @@ export function parseArgs(args: string[], extensionFlags?: Map s.trim()); + result.skills = args[++i].split(",").map(s => s.trim()); } else if (arg === "--list-models") { // Check if next arg is a search pattern (not a flag or file arg) if (i + 1 < args.length && !args[i + 1].startsWith("-") && !args[i + 1].startsWith("@")) { diff --git a/packages/coding-agent/src/cli/config-cli.ts b/packages/coding-agent/src/cli/config-cli.ts index 76f3fc1aa..458709b10 100644 --- a/packages/coding-agent/src/cli/config-cli.ts +++ b/packages/coding-agent/src/cli/config-cli.ts @@ -4,7 +4,6 @@ * Handles `omp config ` subcommands for managing settings. * Uses SETTINGS_DEFS as the source of truth for available settings. */ - import chalk from "chalk"; import { APP_NAME, getAgentDir } from "../config"; import { SettingsManager } from "../config/settings-manager"; @@ -32,7 +31,7 @@ export interface ConfigCommandArgs { /** Find setting definition by ID */ function findSettingDef(id: string): SettingDef | undefined { - return SETTINGS_DEFS.find((def) => def.id === id); + return SETTINGS_DEFS.find(def => def.id === id); } /** Get available values for a setting */ @@ -43,7 +42,7 @@ function getSettingValues(def: SettingDef, sm: SettingsManager): readonly string if (def.type === "submenu") { const options = def.getOptions(sm); if (options.length > 0) { - return options.map((o) => o.value); + return options.map(o => o.value); } } return undefined; diff --git a/packages/coding-agent/src/cli/file-processor.ts b/packages/coding-agent/src/cli/file-processor.ts index 46791ef76..9a978d488 100644 --- a/packages/coding-agent/src/cli/file-processor.ts +++ b/packages/coding-agent/src/cli/file-processor.ts @@ -1,7 +1,6 @@ /** * Process @file CLI arguments into text content and image attachments */ - import * as fs from "node:fs/promises"; import * as path from "node:path"; import type { ImageContent } from "@oh-my-pi/pi-ai"; diff --git a/packages/coding-agent/src/cli/jupyter-cli.ts b/packages/coding-agent/src/cli/jupyter-cli.ts index 7e8b57382..d2454c838 100644 --- a/packages/coding-agent/src/cli/jupyter-cli.ts +++ b/packages/coding-agent/src/cli/jupyter-cli.ts @@ -3,7 +3,6 @@ * * Handles `omp jupyter` subcommand for managing the shared Python gateway. */ - import chalk from "chalk"; import { APP_NAME } from "../config"; import { getGatewayStatus, shutdownSharedGateway } from "../ipy/gateway-coordinator"; diff --git a/packages/coding-agent/src/cli/list-models.ts b/packages/coding-agent/src/cli/list-models.ts index 46f7295ac..6634ade1b 100644 --- a/packages/coding-agent/src/cli/list-models.ts +++ b/packages/coding-agent/src/cli/list-models.ts @@ -1,7 +1,6 @@ /** * List available models with optional fuzzy search */ - import type { Api, Model } from "@oh-my-pi/pi-ai"; import type { ModelRegistry } from "../config/model-registry"; import { fuzzyFilter } from "../utils/fuzzy"; @@ -35,7 +34,7 @@ export async function listModels(modelRegistry: ModelRegistry, searchPattern?: s // Apply fuzzy filter if search pattern provided let filteredModels: Model[] = models; if (searchPattern) { - filteredModels = fuzzyFilter(models, searchPattern, (m) => `${m.provider} ${m.id}`); + filteredModels = fuzzyFilter(models, searchPattern, m => `${m.provider} ${m.id}`); } if (filteredModels.length === 0) { @@ -51,7 +50,7 @@ export async function listModels(modelRegistry: ModelRegistry, searchPattern?: s }); // Calculate column widths - const rows = filteredModels.map((m) => ({ + const rows = filteredModels.map(m => ({ provider: m.provider, model: m.id, context: formatTokenCount(m.contextWindow), @@ -70,12 +69,12 @@ export async function listModels(modelRegistry: ModelRegistry, searchPattern?: s }; const widths = { - provider: Math.max(headers.provider.length, ...rows.map((r) => r.provider.length)), - model: Math.max(headers.model.length, ...rows.map((r) => r.model.length)), - context: Math.max(headers.context.length, ...rows.map((r) => r.context.length)), - maxOut: Math.max(headers.maxOut.length, ...rows.map((r) => r.maxOut.length)), - thinking: Math.max(headers.thinking.length, ...rows.map((r) => r.thinking.length)), - images: Math.max(headers.images.length, ...rows.map((r) => r.images.length)), + provider: Math.max(headers.provider.length, ...rows.map(r => r.provider.length)), + model: Math.max(headers.model.length, ...rows.map(r => r.model.length)), + context: Math.max(headers.context.length, ...rows.map(r => r.context.length)), + maxOut: Math.max(headers.maxOut.length, ...rows.map(r => r.maxOut.length)), + thinking: Math.max(headers.thinking.length, ...rows.map(r => r.thinking.length)), + images: Math.max(headers.images.length, ...rows.map(r => r.images.length)), }; // Print header diff --git a/packages/coding-agent/src/cli/plugin-cli.ts b/packages/coding-agent/src/cli/plugin-cli.ts index c5ae95c7b..458e233ca 100644 --- a/packages/coding-agent/src/cli/plugin-cli.ts +++ b/packages/coding-agent/src/cli/plugin-cli.ts @@ -3,7 +3,6 @@ * * Handles `omp plugin ` subcommands for plugin lifecycle management. */ - import chalk from "chalk"; import { APP_NAME } from "../config"; import { PluginManager, parseSettingValue, validateSetting } from "../extensibility/plugins"; @@ -247,7 +246,7 @@ async function handleList(manager: PluginManager, flags: { json?: boolean }): Pr if (availableFeatures.length > 0) { const enabledSet = new Set(plugin.enabledFeatures ?? []); const featureDisplay = availableFeatures - .map((f) => (enabledSet.has(f) ? chalk.green(f) : chalk.dim(f))) + .map(f => (enabledSet.has(f) ? chalk.green(f) : chalk.dim(f))) .join(", "); console.log(chalk.dim(` Available: [${featureDisplay}]`)); } @@ -298,10 +297,10 @@ async function handleDoctor(manager: PluginManager, flags: { json?: boolean; fix } } - const errors = checks.filter((c) => c.status === "error" && !c.fixed).length; - const warnings = checks.filter((c) => c.status === "warning" && !c.fixed).length; - const ok = checks.filter((c) => c.status === "ok").length; - const fixed = checks.filter((c) => c.fixed).length; + const errors = checks.filter(c => c.status === "error" && !c.fixed).length; + const warnings = checks.filter(c => c.status === "warning" && !c.fixed).length; + const ok = checks.filter(c => c.status === "ok").length; + const fixed = checks.filter(c => c.fixed).length; console.log(""); console.log(`Summary: ${ok} ok, ${warnings} warnings, ${errors} errors${fixed > 0 ? `, ${fixed} fixed` : ""}`); @@ -328,7 +327,7 @@ async function handleFeatures( const pluginName = args[0]; const plugins = await manager.list(); - const plugin = plugins.find((p) => p.name === pluginName); + const plugin = plugins.find(p => p.name === pluginName); if (!plugin) { console.error(chalk.red(`Plugin "${pluginName}" not found`)); @@ -344,14 +343,14 @@ async function handleFeatures( currentFeatures = new Set( flags.set .split(",") - .map((f) => f.trim()) + .map(f => f.trim()) .filter(Boolean), ); } else { if (flags.enable) { for (const f of flags.enable .split(",") - .map((f) => f.trim()) + .map(f => f.trim()) .filter(Boolean)) { currentFeatures.add(f); } @@ -359,7 +358,7 @@ async function handleFeatures( if (flags.disable) { for (const f of flags.disable .split(",") - .map((f) => f.trim()) + .map(f => f.trim()) .filter(Boolean)) { currentFeatures.delete(f); } @@ -433,7 +432,7 @@ async function handleConfig( } const plugins = await manager.list(); - const plugin = plugins.find((p) => p.name === pluginName); + const plugin = plugins.find(p => p.name === pluginName); if (!plugin) { console.error(chalk.red(`Plugin "${pluginName}" not found`)); diff --git a/packages/coding-agent/src/cli/session-picker.ts b/packages/coding-agent/src/cli/session-picker.ts index 6c2a2d8e2..826ab7cd4 100644 --- a/packages/coding-agent/src/cli/session-picker.ts +++ b/packages/coding-agent/src/cli/session-picker.ts @@ -1,7 +1,6 @@ /** * TUI session selector for --resume flag */ - import { ProcessTerminal, TUI } from "@oh-my-pi/pi-tui"; import { SessionSelectorComponent } from "../modes/components/session-selector"; import type { SessionInfo } from "../session/session-manager"; diff --git a/packages/coding-agent/src/cli/setup-cli.ts b/packages/coding-agent/src/cli/setup-cli.ts index 82e7c421d..09d111515 100644 --- a/packages/coding-agent/src/cli/setup-cli.ts +++ b/packages/coding-agent/src/cli/setup-cli.ts @@ -3,7 +3,6 @@ * * Handles `omp setup ` to install dependencies for optional features. */ - import { $ } from "bun"; import chalk from "chalk"; import { APP_NAME } from "../config"; diff --git a/packages/coding-agent/src/cli/stats-cli.ts b/packages/coding-agent/src/cli/stats-cli.ts index 4bb7d0be3..1cb668645 100644 --- a/packages/coding-agent/src/cli/stats-cli.ts +++ b/packages/coding-agent/src/cli/stats-cli.ts @@ -3,7 +3,6 @@ * * Handles `omp stats` subcommand for viewing AI usage statistics. */ - import chalk from "chalk"; import { APP_NAME } from "../config"; diff --git a/packages/coding-agent/src/cli/update-cli.ts b/packages/coding-agent/src/cli/update-cli.ts index aa431b804..f19707f2a 100644 --- a/packages/coding-agent/src/cli/update-cli.ts +++ b/packages/coding-agent/src/cli/update-cli.ts @@ -4,7 +4,6 @@ * Handles `omp update` to check for and install updates. * Uses bun if available, otherwise downloads binary from GitHub releases. */ - import { execSync, spawnSync } from "node:child_process"; import * as fs from "node:fs"; import * as path from "node:path"; @@ -74,7 +73,7 @@ async function getLatestRelease(): Promise { return { tag: data.tag_name, version: data.tag_name.replace(/^v/, ""), - assets: data.assets.map((a) => ({ name: a.name, url: a.browser_download_url })), + assets: data.assets.map(a => ({ name: a.name, url: a.browser_download_url })), }; } @@ -155,7 +154,7 @@ async function updateViaBun(): Promise { */ async function updateViaBinary(release: ReleaseInfo): Promise { const binaryName = getBinaryName(); - const asset = release.assets.find((a) => a.name === binaryName); + const asset = release.assets.find(a => a.name === binaryName); if (!asset) { throw new Error(`No binary found for ${binaryName}`); diff --git a/packages/coding-agent/src/commit/agentic/agent.ts b/packages/coding-agent/src/commit/agentic/agent.ts index e5ad14bd0..54c3451c9 100644 --- a/packages/coding-agent/src/commit/agentic/agent.ts +++ b/packages/coding-agent/src/commit/agentic/agent.ts @@ -174,8 +174,8 @@ function writeStdout(message: string): void { function extractMessagePreview(content: Array<{ type: string; text?: string }>): string | null { const textBlocks = content - .filter((block) => block.type === "text" && typeof block.text === "string") - .map((block) => block.text?.trim()) + .filter(block => block.type === "text" && typeof block.text === "string") + .map(block => block.text?.trim()) .filter((value): value is string => Boolean(value)); if (textBlocks.length === 0) return null; const combined = textBlocks.join(" ").replace(/\s+/g, " ").trim(); @@ -184,9 +184,9 @@ function extractMessagePreview(content: Array<{ type: string; text?: string }>): function extractMessageText(content: Array<{ type: string; text?: string }>): string | null { const textBlocks = content - .filter((block) => block.type === "text" && typeof block.text === "string") - .map((block) => block.text ?? "") - .filter((value) => value.trim().length > 0); + .filter(block => block.type === "text" && typeof block.text === "string") + .map(block => block.text ?? "") + .filter(value => value.trim().length > 0); if (textBlocks.length === 0) return null; return textBlocks.join("\n").trim(); } @@ -194,7 +194,7 @@ function extractMessageText(content: Array<{ type: string; text?: string }>): st function writeAssistantMessage(message: string): void { const lines = renderMarkdownLines(message); if (lines.length === 0) return; - let firstContentIndex = lines.findIndex((line) => line.trim().length > 0); + let firstContentIndex = lines.findIndex(line => line.trim().length > 0); if (firstContentIndex === -1) { firstContentIndex = 0; } @@ -213,7 +213,7 @@ function renderMarkdownLines(message: string): string[] { function formatToolLabel(toolName: string): string { const displayName = toolName .split(/[_-]/) - .map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1)) + .map(segment => segment.charAt(0).toUpperCase() + segment.slice(1)) .join(""); return displayName; } @@ -225,7 +225,7 @@ function formatToolArgs(args?: Record): string[] { if (value === null || value === undefined) return; if (Array.isArray(value)) { if (value.length === 0) return; - const rendered = value.map((item) => renderPrimitive(item)).filter(Boolean); + const rendered = value.map(item => renderPrimitive(item)).filter(Boolean); if (rendered.length > 0) { lines.push(`${keyPath}: ${rendered.join(", ")}`); } diff --git a/packages/coding-agent/src/commit/agentic/fallback.ts b/packages/coding-agent/src/commit/agentic/fallback.ts index 728ad65ab..9d5bda3cc 100644 --- a/packages/coding-agent/src/commit/agentic/fallback.ts +++ b/packages/coding-agent/src/commit/agentic/fallback.ts @@ -20,7 +20,7 @@ function inferTypeFromFiles(numstat: NumstatEntry[]): CommitType { const lowerPath = entry.path.toLowerCase(); const ext = getExtension(entry.path); - if (TEST_PATTERNS.some((pattern) => lowerPath.includes(pattern))) { + if (TEST_PATTERNS.some(pattern => lowerPath.includes(pattern))) { hasTests = true; } else if (DOC_EXTENSIONS.has(ext)) { hasDocs = true; @@ -48,7 +48,7 @@ function getExtension(filePath: string): string { export function generateFallbackAnalysis(numstat: NumstatEntry[]): ConventionalAnalysis { const type = inferTypeFromFiles(numstat); - const details = numstat.slice(0, 3).map((e) => ({ + const details = numstat.slice(0, 3).map(e => ({ text: `Updated ${path.basename(e.path)}`, userVisible: false, })); diff --git a/packages/coding-agent/src/commit/agentic/index.ts b/packages/coding-agent/src/commit/agentic/index.ts index 275927a10..52ded396e 100644 --- a/packages/coding-agent/src/commit/agentic/index.ts +++ b/packages/coding-agent/src/commit/agentic/index.ts @@ -61,7 +61,7 @@ export async function runAgenticCommit(args: CommitCommandArgs): Promise { git.getNumstat(true), git.getDiff(true), ]); - const changelogTargets = changelogBoundaries.map((boundary) => boundary.changelogPath); + const changelogTargets = changelogBoundaries.map(boundary => boundary.changelogPath); if (!args.noChangelog) { if (changelogTargets.length > 0) { for (const path of changelogTargets) { @@ -73,7 +73,7 @@ export async function runAgenticCommit(args: CommitCommandArgs): Promise { } writeStdout("● Discovering context files..."); - const agentsMdFiles = contextFiles.filter((file) => file.path.endsWith("AGENTS.md")); + const agentsMdFiles = contextFiles.filter(file => file.path.endsWith("AGENTS.md")); if (agentsMdFiles.length > 0) { for (const file of agentsMdFiles) { writeStdout(` └─ ${file.path}`); @@ -160,11 +160,11 @@ export async function runAgenticCommit(args: CommitCommandArgs): Promise { cwd, proposals: commitState.changelogProposal.entries, dryRun: args.dryRun, - onProgress: (message) => { + onProgress: message => { writeStdout(` ├─ ${message}`); }, }); - updatedChangelogFiles = updated.map((filePath) => path.relative(cwd, filePath)); + updatedChangelogFiles = updated.map(filePath => path.relative(cwd, filePath)); if (updated.length > 0) { for (const filePath of updated) { writeStdout(` └─ ${filePath}`); @@ -221,8 +221,8 @@ async function runSplitCommit( appendFilesToLastCommit(plan, ctx.additionalFiles); } const stagedFiles = await ctx.git.getStagedFiles(); - const plannedFiles = new Set(plan.commits.flatMap((commit) => commit.changes.map((change) => change.path))); - const missingFiles = stagedFiles.filter((file) => !plannedFiles.has(file)); + const plannedFiles = new Set(plan.commits.flatMap(commit => commit.changes.map(change => change.path))); + const missingFiles = stagedFiles.filter(file => !plannedFiles.has(file)); if (missingFiles.length > 0) { writeStderr(`Split commit plan missing staged files: ${missingFiles.join(", ")}`); return; @@ -240,7 +240,7 @@ async function runSplitCommit( const message = formatCommitMessage(analysis, commit.summary); writeStdout(`Commit ${index + 1}:\n${message}\n`); const changeSummary = commit.changes - .map((change) => formatFileChangeSummary(change.path, change.hunks)) + .map(change => formatFileChangeSummary(change.path, change.hunks)) .join(", "); writeStdout(`Changes: ${changeSummary}\n`); } @@ -280,7 +280,7 @@ async function runSplitCommit( function appendFilesToLastCommit(plan: SplitCommitPlan, files: string[]): void { if (plan.commits.length === 0) return; - const planned = new Set(plan.commits.flatMap((commit) => commit.changes.map((change) => change.path))); + const planned = new Set(plan.commits.flatMap(commit => commit.changes.map(change => change.path))); const targetCommit = plan.commits[plan.commits.length - 1]; for (const file of files) { if (planned.has(file)) continue; @@ -304,7 +304,7 @@ async function confirmSplitCommitPlan(plan: SplitCommitPlan): Promise { } function formatWarnings(warnings: string[]): string { - return `Warnings:\n${warnings.map((warning) => `- ${warning}`).join("\n")}`; + return `Warnings:\n${warnings.map(warning => `- ${warning}`).join("\n")}`; } function writeStdout(message: string): void { @@ -327,7 +327,7 @@ function formatFileChangeSummary(path: string, hunks: HunkSelector): string { async function loadExistingChangelogEntries(paths: string[]): Promise { const entries = await Promise.all( - paths.map(async (path) => { + paths.map(async path => { const file = Bun.file(path); if (!(await file.exists())) { return null; diff --git a/packages/coding-agent/src/commit/agentic/tools/analyze-file.ts b/packages/coding-agent/src/commit/agentic/tools/analyze-file.ts index a890465e6..713a1bde5 100644 --- a/packages/coding-agent/src/commit/agentic/tools/analyze-file.ts +++ b/packages/coding-agent/src/commit/agentic/tools/analyze-file.ts @@ -112,12 +112,12 @@ function inferFileType(path: string): string { } function formatRelatedFiles(files: string[], currentFile: string, numstat: NumstatEntry[]): string | undefined { - const others = files.filter((file) => file !== currentFile); + const others = files.filter(file => file !== currentFile); if (others.length === 0) return undefined; - const numstatMap = new Map(numstat.map((entry) => [entry.path, entry])); + const numstatMap = new Map(numstat.map(entry => [entry.path, entry])); - const lines = others.map((file) => { + const lines = others.map(file => { const entry = numstatMap.get(file); const fileType = inferFileType(file); if (entry) { diff --git a/packages/coding-agent/src/commit/agentic/tools/git-hunk.ts b/packages/coding-agent/src/commit/agentic/tools/git-hunk.ts index 6c7ad024f..9e6b528e0 100644 --- a/packages/coding-agent/src/commit/agentic/tools/git-hunk.ts +++ b/packages/coding-agent/src/commit/agentic/tools/git-hunk.ts @@ -11,8 +11,8 @@ const gitHunkSchema = Type.Object({ function selectHunks(fileHunks: FileHunks, requested?: number[]): DiffHunk[] { if (!requested || requested.length === 0) return fileHunks.hunks; - const wanted = new Set(requested.map((value) => Math.max(1, Math.floor(value)))); - return fileHunks.hunks.filter((hunk) => wanted.has(hunk.index + 1)); + const wanted = new Set(requested.map(value => Math.max(1, Math.floor(value)))); + return fileHunks.hunks.filter(hunk => wanted.has(hunk.index + 1)); } export function createGitHunkTool(git: ControlledGit): CustomTool { @@ -24,7 +24,7 @@ export function createGitHunkTool(git: ControlledGit): CustomTool entry.filename === params.file) ?? { + const fileHunks = hunks.find(entry => entry.filename === params.file) ?? { filename: params.file, isBinary: false, hunks: [], @@ -36,7 +36,7 @@ export function createGitHunkTool(git: ControlledGit): CustomTool hunk.content).join("\n\n") : "(no matching hunks)"; + const text = selected.length ? selected.map(hunk => hunk.content).join("\n\n") : "(no matching hunks)"; return { content: [{ type: "text", text }], details: { diff --git a/packages/coding-agent/src/commit/agentic/tools/git-overview.ts b/packages/coding-agent/src/commit/agentic/tools/git-overview.ts index de956c77e..9451ea630 100644 --- a/packages/coding-agent/src/commit/agentic/tools/git-overview.ts +++ b/packages/coding-agent/src/commit/agentic/tools/git-overview.ts @@ -62,7 +62,7 @@ export function createGitOverviewTool( const { filtered: files, excluded } = filterExcludedFiles(allFiles); const stat = await git.getStat(staged); const allNumstat = await git.getNumstat(staged); - const numstat = allNumstat.filter((entry) => !isExcludedFile(entry.path)); + const numstat = allNumstat.filter(entry => !isExcludedFile(entry.path)); const scopeResult = extractScopeCandidates(numstat); const untrackedFiles = !staged && params.include_untracked ? await git.getUntrackedFiles() : undefined; const snapshot: GitOverviewSnapshot = { diff --git a/packages/coding-agent/src/commit/agentic/tools/propose-changelog.ts b/packages/coding-agent/src/commit/agentic/tools/propose-changelog.ts index bd07c974c..0e9a8f6f6 100644 --- a/packages/coding-agent/src/commit/agentic/tools/propose-changelog.ts +++ b/packages/coding-agent/src/commit/agentic/tools/propose-changelog.ts @@ -48,14 +48,14 @@ export function createProposeChangelogTool( const targets = new Set(changelogTargets); const seen = new Set(); - const normalized = params.entries.map((entry) => { + const normalized = params.entries.map(entry => { const cleaned: Record = {}; for (const [category, values] of Object.entries(entry.entries ?? {})) { if (!allowedCategories.has(category as ChangelogCategory)) { errors.push(`Unknown changelog category for ${entry.path}: ${category}`); continue; } - const items = values.map((value) => value.trim().replace(/\.$/, "")).filter((value) => value.length > 0); + const items = values.map(value => value.trim().replace(/\.$/, "")).filter(value => value.length > 0); if (items.length > 0) { cleaned[category] = Array.from(new Set(items)); } @@ -69,7 +69,7 @@ export function createProposeChangelogTool( errors.push(`Unknown deletion category for ${entry.path}: ${category}`); continue; } - const items = values.map((value) => value.trim()).filter((value) => value.length > 0); + const items = values.map(value => value.trim()).filter(value => value.length > 0); if (items.length > 0) { cleanedDeletions[category] = Array.from(new Set(items)); } diff --git a/packages/coding-agent/src/commit/agentic/tools/propose-commit.ts b/packages/coding-agent/src/commit/agentic/tools/propose-commit.ts index 71447c7b1..6ad020bd8 100644 --- a/packages/coding-agent/src/commit/agentic/tools/propose-commit.ts +++ b/packages/coding-agent/src/commit/agentic/tools/propose-commit.ts @@ -71,7 +71,7 @@ function normalizeDetails( user_visible?: boolean; }>, ): ConventionalDetail[] { - return details.map((detail) => ({ + return details.map(detail => ({ text: detail.text.trim(), changelogCategory: detail.user_visible ? detail.changelog_category : undefined, userVisible: detail.user_visible ?? false, diff --git a/packages/coding-agent/src/commit/agentic/tools/split-commit.ts b/packages/coding-agent/src/commit/agentic/tools/split-commit.ts index f5d7ef274..939f481b6 100644 --- a/packages/coding-agent/src/commit/agentic/tools/split-commit.ts +++ b/packages/coding-agent/src/commit/agentic/tools/split-commit.ts @@ -85,7 +85,7 @@ function normalizeDetails( user_visible?: boolean; }>, ): ConventionalDetail[] { - return details.map((detail) => ({ + return details.map(detail => ({ text: detail.text.trim(), changelogCategory: detail.user_visible ? detail.changelog_category : undefined, userVisible: detail.user_visible ?? false, @@ -116,14 +116,14 @@ export function createSplitCommitTool( const summary = normalizeSummary(commit.summary, commit.type, scope); const detailInput = normalizeDetails(commit.details ?? []); const detailResult = capDetails(detailInput); - warnings.push(...detailResult.warnings.map((warning) => `Commit ${index + 1}: ${warning}`)); + warnings.push(...detailResult.warnings.map(warning => `Commit ${index + 1}: ${warning}`)); const issueRefs = commit.issue_refs ?? []; - const dependencies = (commit.dependencies ?? []).map((dep) => Math.floor(dep)); - const changes = commit.changes.map((change) => ({ + const dependencies = (commit.dependencies ?? []).map(dep => Math.floor(dep)); + const changes = commit.changes.map(change => ({ path: change.path, hunks: change.hunks, })); - const files = changes.map((change) => change.path); + const files = changes.map(change => change.path); const summaryValidation = validateSummaryRules(summary); const scopeValidation = validateScope(scope); @@ -134,16 +134,16 @@ export function createSplitCommitTool( }); if (summaryValidation.errors.length > 0) { - errors.push(...summaryValidation.errors.map((error) => `Commit ${index + 1}: ${error}`)); + errors.push(...summaryValidation.errors.map(error => `Commit ${index + 1}: ${error}`)); } if (!scopeValidation.valid) { - errors.push(...scopeValidation.errors.map((error) => `Commit ${index + 1}: ${error}`)); + errors.push(...scopeValidation.errors.map(error => `Commit ${index + 1}: ${error}`)); } if (typeValidation.errors.length > 0) { - errors.push(...typeValidation.errors.map((error) => `Commit ${index + 1}: ${error}`)); + errors.push(...typeValidation.errors.map(error => `Commit ${index + 1}: ${error}`)); } - warnings.push(...summaryValidation.warnings.map((warning) => `Commit ${index + 1}: ${warning}`)); - warnings.push(...typeValidation.warnings.map((warning) => `Commit ${index + 1}: ${warning}`)); + warnings.push(...summaryValidation.warnings.map(warning => `Commit ${index + 1}: ${warning}`)); + warnings.push(...typeValidation.warnings.map(warning => `Commit ${index + 1}: ${warning}`)); const hunkValidation = validateHunkSelectors(index, changes, files); warnings.push(...hunkValidation.warnings); errors.push(...hunkValidation.errors); @@ -239,7 +239,7 @@ function validateHunkSelectors( for (const change of changes) { if (change.hunks.type === "indices") { const invalid = change.hunks.indices.filter( - (value) => !Number.isFinite(value) || Math.floor(value) !== value || value < 1, + value => !Number.isFinite(value) || Math.floor(value) !== value || value < 1, ); if (invalid.length > 0) { errors.push(`${prefix}: invalid hunk indices for ${change.path}`); diff --git a/packages/coding-agent/src/commit/agentic/trivial.ts b/packages/coding-agent/src/commit/agentic/trivial.ts index 5e3d3303a..0dff6f4d5 100644 --- a/packages/coding-agent/src/commit/agentic/trivial.ts +++ b/packages/coding-agent/src/commit/agentic/trivial.ts @@ -12,9 +12,9 @@ const EMPTY_LINE_PATTERN = /^[-+]\s*$/; export function detectTrivialChange(diff: string): TrivialChangeResult | null { const lines = diff.split("\n"); - const changeLines = lines.filter((line) => line.startsWith("+") || line.startsWith("-")); + const changeLines = lines.filter(line => line.startsWith("+") || line.startsWith("-")); const contentLines = changeLines.filter( - (line) => !line.startsWith("+++") && !line.startsWith("---") && !line.startsWith("@@"), + line => !line.startsWith("+++") && !line.startsWith("---") && !line.startsWith("@@"), ); if (contentLines.length === 0) return null; diff --git a/packages/coding-agent/src/commit/agentic/validation.ts b/packages/coding-agent/src/commit/agentic/validation.ts index 77b668ca1..56408e9b0 100644 --- a/packages/coding-agent/src/commit/agentic/validation.ts +++ b/packages/coding-agent/src/commit/agentic/validation.ts @@ -129,7 +129,7 @@ export function capDetails(details: ConventionalDetail[]): { details: Convention })); scored.sort((a, b) => b.score - a.score || a.index - b.index); - const keep = new Set(scored.slice(0, MAX_DETAIL_ITEMS).map((entry) => entry.index)); + const keep = new Set(scored.slice(0, MAX_DETAIL_ITEMS).map(entry => entry.index)); const kept = details.filter((_detail, index) => keep.has(index)); const warnings = [`Capped detail list to ${MAX_DETAIL_ITEMS} items based on priority scoring.`]; return { details: kept, warnings }; @@ -155,18 +155,18 @@ export function validateTypeConsistency( ): { errors: string[]; warnings: string[] } { const errors: string[] = []; const warnings: string[] = []; - const lowerFiles = files.map((file) => file.toLowerCase()); - const hasDocs = lowerFiles.some((file) => /\.(md|mdx|adoc|rst)$/.test(file)); + const lowerFiles = files.map(file => file.toLowerCase()); + const hasDocs = lowerFiles.some(file => /\.(md|mdx|adoc|rst)$/.test(file)); const hasTests = lowerFiles.some( - (file) => /(^|\/)(test|tests|__tests__)(\/|$)/.test(file) || /(^|\/).*(_test|\.test|\.spec)\./.test(file), + file => /(^|\/)(test|tests|__tests__)(\/|$)/.test(file) || /(^|\/).*(_test|\.test|\.spec)\./.test(file), ); - const hasCI = lowerFiles.some((file) => file.startsWith(".github/workflows/") || file.startsWith(".gitlab-ci")); - const hasBuild = lowerFiles.some((file) => - ["cargo.toml", "package.json", "makefile"].some((candidate) => file.endsWith(candidate)), + const hasCI = lowerFiles.some(file => file.startsWith(".github/workflows/") || file.startsWith(".gitlab-ci")); + const hasBuild = lowerFiles.some(file => + ["cargo.toml", "package.json", "makefile"].some(candidate => file.endsWith(candidate)), ); - const hasPerfEvidence = lowerFiles.some((file) => /(bench|benchmark|perf)/.test(file)); + const hasPerfEvidence = lowerFiles.some(file => /(bench|benchmark|perf)/.test(file)); const summary = options.summary?.toLowerCase() ?? ""; - const detailText = options.details?.map((detail) => detail.text.toLowerCase()).join(" ") ?? ""; + const detailText = options.details?.map(detail => detail.text.toLowerCase()).join(" ") ?? ""; const hasPerfKeywords = /(performance|optimiz|latency|throughput|benchmark)/.test(`${summary} ${detailText}`); switch (type) { diff --git a/packages/coding-agent/src/commit/analysis/conventional.ts b/packages/coding-agent/src/commit/analysis/conventional.ts index 346541232..575ec1a5b 100644 --- a/packages/coding-agent/src/commit/analysis/conventional.ts +++ b/packages/coding-agent/src/commit/analysis/conventional.ts @@ -122,7 +122,7 @@ function normalizeAnalysis(parsed: { details: Array<{ text: string; changelog_category?: ChangelogCategory; user_visible?: boolean }>; issue_refs: string[]; }): ConventionalAnalysis { - const details: ConventionalDetail[] = parsed.details.map((detail) => ({ + const details: ConventionalDetail[] = parsed.details.map(detail => ({ text: detail.text.trim(), changelogCategory: detail.user_visible ? detail.changelog_category : undefined, userVisible: detail.user_visible ?? false, @@ -146,8 +146,8 @@ function extractToolCall(message: AssistantMessage, name: string): ToolCall | un function extractTextContent(message: AssistantMessage): string { return message.content - .filter((content) => content.type === "text") - .map((content) => content.text) + .filter(content => content.type === "text") + .map(content => content.text) .join("") .trim(); } diff --git a/packages/coding-agent/src/commit/analysis/scope.ts b/packages/coding-agent/src/commit/analysis/scope.ts index ce61c38b3..8a3cfd5eb 100644 --- a/packages/coding-agent/src/commit/analysis/scope.ts +++ b/packages/coding-agent/src/commit/analysis/scope.ts @@ -50,7 +50,7 @@ export function extractScopeCandidates(numstat: NumstatEntry[]): ScopeCandidates totalLines += linesChanged; const components = extractComponentsFromPath(normalizedPath); for (const component of components) { - if (component.split("/").some((segment) => segment.includes("."))) { + if (component.split("/").some(segment => segment.includes("."))) { continue; } componentLines.set(component, (componentLines.get(component) ?? 0) + linesChanged); @@ -150,7 +150,7 @@ function extractComponentsFromPath(path: string): string[] { } function extractTopLevelRoot(path: string): string | null { - const segments = path.split("/").filter((segment) => segment.length > 0); + const segments = path.split("/").filter(segment => segment.length > 0); if (segments.length === 0) return null; if (segments.length === 1) { return segments[0]!.startsWith(".") ? null : "(root)"; diff --git a/packages/coding-agent/src/commit/analysis/summary.ts b/packages/coding-agent/src/commit/analysis/summary.ts index f520a961f..e23a8a42f 100644 --- a/packages/coding-agent/src/commit/analysis/summary.ts +++ b/packages/coding-agent/src/commit/analysis/summary.ts @@ -86,15 +86,13 @@ function parseSummaryFromResponse(message: AssistantMessage, commitType: string, } function extractToolCall(message: AssistantMessage, name: string): ToolCall | undefined { - return message.content.find((content) => content.type === "toolCall" && content.name === name) as - | ToolCall - | undefined; + return message.content.find(content => content.type === "toolCall" && content.name === name) as ToolCall | undefined; } function extractTextContent(message: AssistantMessage): string { return message.content - .filter((content) => content.type === "text") - .map((content) => content.text) + .filter(content => content.type === "text") + .map(content => content.text) .join("") .trim(); } diff --git a/packages/coding-agent/src/commit/changelog/generate.ts b/packages/coding-agent/src/commit/changelog/generate.ts index 1cc1e55ad..76cf48613 100644 --- a/packages/coding-agent/src/commit/changelog/generate.ts +++ b/packages/coding-agent/src/commit/changelog/generate.ts @@ -67,15 +67,13 @@ function parseChangelogResponse(message: AssistantMessage): ChangelogGenerationR } function extractToolCall(message: AssistantMessage, name: string): ToolCall | undefined { - return message.content.find((content) => content.type === "toolCall" && content.name === name) as - | ToolCall - | undefined; + return message.content.find(content => content.type === "toolCall" && content.name === name) as ToolCall | undefined; } function extractTextContent(message: AssistantMessage): string { return message.content - .filter((content) => content.type === "text") - .map((content) => content.text) + .filter(content => content.type === "text") + .map(content => content.text) .join("") .trim(); } diff --git a/packages/coding-agent/src/commit/changelog/index.ts b/packages/coding-agent/src/commit/changelog/index.ts index c1ff312e9..1fb2ea304 100644 --- a/packages/coding-agent/src/commit/changelog/index.ts +++ b/packages/coding-agent/src/commit/changelog/index.ts @@ -178,8 +178,8 @@ function applyDeletions( ): Record { const result: Record = {}; for (const [section, items] of Object.entries(existing)) { - const toDelete = new Set((deletions[section] ?? []).map((d) => d.toLowerCase())); - const filtered = items.filter((item) => !toDelete.has(item.toLowerCase())); + const toDelete = new Set((deletions[section] ?? []).map(d => d.toLowerCase())); + const filtered = items.filter(item => !toDelete.has(item.toLowerCase())); if (filtered.length > 0) { result[section] = filtered; } @@ -194,7 +194,7 @@ function mergeEntries( const merged: Record = { ...existing }; for (const [section, items] of Object.entries(incoming)) { const current = merged[section] ?? []; - const lower = new Set(current.map((item) => item.toLowerCase())); + const lower = new Set(current.map(item => item.toLowerCase())); for (const item of items) { if (!lower.has(item.toLowerCase())) { current.push(item); @@ -225,9 +225,9 @@ function renderUnreleasedSections(entries: Record): string[] { function normalizeEntries(entries: Record): Record { const result: Record = {}; for (const [section, items] of Object.entries(entries)) { - const trimmed = items.map((item) => item.trim().replace(/\.$/, "")).filter((item) => item.length > 0); + const trimmed = items.map(item => item.trim().replace(/\.$/, "")).filter(item => item.length > 0); if (trimmed.length === 0) continue; - result[section] = Array.from(new Set(trimmed.map((item) => item.trim()))); + result[section] = Array.from(new Set(trimmed.map(item => item.trim()))); } return result; } diff --git a/packages/coding-agent/src/commit/changelog/parse.ts b/packages/coding-agent/src/commit/changelog/parse.ts index b0a981f6a..e9b4354c0 100644 --- a/packages/coding-agent/src/commit/changelog/parse.ts +++ b/packages/coding-agent/src/commit/changelog/parse.ts @@ -5,7 +5,7 @@ const SECTION_PATTERN = /^###\s+(.*)$/; export function parseUnreleasedSection(content: string): UnreleasedSection { const lines = content.split("\n"); - const startIndex = lines.findIndex((line) => UNRELEASED_PATTERN.test(line.trim())); + const startIndex = lines.findIndex(line => UNRELEASED_PATTERN.test(line.trim())); if (startIndex === -1) { throw new Error("No [Unreleased] section found in changelog"); } diff --git a/packages/coding-agent/src/commit/git/diff.ts b/packages/coding-agent/src/commit/git/diff.ts index 221b36a03..6c16aa5b5 100644 --- a/packages/coding-agent/src/commit/git/diff.ts +++ b/packages/coding-agent/src/commit/git/diff.ts @@ -31,7 +31,7 @@ export function parseFileDiffs(diff: string): FileDiff[] { if (!match) continue; const filename = match[2]; const content = part; - const isBinary = lines.some((line) => line.startsWith("Binary files ")); + const isBinary = lines.some(line => line.startsWith("Binary files ")); let additions = 0; let deletions = 0; for (const line of lines) { @@ -52,7 +52,7 @@ export function parseFileDiffs(diff: string): FileDiff[] { export function parseDiffHunks(diff: string): FileHunks[] { const files = parseFileDiffs(diff); - return files.map((file) => parseFileHunks(file)); + return files.map(file => parseFileHunks(file)); } export function parseFileHunks(fileDiff: FileDiff): FileHunks { diff --git a/packages/coding-agent/src/commit/git/index.ts b/packages/coding-agent/src/commit/git/index.ts index 5e28ec4df..0555677db 100644 --- a/packages/coding-agent/src/commit/git/index.ts +++ b/packages/coding-agent/src/commit/git/index.ts @@ -36,7 +36,7 @@ export class ControlledGit { this.ensureSuccess(result, "git diff --name-only"); return result.stdout .split("\n") - .map((line) => line.trim()) + .map(line => line.trim()) .filter(Boolean); } @@ -66,7 +66,7 @@ export class ControlledGit { this.ensureSuccess(result, "git log"); return result.stdout .split("\n") - .map((line) => line.trim()) + .map(line => line.trim()) .filter(Boolean); } @@ -75,7 +75,7 @@ export class ControlledGit { this.ensureSuccess(result, "git diff --cached --name-only"); return result.stdout .split("\n") - .map((line) => line.trim()) + .map(line => line.trim()) .filter(Boolean); } @@ -84,7 +84,7 @@ export class ControlledGit { this.ensureSuccess(result, "git ls-files --others --exclude-standard"); return result.stdout .split("\n") - .map((line) => line.trim()) + .map(line => line.trim()) .filter(Boolean); } @@ -102,7 +102,7 @@ export class ControlledGit { if (selections.length === 0) return; const diff = await this.getDiff(false); const fileDiffs = parseFileDiffs(diff); - const fileDiffMap = new Map(fileDiffs.map((entry) => [entry.filename, entry])); + const fileDiffMap = new Map(fileDiffs.map(entry => [entry.filename, entry])); const patchParts: string[] = []; for (const selection of selections) { const fileDiff = fileDiffMap.get(selection.path); @@ -128,7 +128,7 @@ export class ControlledGit { throw new GitError("git apply --cached", `No hunks selected for ${selection.path}`); } const header = extractFileHeader(fileDiff.content); - const filePatch = [header, ...selectedHunks.map((hunk) => hunk.content)].join("\n"); + const filePatch = [header, ...selectedHunks.map(hunk => hunk.content)].join("\n"); patchParts.push(filePatch); } @@ -192,7 +192,7 @@ function extractFileHeader(diff: string): string { function joinPatch(parts: string[]): string { return parts - .map((part) => (part.endsWith("\n") ? part : `${part}\n`)) + .map(part => (part.endsWith("\n") ? part : `${part}\n`)) .join("\n") .trimEnd() .concat("\n"); @@ -200,13 +200,13 @@ function joinPatch(parts: string[]): string { function selectHunks(file: FileHunks, selector: HunkSelection["hunks"]): FileHunks["hunks"] { if (selector.type === "indices") { - const wanted = new Set(selector.indices.map((value) => Math.max(1, Math.floor(value)))); - return file.hunks.filter((hunk) => wanted.has(hunk.index + 1)); + const wanted = new Set(selector.indices.map(value => Math.max(1, Math.floor(value)))); + return file.hunks.filter(hunk => wanted.has(hunk.index + 1)); } if (selector.type === "lines") { const start = Math.floor(selector.start); const end = Math.floor(selector.end); - return file.hunks.filter((hunk) => hunk.newStart <= end && hunk.newStart + hunk.newLines - 1 >= start); + return file.hunks.filter(hunk => hunk.newStart <= end && hunk.newStart + hunk.newLines - 1 >= start); } return file.hunks; } diff --git a/packages/coding-agent/src/commit/map-reduce/index.ts b/packages/coding-agent/src/commit/map-reduce/index.ts index ba997555c..41f6e4d14 100644 --- a/packages/coding-agent/src/commit/map-reduce/index.ts +++ b/packages/coding-agent/src/commit/map-reduce/index.ts @@ -34,10 +34,10 @@ export function shouldUseMapReduce(diff: string, settings?: MapReduceSettings): if (settings?.enabled === false) return false; const minFiles = settings?.minFiles ?? MIN_FILES_FOR_MAP_REDUCE; const maxFileTokens = settings?.maxFileTokens ?? MAX_FILE_TOKENS; - const files = parseFileDiffs(diff).filter((file) => !isExcludedFile(file.filename)); + const files = parseFileDiffs(diff).filter(file => !isExcludedFile(file.filename)); const fileCount = files.length; if (fileCount >= minFiles) return true; - return files.some((file) => estimateTokens(file.content) > maxFileTokens); + return files.some(file => estimateTokens(file.content) > maxFileTokens); } /** @@ -45,7 +45,7 @@ export function shouldUseMapReduce(diff: string, settings?: MapReduceSettings): */ export async function runMapReduceAnalysis(input: MapReduceInput): Promise { - const fileDiffs = parseFileDiffs(input.diff).filter((file) => !isExcludedFile(file.filename)); + const fileDiffs = parseFileDiffs(input.diff).filter(file => !isExcludedFile(file.filename)); const observations = await runMapPhase({ model: input.smolModel, apiKey: input.smolApiKey, diff --git a/packages/coding-agent/src/commit/map-reduce/map-phase.ts b/packages/coding-agent/src/commit/map-reduce/map-phase.ts index 3ab4377f0..ddab035fe 100644 --- a/packages/coding-agent/src/commit/map-reduce/map-phase.ts +++ b/packages/coding-agent/src/commit/map-reduce/map-phase.ts @@ -28,14 +28,14 @@ export interface MapPhaseInput { } export async function runMapPhase({ model, apiKey, files, config }: MapPhaseInput): Promise { - const filtered = files.filter((file) => !isExcludedFile(file.filename)); + const filtered = files.filter(file => !isExcludedFile(file.filename)); const systemPrompt = renderPromptTemplate(fileObserverSystemPrompt); const maxFileTokens = config?.maxFileTokens ?? MAX_FILE_TOKENS; const maxConcurrency = config?.maxConcurrency ?? MAX_CONCURRENCY; const timeoutMs = config?.timeoutMs ?? MAP_PHASE_TIMEOUT_MS; const maxRetries = config?.maxRetries ?? MAX_RETRIES; const retryBackoffMs = config?.retryBackoffMs ?? RETRY_BACKOFF_MS; - return runWithConcurrency(filtered, maxConcurrency, async (file) => { + return runWithConcurrency(filtered, maxConcurrency, async file => { if (file.isBinary) { return { file: file.filename, @@ -75,8 +75,8 @@ export async function runMapPhase({ model, apiKey, files, config }: MapPhaseInpu function parseObservations(message: AssistantMessage): string[] { const text = message.content - .filter((content) => content.type === "text") - .map((content) => content.text) + .filter(content => content.type === "text") + .map(content => content.text) .join("") .trim(); @@ -84,9 +84,9 @@ function parseObservations(message: AssistantMessage): string[] { const lines = text .split("\n") - .map((line) => line.trim()) + .map(line => line.trim()) .filter(Boolean) - .map((line) => line.replace(/^[-*]\s+/, "")) + .map(line => line.replace(/^[-*]\s+/, "")) .filter(Boolean); return lines.slice(0, 5); @@ -97,7 +97,7 @@ function generateContextHeader(files: FileDiff[], currentFile: string): string { return `(Large commit with ${files.length} total files)`; } - const otherFiles = files.filter((file) => file.filename !== currentFile); + const otherFiles = files.filter(file => file.filename !== currentFile); if (otherFiles.length === 0) return ""; const sorted = [...otherFiles].sort((a, b) => b.additions + b.deletions - (a.additions + a.deletions)); diff --git a/packages/coding-agent/src/commit/map-reduce/reduce-phase.ts b/packages/coding-agent/src/commit/map-reduce/reduce-phase.ts index 801e17e03..6f03f6109 100644 --- a/packages/coding-agent/src/commit/map-reduce/reduce-phase.ts +++ b/packages/coding-agent/src/commit/map-reduce/reduce-phase.ts @@ -64,7 +64,7 @@ export async function runReducePhase({ }: ReducePhaseInput): Promise { const prompt = renderPromptTemplate(reduceUserPrompt, { types_description: typesDescription, - observations: observations.flatMap((obs) => obs.observations.map((line) => `- ${obs.file}: ${line}`)).join("\n"), + observations: observations.flatMap(obs => obs.observations.map(line => `- ${obs.file}: ${line}`)).join("\n"), stat, scope_candidates: scopeCandidates, }); @@ -123,7 +123,7 @@ function normalizeAnalysis(parsed: { return { type: parsed.type, scope: parsed.scope?.trim() || null, - details: parsed.details.map((detail) => ({ + details: parsed.details.map(detail => ({ text: detail.text.trim(), changelogCategory: detail.user_visible ? detail.changelog_category : undefined, userVisible: detail.user_visible ?? false, @@ -133,15 +133,13 @@ function normalizeAnalysis(parsed: { } function extractToolCall(message: AssistantMessage, name: string): ToolCall | undefined { - return message.content.find((content) => content.type === "toolCall" && content.name === name) as - | ToolCall - | undefined; + return message.content.find(content => content.type === "toolCall" && content.name === name) as ToolCall | undefined; } function extractTextContent(message: AssistantMessage): string { return message.content - .filter((content) => content.type === "text") - .map((content) => content.text) + .filter(content => content.type === "text") + .map(content => content.text) .join("") .trim(); } diff --git a/packages/coding-agent/src/commit/message.ts b/packages/coding-agent/src/commit/message.ts index 5b9af8708..48cb5dcae 100644 --- a/packages/coding-agent/src/commit/message.ts +++ b/packages/coding-agent/src/commit/message.ts @@ -3,7 +3,7 @@ import type { ConventionalAnalysis } from "./types"; export function formatCommitMessage(analysis: ConventionalAnalysis, summary: string): string { const scopePart = analysis.scope ? `(${analysis.scope})` : ""; const header = `${analysis.type}${scopePart}: ${summary}`; - const bodyLines = analysis.details.map((detail) => `- ${detail.text.trim()}`); + const bodyLines = analysis.details.map(detail => `- ${detail.text.trim()}`); if (bodyLines.length === 0) { return header; } diff --git a/packages/coding-agent/src/commit/model-selection.ts b/packages/coding-agent/src/commit/model-selection.ts index 536e66982..a127282d9 100644 --- a/packages/coding-agent/src/commit/model-selection.ts +++ b/packages/coding-agent/src/commit/model-selection.ts @@ -65,7 +65,7 @@ function resolveModelFromSettings(settingsManager: SettingsManager, available: M function resolveModelFromString(value: string, available: Model[]): Model | undefined { const parsed = parseModelString(value); if (parsed) { - return available.find((model) => model.provider === parsed.provider && model.id === parsed.id); + return available.find(model => model.provider === parsed.provider && model.id === parsed.id); } return parseModelPattern(value, available).model; } diff --git a/packages/coding-agent/src/commit/pipeline.ts b/packages/coding-agent/src/commit/pipeline.ts index 855cffbe8..fea0f782d 100644 --- a/packages/coding-agent/src/commit/pipeline.ts +++ b/packages/coding-agent/src/commit/pipeline.ts @@ -85,7 +85,7 @@ async function runLegacyCommitCommand(args: CommitCommandArgs): Promise { const scopeCandidates = extractScopeCandidates(numstat).scopeCandidates; const recentCommits = await git.getRecentCommits(RECENT_COMMITS_COUNT); const contextFiles = await loadProjectContextFiles({ cwd }); - const formattedContextFiles = contextFiles.map((file) => ({ + const formattedContextFiles = contextFiles.map(file => ({ path: path.relative(cwd, file.path), content: file.content, })); @@ -207,7 +207,7 @@ async function generateSummaryWithRetry(input: { apiKey: input.apiKey, commitType: input.analysis.type, scope: input.analysis.scope, - details: input.analysis.details.map((detail) => detail.text), + details: input.analysis.details.map(detail => detail.text), stat: input.stat, maxChars: SUMMARY_MAX_CHARS, userContext: context, diff --git a/packages/coding-agent/src/commit/utils/exclusions.ts b/packages/coding-agent/src/commit/utils/exclusions.ts index 871df41fe..2a9bd8d3a 100644 --- a/packages/coding-agent/src/commit/utils/exclusions.ts +++ b/packages/coding-agent/src/commit/utils/exclusions.ts @@ -31,12 +31,12 @@ const EXCLUDED_SUFFIXES = [".lock.yml", ".lock.yaml", "-lock.yml", "-lock.yaml"] export function isExcludedFile(path: string): boolean { const lower = path.toLowerCase(); - if (EXCLUDED_FILES.some((name) => lower.endsWith(name.toLowerCase()))) { + if (EXCLUDED_FILES.some(name => lower.endsWith(name.toLowerCase()))) { return true; } - return EXCLUDED_SUFFIXES.some((suffix) => lower.endsWith(suffix)); + return EXCLUDED_SUFFIXES.some(suffix => lower.endsWith(suffix)); } export function filterExcludedFiles(files: T[]): T[] { - return files.filter((file) => !isExcludedFile(file.filename)); + return files.filter(file => !isExcludedFile(file.filename)); } diff --git a/packages/coding-agent/src/config.ts b/packages/coding-agent/src/config.ts index b14d34722..c53d8569a 100644 --- a/packages/coding-agent/src/config.ts +++ b/packages/coding-agent/src/config.ts @@ -202,7 +202,7 @@ export function getConfigDirs(subpath: string, options: GetConfigDirsOptions = { * Returns just the paths, highest priority first. */ export function getConfigDirPaths(subpath: string, options: GetConfigDirsOptions = {}): string[] { - return getConfigDirs(subpath, options).map((e) => e.path); + return getConfigDirs(subpath, options).map(e => e.path); } export interface ConfigFileResult { @@ -392,7 +392,7 @@ export async function findAllNearestProjectConfigDirs( } // Sort by priority order - const order = PROJECT_CONFIG_BASES.map((b) => b.name); + const order = PROJECT_CONFIG_BASES.map(b => b.name); results.sort((a, b) => order.indexOf(a.source) - order.indexOf(b.source)); return results; diff --git a/packages/coding-agent/src/config/file-lock.ts b/packages/coding-agent/src/config/file-lock.ts index 28e03f58c..ec41535ee 100644 --- a/packages/coding-agent/src/config/file-lock.ts +++ b/packages/coding-agent/src/config/file-lock.ts @@ -101,7 +101,7 @@ async function acquireLock(filePath: string, options: FileLockOptions = {}): Pro continue; } - await new Promise((resolve) => setTimeout(resolve, opts.retryDelayMs)); + await new Promise(resolve => setTimeout(resolve, opts.retryDelayMs)); } throw new Error(`Failed to acquire lock for ${filePath} after ${opts.retries} attempts`); diff --git a/packages/coding-agent/src/config/keybindings.ts b/packages/coding-agent/src/config/keybindings.ts index 131c3e018..214bbd9ee 100644 --- a/packages/coding-agent/src/config/keybindings.ts +++ b/packages/coding-agent/src/config/keybindings.ts @@ -201,7 +201,7 @@ export class KeybindingsManager { const keyArray = Array.isArray(keys) ? keys : [keys]; this.appActionToKeys.set( action as AppAction, - keyArray.map((key) => normalizeKeyId(key as KeyId)), + keyArray.map(key => normalizeKeyId(key as KeyId)), ); } @@ -211,7 +211,7 @@ export class KeybindingsManager { const keyArray = Array.isArray(keys) ? keys : [keys]; this.appActionToKeys.set( action, - keyArray.map((key) => normalizeKeyId(key as KeyId)), + keyArray.map(key => normalizeKeyId(key as KeyId)), ); } } diff --git a/packages/coding-agent/src/config/model-registry.ts b/packages/coding-agent/src/config/model-registry.ts index 41f97e8cb..bcb4444be 100644 --- a/packages/coding-agent/src/config/model-registry.ts +++ b/packages/coding-agent/src/config/model-registry.ts @@ -1,7 +1,6 @@ /** * Model registry - manages built-in and custom models, provides API key resolution. */ - import * as fs from "node:fs"; import * as path from "node:path"; import { @@ -12,7 +11,6 @@ import { type Model, normalizeDomain, } from "@oh-my-pi/pi-ai"; - import { isEnoent, logger } from "@oh-my-pi/pi-utils"; import { type Static, Type } from "@sinclair/typebox"; import AjvModule from "ajv"; @@ -142,7 +140,7 @@ export class ModelRegistry { private fallbackPaths: string[] = [], ) { // Set up fallback resolver for custom provider API keys - this.authStorage.setFallbackResolver((provider) => { + this.authStorage.setFallbackResolver(provider => { const keyConfig = this.customProviderApiKeys.get(provider); if (keyConfig) { return resolveApiKeyConfig(keyConfig); @@ -164,7 +162,7 @@ export class ModelRegistry { instance.customProviderApiKeys = new Map(Object.entries(data.customProviderApiKeys ?? {})); instance.loadError = data.loadError; - authStorage.setFallbackResolver((provider) => { + authStorage.setFallbackResolver(provider => { const keyConfig = instance.customProviderApiKeys.get(provider); if (keyConfig) { return resolveApiKeyConfig(keyConfig); @@ -244,7 +242,7 @@ export class ModelRegistry { ? (normalizeDomain(copilotCred.enterpriseUrl) ?? undefined) : undefined; const baseUrl = getGitHubCopilotBaseUrl(copilotCred.access, domain); - this.models = combined.map((m) => (m.provider === "github-copilot" ? { ...m, baseUrl } : m)); + this.models = combined.map(m => (m.provider === "github-copilot" ? { ...m, baseUrl } : m)); } else { this.models = combined; } @@ -253,14 +251,14 @@ export class ModelRegistry { /** Load built-in models, skipping replaced providers and applying overrides */ private loadBuiltInModels(replacedProviders: Set, overrides: Map): Model[] { return getProviders() - .filter((provider) => !replacedProviders.has(provider)) - .flatMap((provider) => { + .filter(provider => !replacedProviders.has(provider)) + .flatMap(provider => { const models = getModels(provider as any) as Model[]; const override = overrides.get(provider); if (!override) return models; // Apply baseUrl/headers override to all models of this provider - return models.map((m) => ({ + return models.map(m => ({ ...m, baseUrl: override.baseUrl ?? m.baseUrl, headers: override.headers ? { ...m.headers, ...override.headers } : m.headers, @@ -451,21 +449,21 @@ export class ModelRegistry { * This is a fast check that doesn't refresh OAuth tokens. */ getAvailable(): Model[] { - return this.models.filter((m) => this.authStorage.hasAuth(m.provider)); + return this.models.filter(m => this.authStorage.hasAuth(m.provider)); } /** * Find a model by provider and ID. */ find(provider: string, modelId: string): Model | undefined { - return this.models.find((m) => m.provider === provider && m.id === modelId); + return this.models.find(m => m.provider === provider && m.id === modelId); } /** * Get the base URL associated with a provider, if any model defines one. */ getProviderBaseUrl(provider: string): string | undefined { - return this.models.find((m) => m.provider === provider && m.baseUrl)?.baseUrl; + return this.models.find(m => m.provider === provider && m.baseUrl)?.baseUrl; } /** diff --git a/packages/coding-agent/src/config/model-resolver.ts b/packages/coding-agent/src/config/model-resolver.ts index e12fbd2a4..3febd737d 100644 --- a/packages/coding-agent/src/config/model-resolver.ts +++ b/packages/coding-agent/src/config/model-resolver.ts @@ -1,7 +1,6 @@ /** * Model resolution, scoping, and initial selection */ - import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core"; import { type Api, type KnownProvider, type Model, modelsAreEqual } from "@oh-my-pi/pi-ai"; import chalk from "chalk"; @@ -84,7 +83,7 @@ function tryMatchModel(modelPattern: string, availableModels: Model[]): Mod const provider = modelPattern.substring(0, slashIndex); const modelId = modelPattern.substring(slashIndex + 1); const providerMatch = availableModels.find( - (m) => m.provider.toLowerCase() === provider.toLowerCase() && m.id.toLowerCase() === modelId.toLowerCase(), + m => m.provider.toLowerCase() === provider.toLowerCase() && m.id.toLowerCase() === modelId.toLowerCase(), ); if (providerMatch) { return providerMatch; @@ -93,14 +92,14 @@ function tryMatchModel(modelPattern: string, availableModels: Model[]): Mod } // Check for exact ID match (case-insensitive) - const exactMatch = availableModels.find((m) => m.id.toLowerCase() === modelPattern.toLowerCase()); + const exactMatch = availableModels.find(m => m.id.toLowerCase() === modelPattern.toLowerCase()); if (exactMatch) { return exactMatch; } // No exact match - fall back to partial matching const matches = availableModels.filter( - (m) => + m => m.id.toLowerCase().includes(modelPattern.toLowerCase()) || m.name?.toLowerCase().includes(modelPattern.toLowerCase()), ); @@ -110,8 +109,8 @@ function tryMatchModel(modelPattern: string, availableModels: Model[]): Mod } // Separate into aliases and dated versions - const aliases = matches.filter((m) => isAlias(m.id)); - const datedVersions = matches.filter((m) => !isAlias(m.id)); + const aliases = matches.filter(m => isAlias(m.id)); + const datedVersions = matches.filter(m => !isAlias(m.id)); if (aliases.length > 0) { // Prefer alias - if multiple aliases, pick the one that sorts highest @@ -226,7 +225,7 @@ export async function resolveModelScope(patterns: string[], modelRegistry: Model // Match against "provider/modelId" format OR just model ID // This allows "*sonnet*" to match without requiring "anthropic/*sonnet*" - const matchingModels = availableModels.filter((m) => { + const matchingModels = availableModels.filter(m => { const fullId = `${m.provider}/${m.id}`; const glob = new Bun.Glob(globPattern.toLowerCase()); return glob.match(fullId.toLowerCase()) || glob.match(m.id.toLowerCase()); @@ -238,7 +237,7 @@ export async function resolveModelScope(patterns: string[], modelRegistry: Model } for (const model of matchingModels) { - if (!scopedModels.find((sm) => modelsAreEqual(sm.model, model))) { + if (!scopedModels.find(sm => modelsAreEqual(sm.model, model))) { scopedModels.push({ model, thinkingLevel, explicitThinkingLevel }); } } @@ -257,7 +256,7 @@ export async function resolveModelScope(patterns: string[], modelRegistry: Model } // Avoid duplicates - if (!scopedModels.find((sm) => modelsAreEqual(sm.model, model))) { + if (!scopedModels.find(sm => modelsAreEqual(sm.model, model))) { scopedModels.push({ model, thinkingLevel, explicitThinkingLevel }); } } @@ -343,7 +342,7 @@ export async function findInitialModel(options: { // Try to find a default model from known providers for (const provider of Object.keys(defaultModelPerProvider) as KnownProvider[]) { const defaultId = defaultModelPerProvider[provider]; - const match = availableModels.find((m) => m.provider === provider && m.id === defaultId); + const match = availableModels.find(m => m.provider === provider && m.id === defaultId); if (match) { return { model: match, thinkingLevel: "off", fallbackMessage: undefined }; } @@ -405,7 +404,7 @@ export async function restoreModelFromSession( let fallbackModel: Model | undefined; for (const provider of Object.keys(defaultModelPerProvider) as KnownProvider[]) { const defaultId = defaultModelPerProvider[provider]; - const match = availableModels.find((m) => m.provider === provider && m.id === defaultId); + const match = availableModels.find(m => m.provider === provider && m.id === defaultId); if (match) { fallbackModel = match; break; @@ -450,7 +449,7 @@ export async function findSmolModel( if (savedModel) { const parsed = parseModelString(savedModel); if (parsed) { - const match = availableModels.find((m) => m.provider === parsed.provider && m.id === parsed.id); + const match = availableModels.find(m => m.provider === parsed.provider && m.id === parsed.id); if (match) return match; } } @@ -458,15 +457,15 @@ export async function findSmolModel( // 2. Try priority chain for (const pattern of SMOL_MODEL_PRIORITY) { // Try exact match with provider prefix - const providerMatch = availableModels.find((m) => `${m.provider}/${m.id}`.toLowerCase() === pattern); + const providerMatch = availableModels.find(m => `${m.provider}/${m.id}`.toLowerCase() === pattern); if (providerMatch) return providerMatch; // Try exact match first - const exactMatch = availableModels.find((m) => m.id.toLowerCase() === pattern); + const exactMatch = availableModels.find(m => m.id.toLowerCase() === pattern); if (exactMatch) return exactMatch; // Try fuzzy match (substring) - const fuzzyMatch = availableModels.find((m) => m.id.toLowerCase().includes(pattern)); + const fuzzyMatch = availableModels.find(m => m.id.toLowerCase().includes(pattern)); if (fuzzyMatch) return fuzzyMatch; } @@ -493,7 +492,7 @@ export async function findSlowModel( if (savedModel) { const parsed = parseModelString(savedModel); if (parsed) { - const match = availableModels.find((m) => m.provider === parsed.provider && m.id === parsed.id); + const match = availableModels.find(m => m.provider === parsed.provider && m.id === parsed.id); if (match) return match; } } @@ -501,11 +500,11 @@ export async function findSlowModel( // 2. Try priority chain for (const pattern of SLOW_MODEL_PRIORITY) { // Try exact match first - const exactMatch = availableModels.find((m) => m.id.toLowerCase() === pattern.toLowerCase()); + const exactMatch = availableModels.find(m => m.id.toLowerCase() === pattern.toLowerCase()); if (exactMatch) return exactMatch; // Try fuzzy match (substring) - const fuzzyMatch = availableModels.find((m) => m.id.toLowerCase().includes(pattern.toLowerCase())); + const fuzzyMatch = availableModels.find(m => m.id.toLowerCase().includes(pattern.toLowerCase())); if (fuzzyMatch) return fuzzyMatch; } diff --git a/packages/coding-agent/src/config/prompt-templates.ts b/packages/coding-agent/src/config/prompt-templates.ts index e777c634d..be33deac0 100644 --- a/packages/coding-agent/src/config/prompt-templates.ts +++ b/packages/coding-agent/src/config/prompt-templates.ts @@ -44,7 +44,7 @@ handlebars.registerHelper( const suffix = (options.hash.suffix as string) ?? ""; const rawSeparator = (options.hash.join as string) ?? "\n"; const separator = rawSeparator.replace(/\\n/g, "\n").replace(/\\t/g, "\t"); - return context.map((item) => `${prefix}${options.fn(item)}${suffix}`).join(separator); + return context.map(item => `${prefix}${options.fn(item)}${suffix}`).join(separator); }, ); @@ -126,7 +126,7 @@ handlebars.registerHelper( const headers = headersStr?.split("|") ?? []; const separator = headers.map(() => "---").join(" | "); const headerRow = headers.length > 0 ? `| ${headers.join(" | ")} |\n| ${separator} |\n` : ""; - const rows = context.map((item) => `| ${options.fn(item).trim()} |`).join("\n"); + const rows = context.map(item => `| ${options.fn(item).trim()} |`).join("\n"); return headerRow + rows; }, ); @@ -235,7 +235,7 @@ function optimizePromptLayout(input: string): string { // normalize NBSP -> space s = s.replace(/\u00A0/g, " "); - const lines = s.split("\n").map((line) => { + const lines = s.split("\n").map(line => { // 2) remove trailing whitespace (spaces/tabs) per line let l = line.replace(/[ \t]+$/g, ""); @@ -409,7 +409,7 @@ async function loadTemplatesFromDir( // Get description from frontmatter or first non-empty line let description = String(frontmatter.description || ""); if (!description) { - const firstLine = body.split("\n").find((line) => line.trim()); + const firstLine = body.split("\n").find(line => line.trim()); if (firstLine) { // Truncate if too long description = firstLine.slice(0, 60); @@ -482,7 +482,7 @@ export function expandPromptTemplate(text: string, templates: PromptTemplate[]): const templateName = spaceIndex === -1 ? text.slice(1) : text.slice(1, spaceIndex); const argsString = spaceIndex === -1 ? "" : text.slice(spaceIndex + 1); - const template = templates.find((t) => t.name === templateName); + const template = templates.find(t => t.name === templateName); if (template) { const args = parseCommandArgs(argsString); const argsText = args.join(" "); diff --git a/packages/coding-agent/src/config/settings-manager.ts b/packages/coding-agent/src/config/settings-manager.ts index 67f70f156..b2e1841bf 100644 --- a/packages/coding-agent/src/config/settings-manager.ts +++ b/packages/coding-agent/src/config/settings-manager.ts @@ -343,7 +343,7 @@ function normalizeBashInterceptorSettings( patterns = DEFAULT_BASH_INTERCEPTOR_RULES; } else if (Array.isArray(rawPatterns)) { patterns = rawPatterns - .map((rule) => normalizeBashInterceptorRule(rule)) + .map(rule => normalizeBashInterceptorRule(rule)) .filter((rule): rule is BashInterceptorRule => rule !== null); } else { patterns = DEFAULT_BASH_INTERCEPTOR_RULES; @@ -372,7 +372,7 @@ function hasNerdFonts(): boolean { const termProgram = (process.env.TERM_PROGRAM || "").toLowerCase(); const term = (process.env.TERM || "").toLowerCase(); const nerdTerms = ["iterm", "wezterm", "kitty", "ghostty", "alacritty"]; - cachedNerdFonts = nerdTerms.some((candidate) => termProgram.includes(candidate) || term.includes(candidate)); + cachedNerdFonts = nerdTerms.some(candidate => termProgram.includes(candidate) || term.includes(candidate)); return cachedNerdFonts; } diff --git a/packages/coding-agent/src/cursor.ts b/packages/coding-agent/src/cursor.ts index 208c0b46d..d546c7243 100644 --- a/packages/coding-agent/src/cursor.ts +++ b/packages/coding-agent/src/cursor.ts @@ -59,7 +59,7 @@ async function executeTool( let isError = false; const onUpdate: AgentToolUpdateCallback | undefined = options.emitEvent - ? (partialResult) => { + ? partialResult => { options.emitEvent?.({ type: "tool_execution_update", toolCallId, @@ -222,7 +222,7 @@ export class CursorExecHandlers implements ICursorExecHandlers { const toolCallId = decodeToolCallId(call.toolCallId); const tool = this.options.tools.get(toolName); if (!tool) { - const availableTools = Array.from(this.options.tools.keys()).filter((name) => name.startsWith("mcp_")); + const availableTools = Array.from(this.options.tools.keys()).filter(name => name.startsWith("mcp_")); const message = formatMcpToolErrorMessage(toolName, availableTools); const toolResult: ToolResultMessage = { role: "toolResult", diff --git a/packages/coding-agent/src/discovery/agents-md.ts b/packages/coding-agent/src/discovery/agents-md.ts index 0fa16d81e..233c743fc 100644 --- a/packages/coding-agent/src/discovery/agents-md.ts +++ b/packages/coding-agent/src/discovery/agents-md.ts @@ -5,7 +5,6 @@ * This handles AGENTS.md files that live in project root (not in config directories * like .codex/ or .gemini/, which are handled by their respective providers). */ - import * as path from "node:path"; import { registerProvider } from "../capability"; import { type ContextFile, contextFileCapability } from "../capability/context-file"; diff --git a/packages/coding-agent/src/discovery/builtin.ts b/packages/coding-agent/src/discovery/builtin.ts index 2d054e8c7..4f4bab1b2 100644 --- a/packages/coding-agent/src/discovery/builtin.ts +++ b/packages/coding-agent/src/discovery/builtin.ts @@ -4,7 +4,6 @@ * Primary provider for OMP native configs. Supports all capabilities. * .pi is an alias for backwards compatibility. */ - import * as path from "node:path"; import { registerProvider } from "../capability"; import { type ContextFile, contextFileCapability } from "../capability/context-file"; @@ -95,18 +94,18 @@ async function loadMCPServers(ctx: LoadContext): Promise> }; const projectDirs = await Promise.all( - PROJECT_DIRS.map(async (name) => { + PROJECT_DIRS.map(async name => { const dir = path.join(ctx.cwd, name); const entries = await readDirEntries(dir); return entries.length > 0 ? dir : null; }), ); - const userPaths = USER_DIRS.map((name) => path.join(ctx.home, name, "mcp.json")); + const userPaths = USER_DIRS.map(name => path.join(ctx.home, name, "mcp.json")); - const projectDir = projectDirs.find((dir) => dir !== null); + const projectDir = projectDirs.find(dir => dir !== null); if (projectDir) { - const projectCandidates = ["mcp.json", ".mcp.json"].map((filename) => path.join(projectDir, filename)); - const projectContents = await Promise.all(projectCandidates.map((path) => readFile(path))); + const projectCandidates = ["mcp.json", ".mcp.json"].map(filename => path.join(projectDir, filename)); + const projectContents = await Promise.all(projectCandidates.map(path => readFile(path))); for (let i = 0; i < projectCandidates.length; i++) { const content = projectContents[i]; if (content) { @@ -116,7 +115,7 @@ async function loadMCPServers(ctx: LoadContext): Promise> } } - const userContents = await Promise.all(userPaths.map((path) => readFile(path))); + const userContents = await Promise.all(userPaths.map(path => readFile(path))); for (let i = 0; i < userPaths.length; i++) { const content = userContents[i]; if (content) { @@ -140,10 +139,10 @@ registerProvider(mcpCapability.id, { async function loadSystemPrompt(ctx: LoadContext): Promise> { const items: SystemPrompt[] = []; - const userPaths = USER_DIRS.map((name) => + const userPaths = USER_DIRS.map(name => path.join(ctx.home, name, PATHS.userAgent.replace(`${PATHS.userBase}/`, ""), "SYSTEM.md"), ); - const userContents = await Promise.all(userPaths.map((p) => readFile(p))); + const userContents = await Promise.all(userPaths.map(p => readFile(p))); for (let i = 0; i < userPaths.length; i++) { const content = userContents[i]; if (content) { @@ -167,8 +166,8 @@ async function loadSystemPrompt(ctx: LoadContext): Promise path.join(dir, name)); - const entriesResults = await Promise.all(configDirs.map((d) => readDirEntries(d))); + const configDirs = PROJECT_DIRS.map(name => path.join(dir, name)); + const entriesResults = await Promise.all(configDirs.map(d => readDirEntries(d))); const validConfigDir = configDirs.find((_, i) => entriesResults[i].length > 0); if (!validConfigDir) continue; @@ -211,8 +210,8 @@ async function loadSkills(ctx: LoadContext): Promise> { ); return { - items: results.flatMap((r) => r.items), - warnings: results.flatMap((r) => r.warnings ?? []), + items: results.flatMap(r => r.items), + warnings: results.flatMap(r => r.warnings ?? []), }; } @@ -420,7 +419,7 @@ async function loadExtensionModules(ctx: LoadContext): Promise d.promise)); + const dirDiscoveryResults = await Promise.all(dirDiscoveryPromises.map(d => d.promise)); for (let i = 0; i < dirDiscoveryPromises.length; i++) { const { level } = dirDiscoveryPromises[i]; for (const extPath of dirDiscoveryResults[i]) { @@ -735,10 +734,10 @@ async function loadContextFiles(ctx: LoadContext): Promise + const userPaths = USER_DIRS.map(name => path.join(ctx.home, name, PATHS.userAgent.replace(`${PATHS.userBase}/`, ""), "AGENTS.md"), ); - const userContents = await Promise.all(userPaths.map((p) => readFile(p))); + const userContents = await Promise.all(userPaths.map(p => readFile(p))); for (let i = 0; i < userPaths.length; i++) { const content = userContents[i]; if (content) { @@ -764,8 +763,8 @@ async function loadContextFiles(ctx: LoadContext): Promise path.join(dir, name)); - const entriesResults = await Promise.all(configDirs.map((d) => readDirEntries(d))); + const configDirs = PROJECT_DIRS.map(name => path.join(dir, name)); + const entriesResults = await Promise.all(configDirs.map(d => readDirEntries(d))); const validConfigDir = configDirs.find((_, i) => entriesResults[i].length > 0); if (!validConfigDir) continue; diff --git a/packages/coding-agent/src/discovery/claude.ts b/packages/coding-agent/src/discovery/claude.ts index 73e11e071..c8283dc8e 100644 --- a/packages/coding-agent/src/discovery/claude.ts +++ b/packages/coding-agent/src/discovery/claude.ts @@ -4,7 +4,6 @@ * Loads configuration from .claude directories. * Priority: 80 (tool-specific, below builtin but above shared standards) */ - import * as path from "node:path"; import { registerProvider } from "../capability"; import { type ContextFile, contextFileCapability } from "../capability/context-file"; @@ -169,8 +168,8 @@ async function loadSkills(ctx: LoadContext): Promise> { ]); return { - items: results.flatMap((r) => r.items), - warnings: results.flatMap((r) => r.warnings ?? []), + items: results.flatMap(r => r.items), + warnings: results.flatMap(r => r.warnings ?? []), }; } @@ -194,7 +193,7 @@ async function loadExtensionModules(ctx: LoadContext): Promise { const paths = await discoverExtensionModulePaths(ctx, dir); - return paths.map((extPath) => ({ extPath, level })); + return paths.map(extPath => ({ extPath, level })); }), ); diff --git a/packages/coding-agent/src/discovery/cline.ts b/packages/coding-agent/src/discovery/cline.ts index 40710cba8..2063d2948 100644 --- a/packages/coding-agent/src/discovery/cline.ts +++ b/packages/coding-agent/src/discovery/cline.ts @@ -4,7 +4,6 @@ * Loads rules from .clinerules (can be single file or directory with *.md files). * Project-only (no user-level config). */ - import * as path from "node:path"; import { registerProvider } from "../capability"; import { readDirEntries, readFile } from "../capability/fs"; @@ -23,7 +22,7 @@ async function findClinerules(startDir: string): Promise<{ path: string; isDir: while (true) { const entries = await readDirEntries(current); - const entry = entries.find((e) => e.name === ".clinerules"); + const entry = entries.find(e => e.name === ".clinerules"); if (entry) { return { path: path.resolve(current, ".clinerules"), diff --git a/packages/coding-agent/src/discovery/codex.ts b/packages/coding-agent/src/discovery/codex.ts index 105d66e24..39f844811 100644 --- a/packages/coding-agent/src/discovery/codex.ts +++ b/packages/coding-agent/src/discovery/codex.ts @@ -6,7 +6,6 @@ * * User directory: ~/.codex */ - import * as path from "node:path"; import { logger } from "@oh-my-pi/pi-utils"; import { parse as parseToml } from "smol-toml"; @@ -227,8 +226,8 @@ async function loadSkills(ctx: LoadContext): Promise> { }), ]); - const items = results.flatMap((r) => r.items); - const warnings = results.flatMap((r) => r.warnings || []); + const items = results.flatMap(r => r.items); + const warnings = results.flatMap(r => r.warnings || []); return { items, warnings }; } @@ -250,13 +249,13 @@ async function loadExtensionModules(ctx: LoadContext): Promise ({ + ...userPaths.map(extPath => ({ name: getExtensionNameFromPath(extPath), path: extPath, level: "user" as const, _source: createSourceMeta(PROVIDER_ID, extPath, "user"), })), - ...projectPaths.map((extPath) => ({ + ...projectPaths.map(extPath => ({ name: getExtensionNameFromPath(extPath), path: extPath, level: "project" as const, @@ -301,8 +300,8 @@ async function loadSlashCommands(ctx: LoadContext): Promise r.items); - const warnings = results.flatMap((r) => r.warnings || []); + const items = results.flatMap(r => r.items); + const warnings = results.flatMap(r => r.warnings || []); return { items, warnings }; } @@ -344,8 +343,8 @@ async function loadPrompts(ctx: LoadContext): Promise> { }), ]); - const items = results.flatMap((r) => r.items); - const warnings = results.flatMap((r) => r.warnings || []); + const items = results.flatMap(r => r.items); + const warnings = results.flatMap(r => r.warnings || []); return { items, warnings }; } @@ -387,8 +386,8 @@ async function loadHooks(ctx: LoadContext): Promise> { }), ]); - const items = results.flatMap((r) => r.items); - const warnings = results.flatMap((r) => r.warnings || []); + const items = results.flatMap(r => r.items); + const warnings = results.flatMap(r => r.warnings || []); return { items, warnings }; } @@ -425,8 +424,8 @@ async function loadTools(ctx: LoadContext): Promise> { }), ]); - const items = results.flatMap((r) => r.items); - const warnings = results.flatMap((r) => r.warnings || []); + const items = results.flatMap(r => r.items); + const warnings = results.flatMap(r => r.warnings || []); return { items, warnings }; } diff --git a/packages/coding-agent/src/discovery/cursor.ts b/packages/coding-agent/src/discovery/cursor.ts index c87b4688b..47b48eabc 100644 --- a/packages/coding-agent/src/discovery/cursor.ts +++ b/packages/coding-agent/src/discovery/cursor.ts @@ -13,7 +13,6 @@ * - rules: From rules/*.mdc files with MDC frontmatter (description, globs, alwaysApply) * - settings: From settings.json if present */ - import { registerProvider } from "../capability"; import { readFile } from "../capability/fs"; import { type MCPServer, mcpCapability } from "../capability/mcp"; diff --git a/packages/coding-agent/src/discovery/gemini.ts b/packages/coding-agent/src/discovery/gemini.ts index dc704fe33..4546c2ca4 100644 --- a/packages/coding-agent/src/discovery/gemini.ts +++ b/packages/coding-agent/src/discovery/gemini.ts @@ -15,7 +15,6 @@ * - extensions: From extensions/STAR/gemini-extension.json manifests (STAR = wildcard) * - settings: From settings.json */ - import * as path from "node:path"; import { registerProvider } from "../capability"; import { type ContextFile, contextFileCapability } from "../capability/context-file"; @@ -190,10 +189,10 @@ async function loadExtensions(ctx: LoadContext): Promise> async function loadExtensionsFromDir(extensionsDir: string, level: "user" | "project"): Promise> { const entries = await readDirEntries(extensionsDir); - const dirEntries = entries.filter((entry) => entry.isDirectory()); + const dirEntries = entries.filter(entry => entry.isDirectory()); const results = await Promise.all( - dirEntries.map(async (entry) => { + dirEntries.map(async entry => { const extPath = path.join(extensionsDir, entry.name); const manifestPath = path.join(extPath, "gemini-extension.json"); const content = await readFile(manifestPath); @@ -239,13 +238,13 @@ async function loadExtensionModules(ctx: LoadContext): Promise ({ + ...userPaths.map(extPath => ({ name: getExtensionNameFromPath(extPath), path: extPath, level: "user" as const, _source: createSourceMeta(PROVIDER_ID, extPath, "user"), })), - ...projectPaths.map((extPath) => ({ + ...projectPaths.map(extPath => ({ name: getExtensionNameFromPath(extPath), path: extPath, level: "project" as const, diff --git a/packages/coding-agent/src/discovery/github.ts b/packages/coding-agent/src/discovery/github.ts index 1e01ddb3d..5ddc95109 100644 --- a/packages/coding-agent/src/discovery/github.ts +++ b/packages/coding-agent/src/discovery/github.ts @@ -11,7 +11,6 @@ * - context-files: copilot-instructions.md in .github/ * - instructions: *.instructions.md in .github/instructions/ with applyTo frontmatter */ - import * as path from "node:path"; import { registerProvider } from "../capability"; import { type ContextFile, contextFileCapability } from "../capability/context-file"; diff --git a/packages/coding-agent/src/discovery/helpers.ts b/packages/coding-agent/src/discovery/helpers.ts index 0315e9f32..fc81680b2 100644 --- a/packages/coding-agent/src/discovery/helpers.ts +++ b/packages/coding-agent/src/discovery/helpers.ts @@ -1,7 +1,6 @@ /** * Shared helpers for discovery providers. */ - import * as os from "node:os"; import * as path from "node:path"; import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core"; @@ -137,7 +136,7 @@ export function parseThinkingLevel(frontmatter: Record): Thinki export function parseCSV(value: string): string[] { return value .split(",") - .map((s) => s.trim()) + .map(s => s.trim()) .filter(Boolean); } @@ -224,11 +223,11 @@ export async function loadSkillsFromDir( const entries = await readDirEntries(dir); const skillDirs = entries.filter( - (entry) => entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules", + entry => entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules", ); const results = await Promise.all( - skillDirs.map(async (entry) => { + skillDirs.map(async entry => { const skillFile = path.join(dir, entry.name, "SKILL.md"); const content = await readFile(skillFile); if (!content) { @@ -283,7 +282,7 @@ export function expandEnvVarsDeep(obj: T, extraEnv?: Record): return expandEnvVars(obj, extraEnv) as T; } if (Array.isArray(obj)) { - return obj.map((item) => expandEnvVarsDeep(item, extraEnv)) as T; + return obj.map(item => expandEnvVarsDeep(item, extraEnv)) as T; } if (obj !== null && typeof obj === "object") { const result: Record = {}; @@ -314,23 +313,23 @@ export async function loadFilesFromDir( ): Promise> { const entries = await readDirEntries(dir); - const visibleEntries = entries.filter((entry) => !entry.name.startsWith(".")); + const visibleEntries = entries.filter(entry => !entry.name.startsWith(".")); - const directories = options.recursive ? visibleEntries.filter((entry) => entry.isDirectory()) : []; + const directories = options.recursive ? visibleEntries.filter(entry => entry.isDirectory()) : []; const files = visibleEntries - .filter((entry) => entry.isFile()) - .filter((entry) => { + .filter(entry => entry.isFile()) + .filter(entry => { if (!options.extensions) return true; - return options.extensions.some((ext) => entry.name.endsWith(`.${ext}`)); + return options.extensions.some(ext => entry.name.endsWith(`.${ext}`)); }); const [subResults, fileResults] = await Promise.all([ Promise.all( - directories.map((entry) => loadFilesFromDir(_ctx, path.join(dir, entry.name), provider, level, options)), + directories.map(entry => loadFilesFromDir(_ctx, path.join(dir, entry.name), provider, level, options)), ), Promise.all( - files.map(async (entry) => { + files.map(async entry => { const filePath = path.join(dir, entry.name); const content = await readFile(filePath); return { entry, path: filePath, content }; @@ -440,7 +439,7 @@ export async function discoverExtensionModulePaths(ctx: LoadContext, dir: string // 2 & 3. Subdirectories if (entry.isDirectory()) { const subEntries = await readDirEntries(entryPath); - const subFileNames = new Set(subEntries.filter((e) => e.isFile()).map((e) => e.name)); + const subFileNames = new Set(subEntries.filter(e => e.isFile()).map(e => e.name)); // Check for package.json with "omp"/"pi" field first if (subFileNames.has("package.json")) { diff --git a/packages/coding-agent/src/discovery/index.ts b/packages/coding-agent/src/discovery/index.ts index 5acfd44c7..b76da626f 100644 --- a/packages/coding-agent/src/discovery/index.ts +++ b/packages/coding-agent/src/discovery/index.ts @@ -4,7 +4,6 @@ * Auto-registers all providers by importing them. * Import this module to ensure all providers are registered with the capability registry. */ - // Import capability definitions (ensures capabilities are defined before providers register) import "../capability/context-file"; import "../capability/extension"; @@ -20,7 +19,6 @@ import "../capability/slash-command"; import "../capability/ssh"; import "../capability/system-prompt"; import "../capability/tool"; - // Import providers (each registers itself on import) import "./agents-md"; import "./builtin"; diff --git a/packages/coding-agent/src/discovery/mcp-json.ts b/packages/coding-agent/src/discovery/mcp-json.ts index b9d6d0ea6..cc6f91a64 100644 --- a/packages/coding-agent/src/discovery/mcp-json.ts +++ b/packages/coding-agent/src/discovery/mcp-json.ts @@ -6,7 +6,6 @@ * * Priority: 5 (low, as this is a fallback after tool-specific providers) */ - import * as path from "node:path"; import { registerProvider } from "../capability"; import { readFile } from "../capability/fs"; @@ -102,11 +101,11 @@ async function loadMCPJsonFile( async function load(ctx: LoadContext): Promise> { const filenames = ["mcp.json", ".mcp.json"]; const results = await Promise.all( - filenames.map((filename) => loadMCPJsonFile(ctx, path.join(ctx.cwd, filename), "project")), + filenames.map(filename => loadMCPJsonFile(ctx, path.join(ctx.cwd, filename), "project")), ); - const allItems = results.flatMap((r) => r.items); - const allWarnings = results.flatMap((r) => r.warnings ?? []); + const allItems = results.flatMap(r => r.items); + const allWarnings = results.flatMap(r => r.warnings ?? []); return { items: allItems, diff --git a/packages/coding-agent/src/discovery/ssh.ts b/packages/coding-agent/src/discovery/ssh.ts index ded5d3816..1f00b5983 100644 --- a/packages/coding-agent/src/discovery/ssh.ts +++ b/packages/coding-agent/src/discovery/ssh.ts @@ -4,7 +4,6 @@ * Discovers SSH hosts from ssh.json or .ssh.json in the project root. * Priority: 5 (low, project-level only) */ - import * as path from "node:path"; import { registerProvider } from "../capability"; import { readFile } from "../capability/fs"; @@ -134,10 +133,10 @@ async function loadSshJsonFile(_ctx: LoadContext, path: string): Promise> { const filenames = ["ssh.json", ".ssh.json"]; - const results = await Promise.all(filenames.map((filename) => loadSshJsonFile(ctx, path.join(ctx.cwd, filename)))); + const results = await Promise.all(filenames.map(filename => loadSshJsonFile(ctx, path.join(ctx.cwd, filename)))); - const allItems = results.flatMap((r) => r.items); - const allWarnings = results.flatMap((r) => r.warnings ?? []); + const allItems = results.flatMap(r => r.items); + const allWarnings = results.flatMap(r => r.warnings ?? []); return { items: allItems, diff --git a/packages/coding-agent/src/discovery/vscode.ts b/packages/coding-agent/src/discovery/vscode.ts index bd9569bd1..3ce6e3b09 100644 --- a/packages/coding-agent/src/discovery/vscode.ts +++ b/packages/coding-agent/src/discovery/vscode.ts @@ -4,7 +4,6 @@ * Loads config from `.vscode` directory (project-only). * Supports MCP server discovery from `mcp.json` with nested `mcp.servers` structure. */ - import { registerProvider } from "../capability"; import { readFile } from "../capability/fs"; import { type MCPServer, mcpCapability } from "../capability/mcp"; diff --git a/packages/coding-agent/src/discovery/windsurf.ts b/packages/coding-agent/src/discovery/windsurf.ts index 097bed841..8b75bb11e 100644 --- a/packages/coding-agent/src/discovery/windsurf.ts +++ b/packages/coding-agent/src/discovery/windsurf.ts @@ -10,7 +10,6 @@ * - Rules from .windsurf/rules/*.md and ~/.codeium/windsurf/memories/global_rules.md * - Legacy .windsurfrules file */ - import { registerProvider } from "../capability"; import { readFile } from "../capability/fs"; import { type MCPServer, mcpCapability } from "../capability/mcp"; diff --git a/packages/coding-agent/src/exa/company.ts b/packages/coding-agent/src/exa/company.ts index ddc7f1552..e7af2bf39 100644 --- a/packages/coding-agent/src/exa/company.ts +++ b/packages/coding-agent/src/exa/company.ts @@ -3,7 +3,6 @@ * * Research companies using Exa's comprehensive data sources. */ - import { Type } from "@sinclair/typebox"; import type { CustomTool } from "../extensibility/custom-tools/types"; import { callExaTool, findApiKey, formatSearchResults, isSearchResponse } from "./mcp-client"; diff --git a/packages/coding-agent/src/exa/index.ts b/packages/coding-agent/src/exa/index.ts index 7ba2a22d1..79c86939c 100644 --- a/packages/coding-agent/src/exa/index.ts +++ b/packages/coding-agent/src/exa/index.ts @@ -8,7 +8,6 @@ * - 2 researcher tools (start, poll) * - 14 websets tools (CRUD, items, search, enrichment, monitor) */ - import type { ExaSettings } from "../config/settings-manager"; import type { CustomTool } from "../extensibility/custom-tools/types"; import { companyTool } from "./company"; diff --git a/packages/coding-agent/src/exa/linkedin.ts b/packages/coding-agent/src/exa/linkedin.ts index 6d5d592dd..dfd191311 100644 --- a/packages/coding-agent/src/exa/linkedin.ts +++ b/packages/coding-agent/src/exa/linkedin.ts @@ -3,7 +3,6 @@ * * Search LinkedIn for people, companies, and professional content. */ - import { Type } from "@sinclair/typebox"; import type { CustomTool } from "../extensibility/custom-tools/types"; import { callExaTool, findApiKey, formatSearchResults, isSearchResponse } from "./mcp-client"; diff --git a/packages/coding-agent/src/exa/mcp-client.ts b/packages/coding-agent/src/exa/mcp-client.ts index 6ddcfed09..dcb2de92e 100644 --- a/packages/coding-agent/src/exa/mcp-client.ts +++ b/packages/coding-agent/src/exa/mcp-client.ts @@ -3,11 +3,9 @@ * * Client for interacting with Exa MCP servers. */ - import * as os from "node:os"; import { isEnoent, logger } from "@oh-my-pi/pi-utils"; import type { TSchema } from "@sinclair/typebox"; - import type { CustomTool, CustomToolResult } from "../extensibility/custom-tools/types"; import { callMCP } from "../mcp/json-rpc"; import type { @@ -231,7 +229,7 @@ export async function fetchMCPToolSchema( try { const tools = isWebsetsTool ? await fetchWebsetsTools(apiKey) : await fetchExaTools(apiKey, [mcpToolName]); - const tool = tools.find((t) => t.name === mcpToolName); + const tool = tools.find(t => t.name === mcpToolName); if (tool) { mcpSchemaCache.set(cacheKey, tool); return tool; diff --git a/packages/coding-agent/src/exa/render.ts b/packages/coding-agent/src/exa/render.ts index 4ea1b6075..a636fc1eb 100644 --- a/packages/coding-agent/src/exa/render.ts +++ b/packages/coding-agent/src/exa/render.ts @@ -3,7 +3,6 @@ * * Tree-based rendering with collapsed/expanded states for Exa search results. */ - import type { Component } from "@oh-my-pi/pi-tui"; import { Text } from "@oh-my-pi/pi-tui"; import { logger } from "@oh-my-pi/pi-utils"; @@ -60,7 +59,7 @@ export function renderExaResult( if (!response) { if (details?.raw) { const rawText = typeof details.raw === "string" ? details.raw : JSON.stringify(details.raw, null, 2); - const rawLines = rawText.split("\n").filter((l) => l.trim()); + const rawLines = rawText.split("\n").filter(l => l.trim()); const maxLines = expanded ? rawLines.length : Math.min(rawLines.length, COLLAPSED_PREVIEW_LINES); const displayLines = rawLines.slice(0, maxLines); const remaining = rawLines.length - maxLines; @@ -104,7 +103,7 @@ export function renderExaResult( let hasMorePreview = false; if (!expanded && resultCount > 0) { const previewText = results[0].text ?? results[0].title ?? ""; - const totalLines = previewText.split("\n").filter((l) => l.trim()).length; + const totalLines = previewText.split("\n").filter(l => l.trim()).length; hasMorePreview = totalLines > COLLAPSED_PREVIEW_LINES || resultCount > 1; } const expandHint = formatExpandHint(uiTheme, expanded, hasMorePreview); @@ -123,7 +122,7 @@ export function renderExaResult( ? getPreviewLines(previewText, COLLAPSED_PREVIEW_LINES, COLLAPSED_PREVIEW_LINE_LEN, uiTheme.format.ellipsis) : []; const safePreviewLines = previewLines.length > 0 ? previewLines : ["No preview text"]; - const totalLines = previewText.split("\n").filter((l) => l.trim()).length; + const totalLines = previewText.split("\n").filter(l => l.trim()).length; const remainingLines = Math.max(0, totalLines - previewLines.length); const extraItems: string[] = []; if (remainingLines > 0) { @@ -189,7 +188,7 @@ export function renderExaResult( } if (res.text) { - const textLines = res.text.split("\n").filter((l) => l.trim()); + const textLines = res.text.split("\n").filter(l => l.trim()); const displayLines = textLines.slice(0, EXPANDED_TEXT_LINES); for (const line of displayLines) { text += `\n ${uiTheme.fg("dim", cont)} ${uiTheme.fg("dim", uiTheme.tree.hook)} ${uiTheme.fg( diff --git a/packages/coding-agent/src/exa/researcher.ts b/packages/coding-agent/src/exa/researcher.ts index 9d1dd25a6..61e1b14fe 100644 --- a/packages/coding-agent/src/exa/researcher.ts +++ b/packages/coding-agent/src/exa/researcher.ts @@ -3,7 +3,6 @@ * * Async research tasks with polling for completion. */ - import { Type } from "@sinclair/typebox"; import type { CustomTool } from "../extensibility/custom-tools/types"; import { callExaTool, findApiKey } from "./mcp-client"; diff --git a/packages/coding-agent/src/exa/search.ts b/packages/coding-agent/src/exa/search.ts index b4e14f560..25cd40db7 100644 --- a/packages/coding-agent/src/exa/search.ts +++ b/packages/coding-agent/src/exa/search.ts @@ -3,7 +3,6 @@ * * Basic neural/keyword search, deep research, code search, and URL crawling. */ - import { StringEnum } from "@oh-my-pi/pi-ai"; import { Type } from "@sinclair/typebox"; import type { CustomTool } from "../extensibility/custom-tools/types"; diff --git a/packages/coding-agent/src/exa/types.ts b/packages/coding-agent/src/exa/types.ts index 1ee91c8fa..fcb063c71 100644 --- a/packages/coding-agent/src/exa/types.ts +++ b/packages/coding-agent/src/exa/types.ts @@ -3,7 +3,6 @@ * * Types for the Exa MCP client and tool implementations. */ - import type { TSchema } from "@sinclair/typebox"; /** MCP endpoint URLs */ diff --git a/packages/coding-agent/src/exa/websets.ts b/packages/coding-agent/src/exa/websets.ts index 7837ff7ea..e3d7dfb52 100644 --- a/packages/coding-agent/src/exa/websets.ts +++ b/packages/coding-agent/src/exa/websets.ts @@ -3,7 +3,6 @@ * * CRUD operations for websets, items, searches, enrichments, and monitoring. */ - import { Type } from "@sinclair/typebox"; import type { CustomTool } from "../extensibility/custom-tools/types"; import { callWebsetsTool, findApiKey } from "./mcp-client"; diff --git a/packages/coding-agent/src/exec/bash-executor.ts b/packages/coding-agent/src/exec/bash-executor.ts index a0cdde9e8..f20fc3dc7 100644 --- a/packages/coding-agent/src/exec/bash-executor.ts +++ b/packages/coding-agent/src/exec/bash-executor.ts @@ -3,7 +3,6 @@ * * Provides unified bash execution for AgentSession.executeBash() and direct calls. */ - import { cspawn, Exception, ptree } from "@oh-my-pi/pi-utils"; import { OutputSink } from "../session/streaming-output"; import { getShellConfig } from "../utils/shell"; diff --git a/packages/coding-agent/src/exec/exec.ts b/packages/coding-agent/src/exec/exec.ts index cbe9d8d1a..311cf31b6 100644 --- a/packages/coding-agent/src/exec/exec.ts +++ b/packages/coding-agent/src/exec/exec.ts @@ -1,7 +1,6 @@ /** * Shared command execution utilities for hooks and custom tools. */ - import { ptree } from "@oh-my-pi/pi-utils"; /** diff --git a/packages/coding-agent/src/export/custom-share.ts b/packages/coding-agent/src/export/custom-share.ts index 688ce3116..4601751c2 100644 --- a/packages/coding-agent/src/export/custom-share.ts +++ b/packages/coding-agent/src/export/custom-share.ts @@ -4,7 +4,6 @@ * Allows users to define a custom share handler at ~/.omp/agent/share.ts * that will be used instead of the default GitHub Gist sharing. */ - import * as fs from "node:fs"; import * as path from "node:path"; import { getAgentDir } from "../config"; diff --git a/packages/coding-agent/src/export/html/index.ts b/packages/coding-agent/src/export/html/index.ts index 7262e8669..d9b43224f 100644 --- a/packages/coding-agent/src/export/html/index.ts +++ b/packages/coding-agent/src/export/html/index.ts @@ -4,7 +4,6 @@ import { isEnoent } from "@oh-my-pi/pi-utils"; import { APP_NAME } from "../../config"; import { getResolvedThemeColors, getThemeExportColors } from "../../modes/theme/theme"; import { SessionManager } from "../../session/session-manager"; - // Pre-generated template (created by scripts/generate-template.ts at publish time) import { TEMPLATE } from "./template.generated"; @@ -127,7 +126,7 @@ export async function exportSessionToHtml( entries: sm.getEntries(), leafId: sm.getLeafId(), systemPrompt: state?.systemPrompt, - tools: state?.tools?.map((t) => ({ name: t.name, description: t.description })), + tools: state?.tools?.map(t => ({ name: t.name, description: t.description })), }; const html = await generateHtml(sessionData, opts.themeName); diff --git a/packages/coding-agent/src/export/ttsr.ts b/packages/coding-agent/src/export/ttsr.ts index aa8fc4429..ec29ad53f 100644 --- a/packages/coding-agent/src/export/ttsr.ts +++ b/packages/coding-agent/src/export/ttsr.ts @@ -5,7 +5,6 @@ * the agent's output. When a match occurs, the stream is aborted, the rule is * injected as a system reminder, and the request is retried. */ - import { logger } from "@oh-my-pi/pi-utils"; import type { Rule } from "../capability/rule"; import type { TtsrSettings } from "../config/settings-manager"; diff --git a/packages/coding-agent/src/extensibility/custom-commands/bundled/review/index.ts b/packages/coding-agent/src/extensibility/custom-commands/bundled/review/index.ts index 20ef62963..6f7d60a9d 100644 --- a/packages/coding-agent/src/extensibility/custom-commands/bundled/review/index.ts +++ b/packages/coding-agent/src/extensibility/custom-commands/bundled/review/index.ts @@ -11,7 +11,6 @@ * rich context for the orchestrating agent to distribute work across * multiple reviewer agents based on diff weight and locality. */ - import { renderPromptTemplate } from "../../../../config/prompt-templates"; import type { CustomCommand, CustomCommandAPI } from "../../../../extensibility/custom-commands/types"; import type { HookCommandContext } from "../../../../extensibility/hooks/types"; @@ -203,7 +202,7 @@ function buildReviewPrompt(mode: string, stats: DiffStats, rawDiff: string): str const totalLines = stats.totalAdded + stats.totalRemoved; const linesPerFile = skipDiff ? Math.max(5, Math.floor(100 / stats.files.length)) : 0; - const filesWithExt = stats.files.map((f) => ({ + const filesWithExt = stats.files.map(f => ({ ...f, ext: getFileExt(f.path), hunksPreview: skipDiff ? getDiffPreview(f.hunks, linesPerFile) : "", @@ -393,7 +392,7 @@ async function getGitBranches(api: CustomCommandAPI): Promise { if (result.code !== 0) return []; return result.stdout .split("\n") - .map((b) => b.trim()) + .map(b => b.trim()) .filter(Boolean); } catch { return []; @@ -424,7 +423,7 @@ async function getRecentCommits(api: CustomCommandAPI, count: number): Promise c.trim()) + .map(c => c.trim()) .filter(Boolean); } catch { return []; diff --git a/packages/coding-agent/src/extensibility/custom-commands/loader.ts b/packages/coding-agent/src/extensibility/custom-commands/loader.ts index 3cd0dd99c..f563982e0 100644 --- a/packages/coding-agent/src/extensibility/custom-commands/loader.ts +++ b/packages/coding-agent/src/extensibility/custom-commands/loader.ts @@ -4,7 +4,6 @@ * Dependencies (@sinclair/typebox and pi-coding-agent) are injected via the CustomCommandAPI * to avoid import resolution issues with custom commands loaded from user directories. */ - import * as fs from "node:fs"; import * as path from "node:path"; import * as piCodingAgent from "@oh-my-pi/pi-coding-agent"; @@ -102,7 +101,7 @@ export async function discoverCustomCommands( for (const entry of getConfigDirs("commands", { cwd, existingOnly: true })) { const source = entry.level === "user" ? "user" : "project"; - if (!commandDirs.some((d) => d.path === entry.path)) { + if (!commandDirs.some(d => d.path === entry.path)) { commandDirs.push({ path: entry.path, source }); } } @@ -199,7 +198,7 @@ export async function loadCustomCommands(options: LoadCustomCommandsOptions = {} if (loadedCommands) { for (const command of loadedCommands) { // Allow overriding bundled commands, but not user/project conflicts - const existingIdx = commands.findIndex((c) => c.command.name === command.name); + const existingIdx = commands.findIndex(c => c.command.name === command.name); if (existingIdx !== -1) { const existing = commands[existingIdx]; if (existing.source === "bundled") { diff --git a/packages/coding-agent/src/extensibility/custom-commands/types.ts b/packages/coding-agent/src/extensibility/custom-commands/types.ts index 4773fb335..c368a7267 100644 --- a/packages/coding-agent/src/extensibility/custom-commands/types.ts +++ b/packages/coding-agent/src/extensibility/custom-commands/types.ts @@ -5,7 +5,6 @@ * Unlike markdown commands which expand to prompts, custom commands can execute * arbitrary logic with full access to the hook context. */ - import type { ExecOptions, ExecResult, HookCommandContext } from "../../extensibility/hooks/types"; // Re-export for custom commands to use diff --git a/packages/coding-agent/src/extensibility/custom-tools/loader.ts b/packages/coding-agent/src/extensibility/custom-tools/loader.ts index 8fd6887c0..f0a909181 100644 --- a/packages/coding-agent/src/extensibility/custom-tools/loader.ts +++ b/packages/coding-agent/src/extensibility/custom-tools/loader.ts @@ -4,7 +4,6 @@ * Dependencies (@sinclair/typebox and pi-coding-agent) are injected via the CustomToolAPI * to avoid import resolution issues with custom tools loaded from user directories. */ - import * as path from "node:path"; import * as piCodingAgent from "@oh-my-pi/pi-coding-agent"; import { logger } from "@oh-my-pi/pi-utils"; @@ -90,7 +89,7 @@ async function loadTool( const toolResult = await factory(sharedApi); const toolsArray = Array.isArray(toolResult) ? toolResult : [toolResult]; - const loadedTools: LoadedCustomTool[] = toolsArray.map((tool) => ({ + const loadedTools: LoadedCustomTool[] = toolsArray.map(tool => ({ path: toolPath, resolvedPath, tool, diff --git a/packages/coding-agent/src/extensibility/custom-tools/types.ts b/packages/coding-agent/src/extensibility/custom-tools/types.ts index 64b259cd2..596aebb6f 100644 --- a/packages/coding-agent/src/extensibility/custom-tools/types.ts +++ b/packages/coding-agent/src/extensibility/custom-tools/types.ts @@ -4,7 +4,6 @@ * Custom tools are TypeScript modules that define additional tools for the agent. * They can provide custom rendering for tool calls and results in the TUI. */ - import type { AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core"; import type { Model } from "@oh-my-pi/pi-ai"; import type { Component } from "@oh-my-pi/pi-tui"; diff --git a/packages/coding-agent/src/extensibility/custom-tools/wrapper.ts b/packages/coding-agent/src/extensibility/custom-tools/wrapper.ts index b239f010d..baf16bb35 100644 --- a/packages/coding-agent/src/extensibility/custom-tools/wrapper.ts +++ b/packages/coding-agent/src/extensibility/custom-tools/wrapper.ts @@ -1,7 +1,6 @@ /** * CustomToolAdapter wraps CustomTool instances into AgentTool for use with the agent. */ - import type { AgentTool, AgentToolResult, AgentToolUpdateCallback, RenderResultOptions } from "@oh-my-pi/pi-agent-core"; import type { Component } from "@oh-my-pi/pi-tui"; import type { Static, TSchema } from "@sinclair/typebox"; @@ -64,6 +63,6 @@ export class CustomToolAdapter[], getContext: () => CustomToolContext, ): AgentTool[] { - return loadedTools.map((lt) => CustomToolAdapter.wrap(lt.tool, getContext)); + return loadedTools.map(lt => CustomToolAdapter.wrap(lt.tool, getContext)); } } diff --git a/packages/coding-agent/src/extensibility/extensions/loader.ts b/packages/coding-agent/src/extensibility/extensions/loader.ts index e1164f578..0f1413952 100644 --- a/packages/coding-agent/src/extensibility/extensions/loader.ts +++ b/packages/coding-agent/src/extensibility/extensions/loader.ts @@ -1,7 +1,6 @@ /** * Extension loader - loads TypeScript extension modules using native Bun import. */ - import type * as fs1 from "node:fs"; import * as fs from "node:fs/promises"; import * as path from "node:path"; diff --git a/packages/coding-agent/src/extensibility/extensions/runner.ts b/packages/coding-agent/src/extensibility/extensions/runner.ts index f5f1064f3..cf977336d 100644 --- a/packages/coding-agent/src/extensibility/extensions/runner.ts +++ b/packages/coding-agent/src/extensibility/extensions/runner.ts @@ -1,7 +1,6 @@ /** * Extension runner - executes extensions and manages their lifecycle. */ - import type { AgentMessage } from "@oh-my-pi/pi-agent-core"; import type { ImageContent, Model } from "@oh-my-pi/pi-ai"; import type { KeyId } from "@oh-my-pi/pi-tui"; @@ -184,7 +183,7 @@ export class ExtensionRunner { } getExtensionPaths(): string[] { - return this.extensions.map((e) => e.path); + return this.extensions.map(e => e.path); } /** Get all registered tools from all extensions. */ @@ -313,7 +312,7 @@ export class ExtensionRunner { return { ui: this.uiContext, getContextUsage: () => this.getContextUsageFn(), - compact: (instructionsOrOptions) => this.compactFn(instructionsOrOptions), + compact: instructionsOrOptions => this.compactFn(instructionsOrOptions), hasUI: this.hasUI(), cwd: this.cwd, sessionManager: this.sessionManager, @@ -341,10 +340,10 @@ export class ExtensionRunner { ...this.createContext(), getContextUsage: () => this.getContextUsageFn(), waitForIdle: () => this.waitForIdleFn(), - newSession: (options) => this.newSessionHandler(options), - branch: (entryId) => this.branchHandler(entryId), + newSession: options => this.newSessionHandler(options), + branch: entryId => this.branchHandler(entryId), navigateTree: (targetId, options) => this.navigateTreeHandler(targetId, options), - compact: (instructionsOrOptions) => this.compactFn(instructionsOrOptions), + compact: instructionsOrOptions => this.compactFn(instructionsOrOptions), }; } diff --git a/packages/coding-agent/src/extensibility/extensions/types.ts b/packages/coding-agent/src/extensibility/extensions/types.ts index 7d261762d..01857c0b1 100644 --- a/packages/coding-agent/src/extensibility/extensions/types.ts +++ b/packages/coding-agent/src/extensibility/extensions/types.ts @@ -7,7 +7,6 @@ * - Register commands, keyboard shortcuts, and CLI flags * - Interact with the user via UI primitives */ - import type { AgentMessage, AgentToolResult, AgentToolUpdateCallback, ThinkingLevel } from "@oh-my-pi/pi-agent-core"; import type { ImageContent, Model, TextContent, ToolResultMessage } from "@oh-my-pi/pi-ai"; import type * as piCodingAgent from "@oh-my-pi/pi-coding-agent"; diff --git a/packages/coding-agent/src/extensibility/extensions/wrapper.ts b/packages/coding-agent/src/extensibility/extensions/wrapper.ts index 4491cea56..6064bace0 100644 --- a/packages/coding-agent/src/extensibility/extensions/wrapper.ts +++ b/packages/coding-agent/src/extensibility/extensions/wrapper.ts @@ -1,7 +1,6 @@ /** * Tool wrappers for extensions. */ - import type { AgentTool, AgentToolContext, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core"; import type { ImageContent, TextContent } from "@oh-my-pi/pi-ai"; import type { Static, TSchema } from "@sinclair/typebox"; @@ -63,7 +62,7 @@ export function wrapRegisteredTool(registeredTool: RegisteredTool, runner: Exten * Wrap all registered tools into AgentTools. */ export function wrapRegisteredTools(registeredTools: RegisteredTool[], runner: ExtensionRunner): AgentTool[] { - return registeredTools.map((rt) => wrapRegisteredTool(rt, runner)); + return registeredTools.map(rt => wrapRegisteredTool(rt, runner)); } /** @@ -156,7 +155,7 @@ export class ExtensionToolWrapper c.type === "text"); - const errorText = textBlocks.map((t) => t.text).join("\n") || "Tool result marked as error by extension"; + const errorText = textBlocks.map(t => t.text).join("\n") || "Tool result marked as error by extension"; throw new Error(errorText); } if (resultResult.isError === false && executionError) { diff --git a/packages/coding-agent/src/extensibility/hooks/loader.ts b/packages/coding-agent/src/extensibility/hooks/loader.ts index a2d0c5cd6..3b1985b2b 100644 --- a/packages/coding-agent/src/extensibility/hooks/loader.ts +++ b/packages/coding-agent/src/extensibility/hooks/loader.ts @@ -1,7 +1,6 @@ /** * Hook loader - loads TypeScript hook modules using native Bun import. */ - import * as path from "node:path"; import * as piCodingAgent from "@oh-my-pi/pi-coding-agent"; import { logger } from "@oh-my-pi/pi-utils"; @@ -265,10 +264,10 @@ export async function discoverAndLoadHooks(configuredPaths: string[], cwd: strin // 1. Discover hooks via capability API const discovered = await loadCapability(hookCapability.id, { cwd }); - addPaths(discovered.items.map((hook) => hook.path)); + addPaths(discovered.items.map(hook => hook.path)); // 2. Explicitly configured paths (can override/add) - addPaths(configuredPaths.map((p) => resolveHookPath(p, cwd))); + addPaths(configuredPaths.map(p => resolveHookPath(p, cwd))); return loadHooks(allPaths, cwd); } diff --git a/packages/coding-agent/src/extensibility/hooks/runner.ts b/packages/coding-agent/src/extensibility/hooks/runner.ts index a692308d6..1b56091ae 100644 --- a/packages/coding-agent/src/extensibility/hooks/runner.ts +++ b/packages/coding-agent/src/extensibility/hooks/runner.ts @@ -1,7 +1,6 @@ /** * Hook runner - executes hooks and manages their lifecycle. */ - import type { AgentMessage } from "@oh-my-pi/pi-agent-core"; import type { Model } from "@oh-my-pi/pi-ai"; import type { ModelRegistry } from "../../config/model-registry"; @@ -159,7 +158,7 @@ export class HookRunner { * Get the paths of all loaded hooks. */ getHookPaths(): string[] { - return this.hooks.map((h) => h.path); + return this.hooks.map(h => h.path); } /** @@ -262,8 +261,8 @@ export class HookRunner { return { ...this.createContext(), waitForIdle: () => this.waitForIdleFn(), - newSession: (options) => this.newSessionHandler(options), - branch: (entryId) => this.branchHandler(entryId), + newSession: options => this.newSessionHandler(options), + branch: entryId => this.branchHandler(entryId), navigateTree: (targetId, options) => this.navigateTreeHandler(targetId, options), }; } diff --git a/packages/coding-agent/src/extensibility/hooks/tool-wrapper.ts b/packages/coding-agent/src/extensibility/hooks/tool-wrapper.ts index fc5fc5024..025a8ec08 100644 --- a/packages/coding-agent/src/extensibility/hooks/tool-wrapper.ts +++ b/packages/coding-agent/src/extensibility/hooks/tool-wrapper.ts @@ -1,7 +1,6 @@ /** * Tool wrapper - wraps tools with hook callbacks for interception. */ - import type { AgentTool, AgentToolContext, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core"; import type { Static, TSchema } from "@sinclair/typebox"; import type { HookRunner } from "./runner"; diff --git a/packages/coding-agent/src/extensibility/hooks/types.ts b/packages/coding-agent/src/extensibility/hooks/types.ts index ff047e0b7..8dd72d3fb 100644 --- a/packages/coding-agent/src/extensibility/hooks/types.ts +++ b/packages/coding-agent/src/extensibility/hooks/types.ts @@ -4,7 +4,6 @@ * Hooks are TypeScript modules that can subscribe to agent lifecycle events * and interact with the user via UI primitives. */ - import type { AgentMessage } from "@oh-my-pi/pi-agent-core"; import type { ImageContent, Message, Model, TextContent, ToolResultMessage } from "@oh-my-pi/pi-ai"; import type { Component, TUI } from "@oh-my-pi/pi-tui"; diff --git a/packages/coding-agent/src/extensibility/plugins/doctor.ts b/packages/coding-agent/src/extensibility/plugins/doctor.ts index 53afd05ed..38cb6c3ed 100644 --- a/packages/coding-agent/src/extensibility/plugins/doctor.ts +++ b/packages/coding-agent/src/extensibility/plugins/doctor.ts @@ -57,8 +57,8 @@ export function formatDoctorResults(checks: DoctorCheck[]): string { lines.push(`${icon} ${check.name}: ${check.message}`); } - const errors = checks.filter((c) => c.status === "error").length; - const warnings = checks.filter((c) => c.status === "warning").length; + const errors = checks.filter(c => c.status === "error").length; + const warnings = checks.filter(c => c.status === "warning").length; lines.push(""); lines.push(`Summary: ${checks.length - errors - warnings} ok, ${warnings} warnings, ${errors} errors`); diff --git a/packages/coding-agent/src/extensibility/plugins/loader.ts b/packages/coding-agent/src/extensibility/plugins/loader.ts index 2b02b23dd..08f9780ba 100644 --- a/packages/coding-agent/src/extensibility/plugins/loader.ts +++ b/packages/coding-agent/src/extensibility/plugins/loader.ts @@ -4,7 +4,6 @@ * Reads enabled plugins from the runtime config and loads their tools/hooks * based on manifest entries and enabled features. */ - import * as fs from "node:fs"; import * as path from "node:path"; import { isEnoent } from "@oh-my-pi/pi-utils"; diff --git a/packages/coding-agent/src/extensibility/plugins/manager.ts b/packages/coding-agent/src/extensibility/plugins/manager.ts index 22d26c6ef..2e10c4517 100644 --- a/packages/coding-agent/src/extensibility/plugins/manager.ts +++ b/packages/coding-agent/src/extensibility/plugins/manager.ts @@ -408,7 +408,7 @@ export class PluginManager { // Validate features if setting specific ones if (features && features.length > 0) { const plugins = await this.list(); - const plugin = plugins.find((p) => p.name === name); + const plugin = plugins.find(p => p.name === name); if (plugin?.manifest.features) { for (const feat of features) { if (!(feat in plugin.manifest.features)) { @@ -632,7 +632,7 @@ export class PluginManager { const config = await this.ensureConfigLoaded(); const state = config.plugins[name]; if (state?.enabledFeatures) { - state.enabledFeatures = state.enabledFeatures.filter((f) => f !== feat); + state.enabledFeatures = state.enabledFeatures.filter(f => f !== feat); await this.saveRuntimeConfig(); return true; } diff --git a/packages/coding-agent/src/extensibility/plugins/parser.ts b/packages/coding-agent/src/extensibility/plugins/parser.ts index 9ae1faa23..43bd86fb6 100644 --- a/packages/coding-agent/src/extensibility/plugins/parser.ts +++ b/packages/coding-agent/src/extensibility/plugins/parser.ts @@ -57,7 +57,7 @@ export function parsePluginSpec(spec: string): ParsedPluginSpec { // Specific features (comma-separated) const features = featureStr .split(",") - .map((f) => f.trim()) + .map(f => f.trim()) .filter(Boolean); return { packageName, features }; diff --git a/packages/coding-agent/src/extensibility/skills.ts b/packages/coding-agent/src/extensibility/skills.ts index 12eb72ad5..8951bf46c 100644 --- a/packages/coding-agent/src/extensibility/skills.ts +++ b/packages/coding-agent/src/extensibility/skills.ts @@ -224,17 +224,17 @@ export async function loadSkills(options: LoadSkillsOptions = {}): Promise new Bun.Glob(pattern).match(name)); + return includeSkills.some(pattern => new Bun.Glob(pattern).match(name)); } // Check if skill name matches any of the ignore patterns function matchesIgnorePatterns(name: string): boolean { if (ignoredSkills.length === 0) return false; - return ignoredSkills.some((pattern) => new Bun.Glob(pattern).match(name)); + return ignoredSkills.some(pattern => new Bun.Glob(pattern).match(name)); } // Filter skills by source and patterns first - const filteredSkills = result.items.filter((capSkill) => { + const filteredSkills = result.items.filter(capSkill => { if (!isSourceEnabled(capSkill._source)) return false; if (matchesIgnorePatterns(capSkill.name)) return false; if (!matchesIncludePatterns(capSkill.name)) return false; @@ -243,7 +243,7 @@ export async function loadSkills(options: LoadSkillsOptions = {}): Promise { + filteredSkills.map(async capSkill => { try { return await fs.realpath(capSkill.path); } catch { @@ -285,7 +285,7 @@ export async function loadSkills(options: LoadSkillsOptions = {}): Promise = []; - const customScanResults = await Promise.all(customDirectories.map((dir) => scanDirectoryForSkills(dir))); + const customScanResults = await Promise.all(customDirectories.map(dir => scanDirectoryForSkills(dir))); for (const customSkills of customScanResults) { for (const s of customSkills.skills) { if (matchesIgnorePatterns(s.name)) continue; @@ -335,6 +335,6 @@ export async function loadSkills(options: LoadSkillsOptions = {}): Promise ({ skillPath: "", message: w })), ...collisionWarnings], + warnings: [...result.warnings.map(w => ({ skillPath: "", message: w })), ...collisionWarnings], }; } diff --git a/packages/coding-agent/src/extensibility/slash-commands.ts b/packages/coding-agent/src/extensibility/slash-commands.ts index d21b401a5..cd91ada58 100644 --- a/packages/coding-agent/src/extensibility/slash-commands.ts +++ b/packages/coding-agent/src/extensibility/slash-commands.ts @@ -29,7 +29,7 @@ function parseCommandTemplate( // Get description from frontmatter or first non-empty line let description = frontmatterDesc; if (!description) { - const firstLine = body.split("\n").find((line) => line.trim()); + const firstLine = body.split("\n").find(line => line.trim()); if (firstLine) { description = firstLine.slice(0, 60); if (firstLine.length > 60) description += "..."; @@ -114,7 +114,7 @@ export interface LoadSlashCommandsOptions { export async function loadSlashCommands(options: LoadSlashCommandsOptions = {}): Promise { const result = await loadCapability(slashCommandCapability.id, { cwd: options.cwd }); - const fileCommands: FileSlashCommand[] = result.items.map((cmd) => { + const fileCommands: FileSlashCommand[] = result.items.map(cmd => { const { description, body } = parseCommandTemplate(cmd.content, { source: cmd.path ?? `slash-command:${cmd.name}`, level: cmd.level === "native" ? "fatal" : "warn", @@ -133,7 +133,7 @@ export async function loadSlashCommands(options: LoadSlashCommandsOptions = {}): }; }); - const seenNames = new Set(fileCommands.map((cmd) => cmd.name)); + const seenNames = new Set(fileCommands.map(cmd => cmd.name)); for (const cmd of EMBEDDED_SLASH_COMMANDS) { const name = cmd.name.replace(/\.md$/, ""); if (seenNames.has(name)) continue; @@ -165,7 +165,7 @@ export function expandSlashCommand(text: string, fileCommands: FileSlashCommand[ const commandName = spaceIndex === -1 ? text.slice(1) : text.slice(1, spaceIndex); const argsString = spaceIndex === -1 ? "" : text.slice(spaceIndex + 1); - const fileCommand = fileCommands.find((cmd) => cmd.name === commandName); + const fileCommand = fileCommands.find(cmd => cmd.name === commandName); if (fileCommand) { const args = parseCommandArgs(argsString); const argsText = args.join(" "); diff --git a/packages/coding-agent/src/internal-urls/agent-protocol.ts b/packages/coding-agent/src/internal-urls/agent-protocol.ts index 9490e655b..8875b1757 100644 --- a/packages/coding-agent/src/internal-urls/agent-protocol.ts +++ b/packages/coding-agent/src/internal-urls/agent-protocol.ts @@ -8,7 +8,6 @@ * - agent:/// - JSON extraction via path form * - agent://?q= - JSON extraction via query form */ - import * as fs from "node:fs/promises"; import * as path from "node:path"; import { isEnoent } from "@oh-my-pi/pi-utils"; @@ -29,7 +28,7 @@ export interface AgentProtocolOptions { async function listAvailableOutputs(artifactsDir: string): Promise { try { const files = await fs.readdir(artifactsDir); - return files.filter((f) => f.endsWith(".md")).map((f) => f.replace(".md", "")); + return files.filter(f => f.endsWith(".md")).map(f => f.replace(".md", "")); } catch { return []; } diff --git a/packages/coding-agent/src/internal-urls/artifact-protocol.ts b/packages/coding-agent/src/internal-urls/artifact-protocol.ts index ec8cbfaa2..7878d0325 100644 --- a/packages/coding-agent/src/internal-urls/artifact-protocol.ts +++ b/packages/coding-agent/src/internal-urls/artifact-protocol.ts @@ -9,7 +9,6 @@ * * Pagination is handled by the read tool via offset/limit parameters. */ - import * as fs from "node:fs/promises"; import * as path from "node:path"; import { isEnoent } from "@oh-my-pi/pi-utils"; @@ -29,8 +28,8 @@ async function listAvailableArtifacts(artifactsDir: string): Promise { try { const files = await fs.readdir(artifactsDir); return files - .filter((f) => /^\d+\./.test(f)) - .map((f) => f.split(".")[0]) + .filter(f => /^\d+\./.test(f)) + .map(f => f.split(".")[0]) .sort((a, b) => Number(a) - Number(b)); } catch { return []; @@ -76,7 +75,7 @@ export class ArtifactProtocolHandler implements ProtocolHandler { throw err; } - const match = files.find((f) => f.startsWith(`${id}.`)); + const match = files.find(f => f.startsWith(`${id}.`)); if (!match) { const available = await listAvailableArtifacts(artifactsDir); diff --git a/packages/coding-agent/src/internal-urls/router.ts b/packages/coding-agent/src/internal-urls/router.ts index 5ec345264..3a0beb65c 100644 --- a/packages/coding-agent/src/internal-urls/router.ts +++ b/packages/coding-agent/src/internal-urls/router.ts @@ -1,7 +1,6 @@ /** * Internal URL router for resolving agent:// and skill:// URLs. */ - import type { InternalResource, InternalUrl, ProtocolHandler } from "./types"; /** @@ -59,7 +58,7 @@ export class InternalUrlRouter { if (!handler) { const available = Array.from(this.handlers.keys()) - .map((s) => `${s}://`) + .map(s => `${s}://`) .join(", "); throw new Error(`Unknown protocol: ${scheme}://\nSupported: ${available || "none"}`); } diff --git a/packages/coding-agent/src/internal-urls/rule-protocol.ts b/packages/coding-agent/src/internal-urls/rule-protocol.ts index 76bb7130b..d5629837a 100644 --- a/packages/coding-agent/src/internal-urls/rule-protocol.ts +++ b/packages/coding-agent/src/internal-urls/rule-protocol.ts @@ -6,7 +6,6 @@ * URL forms: * - rule:// - Reads rule content */ - import type { Rule } from "../capability/rule"; import type { InternalResource, InternalUrl, ProtocolHandler } from "./types"; @@ -37,9 +36,9 @@ export class RuleProtocolHandler implements ProtocolHandler { } // Find the rule - const rule = rules.find((r) => r.name === ruleName); + const rule = rules.find(r => r.name === ruleName); if (!rule) { - const available = rules.map((r) => r.name); + const available = rules.map(r => r.name); const availableStr = available.length > 0 ? available.join(", ") : "none"; throw new Error(`Unknown rule: ${ruleName}\nAvailable: ${availableStr}`); } diff --git a/packages/coding-agent/src/internal-urls/skill-protocol.ts b/packages/coding-agent/src/internal-urls/skill-protocol.ts index b9aa88a3d..793f5b29c 100644 --- a/packages/coding-agent/src/internal-urls/skill-protocol.ts +++ b/packages/coding-agent/src/internal-urls/skill-protocol.ts @@ -7,7 +7,6 @@ * - skill:// - Reads SKILL.md * - skill:/// - Reads relative path within skill's baseDir */ - import * as path from "node:path"; import type { Skill } from "../extensibility/skills"; import type { InternalResource, InternalUrl, ProtocolHandler } from "./types"; @@ -62,9 +61,9 @@ export class SkillProtocolHandler implements ProtocolHandler { } // Find the skill - const skill = skills.find((s) => s.name === skillName); + const skill = skills.find(s => s.name === skillName); if (!skill) { - const available = skills.map((s) => s.name); + const available = skills.map(s => s.name); const availableStr = available.length > 0 ? available.join(", ") : "none"; throw new Error(`Unknown skill: ${skillName}\nAvailable: ${availableStr}`); } diff --git a/packages/coding-agent/src/ipy/executor.ts b/packages/coding-agent/src/ipy/executor.ts index 3762ab78c..c5c248c7b 100644 --- a/packages/coding-agent/src/ipy/executor.ts +++ b/packages/coding-agent/src/ipy/executor.ts @@ -113,7 +113,7 @@ async function cleanupIdleSessions(): Promise { if (toDispose.length > 0) { logger.debug("Cleaning up idle kernel sessions", { count: toDispose.length }); - await Promise.allSettled(toDispose.map((session) => disposeKernelSession(session))); + await Promise.allSettled(toDispose.map(session => disposeKernelSession(session))); } if (kernelSessions.size === 0) { @@ -137,7 +137,7 @@ async function evictOldestSession(): Promise { export async function disposeAllKernelSessions(): Promise { stopCleanupTimer(); const sessions = Array.from(kernelSessions.values()); - await Promise.allSettled(sessions.map((session) => disposeKernelSession(session))); + await Promise.allSettled(sessions.map(session => disposeKernelSession(session))); } async function ensureKernelAvailable(cwd: string): Promise { @@ -169,7 +169,7 @@ export async function warmPythonEnvironment( const docs = await withKernelSession( resolvedSessionId, cwd, - async (kernel) => kernel.introspectPrelude(), + async kernel => kernel.introspectPrelude(), useSharedGateway, sessionFile, ); @@ -366,8 +366,8 @@ async function executeWithKernel( const result = await kernel.execute(code, { signal: options?.signal, timeoutMs: options?.timeoutMs, - onChunk: (text) => sink.push(text), - onDisplay: (output) => void displayOutputs.push(output), + onChunk: text => sink.push(text), + onDisplay: output => void displayOutputs.push(output), }); if (result.cancelled) { @@ -451,7 +451,7 @@ export async function executePython(code: string, options?: PythonExecutorOption return await withKernelSession( sessionId, cwd, - async (kernel) => executeWithKernel(kernel, code, options), + async kernel => executeWithKernel(kernel, code, options), useSharedGateway, sessionFile, artifactsDir, diff --git a/packages/coding-agent/src/ipy/gateway-coordinator.ts b/packages/coding-agent/src/ipy/gateway-coordinator.ts index 7d3b7bd58..21cf837a2 100644 --- a/packages/coding-agent/src/ipy/gateway-coordinator.ts +++ b/packages/coding-agent/src/ipy/gateway-coordinator.ts @@ -3,7 +3,6 @@ import { createServer } from "node:net"; import * as path from "node:path"; import { isEnoent, logger } from "@oh-my-pi/pi-utils"; import type { Subprocess } from "bun"; - import { getAgentDir } from "../config"; import { getShellConfig, killProcessTree } from "../utils/shell"; import { getOrCreateSnapshot } from "../utils/shell-snapshot"; @@ -105,13 +104,13 @@ const CASE_INSENSITIVE_ENV = process.platform === "win32"; const ACTIVE_ENV_ALLOWLIST = CASE_INSENSITIVE_ENV ? WINDOWS_ENV_ALLOWLIST : DEFAULT_ENV_ALLOWLIST; const NORMALIZED_ALLOWLIST = new Map( - Array.from(ACTIVE_ENV_ALLOWLIST, (key) => [CASE_INSENSITIVE_ENV ? key.toUpperCase() : key, key] as const), + Array.from(ACTIVE_ENV_ALLOWLIST, key => [CASE_INSENSITIVE_ENV ? key.toUpperCase() : key, key] as const), ); const NORMALIZED_DENYLIST = new Set( - Array.from(DEFAULT_ENV_DENYLIST, (key) => (CASE_INSENSITIVE_ENV ? key.toUpperCase() : key)), + Array.from(DEFAULT_ENV_DENYLIST, key => (CASE_INSENSITIVE_ENV ? key.toUpperCase() : key)), ); const NORMALIZED_ALLOW_PREFIXES = CASE_INSENSITIVE_ENV - ? DEFAULT_ENV_ALLOW_PREFIXES.map((prefix) => prefix.toUpperCase()) + ? DEFAULT_ENV_ALLOW_PREFIXES.map(prefix => prefix.toUpperCase()) : DEFAULT_ENV_ALLOW_PREFIXES; function normalizeEnvKey(key: string): string { @@ -120,7 +119,7 @@ function normalizeEnvKey(key: string): string { function resolvePathKey(env: Record): string { if (!CASE_INSENSITIVE_ENV) return "PATH"; - const match = Object.keys(env).find((candidate) => candidate.toLowerCase() === "path"); + const match = Object.keys(env).find(candidate => candidate.toLowerCase() === "path"); return match ?? "PATH"; } @@ -157,7 +156,7 @@ function filterEnv(env: Record): Record normalizedKey.startsWith(prefix))) { + if (NORMALIZED_ALLOW_PREFIXES.some(prefix => normalizedKey.startsWith(prefix))) { filtered[key] = value; } } diff --git a/packages/coding-agent/src/ipy/kernel.ts b/packages/coding-agent/src/ipy/kernel.ts index 4c77ba09c..9f18a3b49 100644 --- a/packages/coding-agent/src/ipy/kernel.ts +++ b/packages/coding-agent/src/ipy/kernel.ts @@ -132,13 +132,13 @@ const BASE_ENV_ALLOWLIST = CASE_INSENSITIVE_ENV ? new Set([...DEFAULT_ENV_ALLOWLIST, ...WINDOWS_ENV_ALLOWLIST]) : DEFAULT_ENV_ALLOWLIST; const NORMALIZED_ALLOWLIST = new Set( - Array.from(BASE_ENV_ALLOWLIST, (key) => (CASE_INSENSITIVE_ENV ? key.toUpperCase() : key)), + Array.from(BASE_ENV_ALLOWLIST, key => (CASE_INSENSITIVE_ENV ? key.toUpperCase() : key)), ); const NORMALIZED_DENYLIST = new Set( - Array.from(DEFAULT_ENV_DENYLIST, (key) => (CASE_INSENSITIVE_ENV ? key.toUpperCase() : key)), + Array.from(DEFAULT_ENV_DENYLIST, key => (CASE_INSENSITIVE_ENV ? key.toUpperCase() : key)), ); const NORMALIZED_ALLOW_PREFIXES = CASE_INSENSITIVE_ENV - ? DEFAULT_ENV_ALLOW_PREFIXES.map((prefix) => prefix.toUpperCase()) + ? DEFAULT_ENV_ALLOW_PREFIXES.map(prefix => prefix.toUpperCase()) : DEFAULT_ENV_ALLOW_PREFIXES; function normalizeEnvKey(key: string): string { @@ -147,7 +147,7 @@ function normalizeEnvKey(key: string): string { function resolvePathKey(env: Record): string { if (!CASE_INSENSITIVE_ENV) return "PATH"; - const match = Object.keys(env).find((candidate) => candidate.toLowerCase() === "path"); + const match = Object.keys(env).find(candidate => candidate.toLowerCase() === "path"); return match ?? "PATH"; } @@ -231,7 +231,7 @@ function filterEnv(env: Record): Record normalizedKey.startsWith(prefix))) { + if (NORMALIZED_ALLOW_PREFIXES.some(prefix => normalizedKey.startsWith(prefix))) { filtered[key] = value; } } @@ -762,7 +762,7 @@ export class PythonKernel { resolve(); }; - ws.onerror = (event) => { + ws.onerror = event => { const error = new Error(`WebSocket error: ${event}`); if (!settled) { settled = true; @@ -787,7 +787,7 @@ export class PythonKernel { this.abortPendingExecutions("WebSocket closed"); }; - ws.onmessage = (event) => { + ws.onmessage = event => { let msg: JupyterMessage | null = null; if (event.data instanceof ArrayBuffer) { msg = deserializeWebSocketMessage(event.data); @@ -958,7 +958,7 @@ export class PythonKernel { return promise; } - this.#messageHandlers.set(msgId, async (response) => { + this.#messageHandlers.set(msgId, async response => { switch (response.header.msg_type) { case "execute_reply": { replyReceived = true; @@ -1053,7 +1053,7 @@ export class PythonKernel { const result = await this.execute(PRELUDE_INTROSPECTION_SNIPPET, { silent: false, storeHistory: false, - onChunk: (text) => { + onChunk: text => { output += text; }, }); diff --git a/packages/coding-agent/src/ipy/modules.ts b/packages/coding-agent/src/ipy/modules.ts index 1da9d1186..de7d1ab1f 100644 --- a/packages/coding-agent/src/ipy/modules.ts +++ b/packages/coding-agent/src/ipy/modules.ts @@ -40,8 +40,8 @@ async function listModuleCandidates(dir: string, source: PythonModuleSource): Pr try { const entries = await fs.readdir(dir, { withFileTypes: true }); return entries - .filter((entry) => entry.isFile() && entry.name.endsWith(".py")) - .map((entry) => ({ + .filter(entry => entry.isFile() && entry.name.endsWith(".py")) + .map(entry => ({ name: entry.name, path: path.resolve(dir, entry.name), source, @@ -71,8 +71,8 @@ export async function discoverPythonModules(options: DiscoverPythonModulesOption const userDirs = [path.join(homeDir, ".omp", "agent", "modules"), path.join(homeDir, ".pi", "agent", "modules")]; const projectDirs = [path.resolve(cwd, ".omp", "modules"), path.resolve(cwd, ".pi", "modules")]; - const userCandidates = (await Promise.all(userDirs.map((dir) => listModuleCandidates(dir, "user")))).flat(); - const projectCandidates = (await Promise.all(projectDirs.map((dir) => listModuleCandidates(dir, "project")))).flat(); + const userCandidates = (await Promise.all(userDirs.map(dir => listModuleCandidates(dir, "user")))).flat(); + const projectCandidates = (await Promise.all(projectDirs.map(dir => listModuleCandidates(dir, "project")))).flat(); const byName = new Map(); for (const candidate of userCandidates) { @@ -88,7 +88,7 @@ export async function discoverPythonModules(options: DiscoverPythonModulesOption } const sorted = Array.from(byName.values()).sort((a, b) => a.name.localeCompare(b.name)); - return Promise.all(sorted.map((candidate) => readModuleContent(candidate))); + return Promise.all(sorted.map(candidate => readModuleContent(candidate))); } /** diff --git a/packages/coding-agent/src/lsp/client.ts b/packages/coding-agent/src/lsp/client.ts index 7ececeff0..e36352010 100644 --- a/packages/coding-agent/src/lsp/client.ts +++ b/packages/coding-agent/src/lsp/client.ts @@ -297,7 +297,7 @@ async function handleConfigurationRequest(client: LspClient, message: LspJsonRpc if (typeof message.id !== "number") return; const params = message.params as { items?: Array<{ section?: string }> }; const items = params?.items ?? []; - const result = items.map((item) => { + const result = items.map(item => { const section = item.section ?? ""; return client.config.settings?.[section] ?? {}; }); @@ -759,12 +759,12 @@ export async function sendRequest( // Register pending request with timeout wrapper client.pendingRequests.set(id, { - resolve: (result) => { + resolve: result => { if (timeout) clearTimeout(timeout); cleanup(); resolve(result); }, - reject: (err) => { + reject: err => { if (timeout) clearTimeout(timeout); cleanup(); reject(err); @@ -773,7 +773,7 @@ export async function sendRequest( }); // Write request - writeMessage(client.process.stdin as import("bun").FileSink, request).catch((err) => { + writeMessage(client.process.stdin as import("bun").FileSink, request).catch(err => { if (timeout) clearTimeout(timeout); client.pendingRequests.delete(id); cleanup(); @@ -827,7 +827,7 @@ export interface LspServerStatus { * Get status of all active LSP clients. */ export function getActiveClients(): LspServerStatus[] { - return Array.from(clients.values()).map((client) => ({ + return Array.from(clients.values()).map(client => ({ name: client.config.command, status: "ready" as const, fileTypes: client.config.fileTypes, diff --git a/packages/coding-agent/src/lsp/clients/biome-client.ts b/packages/coding-agent/src/lsp/clients/biome-client.ts index 549017901..2fa31c1bb 100644 --- a/packages/coding-agent/src/lsp/clients/biome-client.ts +++ b/packages/coding-agent/src/lsp/clients/biome-client.ts @@ -2,7 +2,6 @@ * Biome CLI-based linter client. * Uses Biome's CLI with JSON output instead of LSP (which has stale diagnostics issues). */ - import path from "node:path"; import type { Diagnostic, DiagnosticSeverity, LinterClient, ServerConfig } from "../../lsp/types"; diff --git a/packages/coding-agent/src/lsp/clients/index.ts b/packages/coding-agent/src/lsp/clients/index.ts index bc1bfce3e..77cf16d64 100644 --- a/packages/coding-agent/src/lsp/clients/index.ts +++ b/packages/coding-agent/src/lsp/clients/index.ts @@ -1,3 +1,6 @@ +import type { LinterClient, ServerConfig } from "../../lsp/types"; +import { LspLinterClient } from "./lsp-linter-client"; + /** * Linter client implementations. * @@ -8,9 +11,6 @@ export { BiomeClient } from "./biome-client"; export { LspLinterClient } from "./lsp-linter-client"; -import type { LinterClient, ServerConfig } from "../../lsp/types"; -import { LspLinterClient } from "./lsp-linter-client"; - // Cache of linter clients by server name + cwd const clientCache = new Map(); diff --git a/packages/coding-agent/src/lsp/clients/lsp-linter-client.ts b/packages/coding-agent/src/lsp/clients/lsp-linter-client.ts index 3ff5b7fb3..ddd4b7550 100644 --- a/packages/coding-agent/src/lsp/clients/lsp-linter-client.ts +++ b/packages/coding-agent/src/lsp/clients/lsp-linter-client.ts @@ -2,7 +2,6 @@ * LSP-based linter client. * Uses the Language Server Protocol for formatting and diagnostics. */ - import { getOrCreateClient, notifySaved, sendRequest, syncContent } from "../../lsp/client"; import { applyTextEditsToString } from "../../lsp/edits"; import type { Diagnostic, LinterClient, LspClient, ServerConfig, TextEdit } from "../../lsp/types"; diff --git a/packages/coding-agent/src/lsp/config.ts b/packages/coding-agent/src/lsp/config.ts index 3aafdd94e..4a40625e8 100644 --- a/packages/coding-agent/src/lsp/config.ts +++ b/packages/coding-agent/src/lsp/config.ts @@ -141,7 +141,7 @@ function applyRuntimeDefaults(servers: Record): Record (arg === PID_TOKEN ? String(process.pid) : arg)); + const args = updated.omnisharp.args.map(arg => (arg === PID_TOKEN ? String(process.pid) : arg)); updated.omnisharp = { ...updated.omnisharp, args }; } @@ -357,7 +357,7 @@ export function getServersForFile(config: LspConfig, filePath: string): Array<[s const matches: Array<[string, ServerConfig]> = []; for (const [name, serverConfig] of Object.entries(config.servers)) { - const supportsFile = serverConfig.fileTypes.some((fileType) => { + const supportsFile = serverConfig.fileTypes.some(fileType => { const normalized = fileType.toLowerCase(); return normalized === ext || normalized === fileName; }); diff --git a/packages/coding-agent/src/lsp/index.ts b/packages/coding-agent/src/lsp/index.ts index 08ce47147..419b9720a 100644 --- a/packages/coding-agent/src/lsp/index.ts +++ b/packages/coding-agent/src/lsp/index.ts @@ -253,7 +253,7 @@ function limitDiagnosticMessages(messages: string[]): string[] { } function findFileByExtensions(baseDir: string, extensions: string[], maxDepth: number): string | null { - const normalized = extensions.map((ext) => ext.toLowerCase()); + const normalized = extensions.map(ext => ext.toLowerCase()); const search = (dir: string, depth: number): string | null => { if (depth > maxDepth) return null; const entries: fs.Dirent[] = []; @@ -280,7 +280,7 @@ function findFileByExtensions(baseDir: string, extensions: string[], maxDepth: n if (entry.isFile()) { const lowerName = entry.name.toLowerCase(); - if (normalized.some((ext) => lowerName.endsWith(ext))) { + if (normalized.some(ext => lowerName.endsWith(ext))) { return fullPath; } } else if (entry.isDirectory()) { @@ -411,10 +411,10 @@ async function runWorkspaceDiagnostics( return { output: "No issues found", projectType }; } - const summary = formatDiagnosticsSummary(collected.map((d) => d.diagnostic)); - const formatted = collected.slice(0, 50).map((d) => formatDiagnostic(d.diagnostic, d.filePath)); + const summary = formatDiagnosticsSummary(collected.map(d => d.diagnostic)); + const formatted = collected.slice(0, 50).map(d => formatDiagnostic(d.diagnostic, d.filePath)); const more = collected.length > 50 ? `\n ... and ${collected.length - 50} more` : ""; - return { output: `${summary}:\n${formatted.map((f) => ` ${f}`).join("\n")}${more}`, projectType }; + return { output: `${summary}:\n${formatted.map(f => ` ${f}`).join("\n")}${more}`, projectType }; } catch (err) { logger.debug("LSP diagnostics failed, falling back to shell", { error: String(err) }); // Fall through to shell command @@ -570,10 +570,10 @@ async function getDiagnosticsForFile( } } - const formatted = uniqueDiagnostics.map((d) => formatDiagnostic(d, relPath)); + const formatted = uniqueDiagnostics.map(d => formatDiagnostic(d, relPath)); const limited = limitDiagnosticMessages(formatted); const summary = formatDiagnosticsSummary(uniqueDiagnostics); - const hasErrors = uniqueDiagnostics.some((d) => d.severity === 1); + const hasErrors = uniqueDiagnostics.some(d => d.severity === 1); return { server: serverNames.join(", "), @@ -1095,8 +1095,8 @@ export class LspTool implements AgentTool formatDiagnostic(d, relPath)); - const output = `${summary}:\n${formatted.map((f) => ` ${f}`).join("\n")}`; + const formatted = uniqueDiagnostics.map(d => formatDiagnostic(d, relPath)); + const output = `${summary}:\n${formatted.map(f => ` ${f}`).join("\n")}`; return { content: [{ type: "text", text: output }], details: { action, serverName: Array.from(allServerNames).join(", "), success: true }, @@ -1186,7 +1186,7 @@ export class LspTool implements AgentTool { + const locations = raw.flatMap(loc => { if ("uri" in loc) { return [loc as Location]; } @@ -1202,7 +1202,7 @@ export class LspTool implements AgentTool ` ${formatLocation(loc, this.session.cwd)}`) + .map(loc => ` ${formatLocation(loc, this.session.cwd)}`) .join("\n")}`; } } @@ -1219,7 +1219,7 @@ export class LspTool implements AgentTool ` ${formatLocation(loc, this.session.cwd)}`); + const lines = result.map(loc => ` ${formatLocation(loc, this.session.cwd)}`); output = `Found ${result.length} reference(s):\n${lines.join("\n")}`; } break; @@ -1256,11 +1256,11 @@ export class LspTool implements AgentTool formatDocumentSymbol(s)); + const lines = (result as DocumentSymbol[]).flatMap(s => formatDocumentSymbol(s)); output = `Symbols in ${relPath}:\n${lines.join("\n")}`; } else { // Flat - const lines = (result as SymbolInformation[]).map((s) => { + const lines = (result as SymbolInformation[]).map(s => { const line = s.location.range.start.line + 1; const icon = symbolKindToIcon(s.kind); return `${icon} ${s.name} @ line ${line}`; @@ -1284,8 +1284,8 @@ export class LspTool implements AgentTool formatSymbolInformation(s, this.session.cwd)); - output = `Found ${result.length} symbol(s) matching "${query}":\n${lines.map((l) => ` ${l}`).join("\n")}`; + const lines = result.map(s => formatSymbolInformation(s, this.session.cwd)); + output = `Found ${result.length} symbol(s) matching "${query}":\n${lines.map(l => ` ${l}`).join("\n")}`; } break; } @@ -1310,10 +1310,10 @@ export class LspTool implements AgentTool ` ${a}`).join("\n")}`; + output = `Applied rename:\n${applied.map(a => ` ${a}`).join("\n")}`; } else { const preview = formatWorkspaceEdit(result, this.session.cwd); - output = `Rename preview:\n${preview.map((p) => ` ${p}`).join("\n")}`; + output = `Rename preview:\n${preview.map(p => ` ${p}`).join("\n")}`; } } break; @@ -1334,7 +1334,7 @@ export class LspTool implements AgentTool d.range.start.line <= range.end.line && d.range.end.line >= range.start.line, + d => d.range.start.line <= range.end.line && d.range.end.line >= range.start.line, ); const codeActionContext: { diagnostics: Diagnostic[]; only?: string[] } = { @@ -1400,7 +1400,7 @@ export class LspTool implements AgentTool ` ${a}`).join("\n")}`; + output = `Applied "${codeAction.title}":\n${applied.map(a => ` ${a}`).join("\n")}`; } else { const commandPayload = getCommandPayload(resolvedAction); if (commandPayload) { @@ -1449,7 +1449,7 @@ export class LspTool implements AgentTool { + const lines = calls.map(call => { const loc = { uri: call.from.uri, range: call.from.selectionRange }; const detail = call.from.detail ? ` (${call.from.detail})` : ""; return ` ${call.from.name}${detail} @ ${formatLocation(loc, this.session.cwd)}`; @@ -1464,7 +1464,7 @@ export class LspTool implements AgentTool { + const lines = calls.map(call => { const loc = { uri: call.to.uri, range: call.to.selectionRange }; const detail = call.to.detail ? ` (${call.to.detail})` : ""; return ` ${call.to.name}${detail} @ ${formatLocation(loc, this.session.cwd)}`; @@ -1499,10 +1499,10 @@ export class LspTool implements AgentTool d.diagnostic)); - const formatted = collected.slice(0, 20).map((d) => formatDiagnostic(d.diagnostic, d.filePath)); + const summary = formatDiagnosticsSummary(collected.map(d => d.diagnostic)); + const formatted = collected.slice(0, 20).map(d => formatDiagnostic(d.diagnostic, d.filePath)); const more = collected.length > 20 ? `\n ... and ${collected.length - 20} more` : ""; - output = `Flycheck ${summary}:\n${formatted.map((f) => ` ${f}`).join("\n")}${more}`; + output = `Flycheck ${summary}:\n${formatted.map(f => ` ${f}`).join("\n")}${more}`; } break; } @@ -1560,13 +1560,13 @@ export class LspTool implements AgentTool 0 - ? `Applied SSR:\n${applied.map((a) => ` ${a}`).join("\n")}` + ? `Applied SSR:\n${applied.map(a => ` ${a}`).join("\n")}` : "SSR: no matches found"; } else { const preview = formatWorkspaceEdit(result, this.session.cwd); output = preview.length > 0 - ? `SSR preview:\n${preview.map((p) => ` ${p}`).join("\n")}` + ? `SSR preview:\n${preview.map(p => ` ${p}`).join("\n")}` : "SSR: no matches found"; } break; @@ -1591,7 +1591,7 @@ export class LspTool implements AgentTool { + const lines = result.map(r => { const args = r.args?.cargoArgs?.join(" ") || ""; return ` [${r.kind}] ${r.label}${args ? ` (cargo ${args})` : ""}`; }); @@ -1619,7 +1619,7 @@ export class LspTool implements AgentTool ` ${t}`).join("\n")}`; + output = `Found ${result.length} related test(s):\n${result.map(t => ` ${t}`).join("\n")}`; } break; } diff --git a/packages/coding-agent/src/lsp/lspmux.ts b/packages/coding-agent/src/lsp/lspmux.ts index 336c1f342..8b629b613 100644 --- a/packages/coding-agent/src/lsp/lspmux.ts +++ b/packages/coding-agent/src/lsp/lspmux.ts @@ -108,7 +108,7 @@ async function checkServerRunning(binaryPath: string): Promise { const exited = await Promise.race([ proc.exited, - new Promise((resolve) => setTimeout(() => resolve(null), LIVENESS_TIMEOUT_MS)), + new Promise(resolve => setTimeout(() => resolve(null), LIVENESS_TIMEOUT_MS)), ]); if (exited === null) { diff --git a/packages/coding-agent/src/lsp/render.ts b/packages/coding-agent/src/lsp/render.ts index 6b8ce1cc4..e9a78d54f 100644 --- a/packages/coding-agent/src/lsp/render.ts +++ b/packages/coding-agent/src/lsp/render.ts @@ -7,7 +7,6 @@ * - Grouped references and symbols * - Collapsible/expandable views */ - import type { AgentToolResult, RenderResultOptions } from "@oh-my-pi/pi-agent-core"; import { type Component, Text } from "@oh-my-pi/pi-tui"; import { highlight, supportsLanguage } from "cli-highlight"; @@ -260,11 +259,11 @@ function renderDiagnostics( if (warnCount > 0) meta.push(`${warnCount} warning${warnCount !== 1 ? "s" : ""}`); if (meta.length === 0) meta.push("No issues"); - const diagLines = lines.filter((l) => l.includes(theme.status.error) || /:\d+:\d+/.test(l)); + const diagLines = lines.filter(l => l.includes(theme.status.error) || /:\d+:\d+/.test(l)); const parsedDiagnostics = diagLines - .map((line) => parseDiagnosticLine(line)) + .map(line => parseDiagnosticLine(line)) .filter((diag): diag is ParsedDiagnostic => diag !== null); - const fallbackDiagnostics: RawDiagnostic[] = diagLines.map((line) => ({ raw: line.trim() })); + const fallbackDiagnostics: RawDiagnostic[] = diagLines.map(line => ({ raw: line.trim() })); if (expanded) { let output = `${icon} ${theme.fg("dim", meta.join(theme.sep.dot))}`; @@ -338,7 +337,7 @@ function renderReferences(refMatch: RegExpMatchArray, lines: string[], expanded: const icon = refCount > 0 ? theme.styledSymbol("status.success", "success") : theme.styledSymbol("status.warning", "warning"); - const locLines = lines.filter((l) => /^\s*\S+:\d+:\d+/.test(l)); + const locLines = lines.filter(l => /^\s*\S+:\d+:\d+/.test(l)); // Group by file const byFile = new Map>(); @@ -431,7 +430,7 @@ function renderSymbols(symbolsMatch: RegExpMatchArray, lines: string[], expanded icon: string; } - const symbolLines = lines.filter((l) => l.includes("@") && l.includes("line")); + const symbolLines = lines.filter(l => l.includes("@") && l.includes("line")); const symbols: SymbolInfo[] = []; for (const line of symbolLines) { @@ -474,7 +473,7 @@ function renderSymbols(symbolsMatch: RegExpMatchArray, lines: string[], expanded return prefix; }; - const topLevelCount = symbols.filter((s) => s.indent === 0).length; + const topLevelCount = symbols.filter(s => s.indent === 0).length; if (expanded) { let output = `${icon} ${theme.fg("dim", `in ${fileName}`)}`; @@ -492,7 +491,7 @@ function renderSymbols(symbolsMatch: RegExpMatchArray, lines: string[], expanded } // Collapsed: show first 3 top-level symbols - const topLevel = symbols.filter((s) => s.indent === 0).slice(0, 3); + const topLevel = symbols.filter(s => s.indent === 0).slice(0, 3); const hasMoreSymbols = symbols.length > topLevel.length; const expandHint = formatExpandHint(theme, expanded, hasMoreSymbols); let output = `${icon} ${theme.fg("dim", `in ${fileName}`)}${expandHint}`; diff --git a/packages/coding-agent/src/lsp/utils.ts b/packages/coding-agent/src/lsp/utils.ts index 6e45c2ff0..3ba1cca26 100644 --- a/packages/coding-agent/src/lsp/utils.ts +++ b/packages/coding-agent/src/lsp/utils.ts @@ -232,7 +232,7 @@ export function severityToIcon(severity?: DiagnosticSeverity): string { function stripDiagnosticNoise(message: string): string { return message .split("\n") - .filter((line) => { + .filter(line => { const trimmed = line.trim(); // Skip "for further information visit " lines if (trimmed.startsWith("for further information visit")) return false; @@ -491,7 +491,7 @@ export function extractHoverText( } if (Array.isArray(contents)) { - return contents.map((c) => extractHoverText(c as string | { kind: string; value: string })).join("\n\n"); + return contents.map(c => extractHoverText(c as string | { kind: string; value: string })).join("\n\n"); } if (typeof contents === "object" && contents !== null) { diff --git a/packages/coding-agent/src/main.ts b/packages/coding-agent/src/main.ts index 0d6be9f82..61cb4cf47 100644 --- a/packages/coding-agent/src/main.ts +++ b/packages/coding-agent/src/main.ts @@ -4,7 +4,6 @@ * This file handles CLI argument parsing and translates them into * createAgentSession() options. The SDK does the heavy lifting. */ - import * as fs from "node:fs/promises"; import * as os from "node:os"; import * as path from "node:path"; @@ -98,7 +97,7 @@ async function runInteractiveMode( await mode.init(); versionCheckPromise - .then((newVersion) => { + .then(newVersion => { if (newVersion) { mode.showNewVersionNotification(newVersion); } @@ -184,11 +183,11 @@ async function resolveSessionMatch( sessionDir?: string, ): Promise { const sessions = await SessionManager.list(cwd, sessionDir); - let matches = sessions.filter((session) => session.id.startsWith(sessionArg)); + let matches = sessions.filter(session => session.id.startsWith(sessionArg)); if (matches.length === 0 && !sessionDir) { const globalSessions = await SessionManager.listAll(); - matches = globalSessions.filter((session) => session.id.startsWith(sessionArg)); + matches = globalSessions.filter(session => session.id.startsWith(sessionArg)); } return matches[0]; @@ -220,13 +219,13 @@ async function getChangelogForDisplay(parsed: Args, settingsManager: SettingsMan if (!lastVersion) { if (entries.length > 0) { settingsManager.setLastChangelogVersion(VERSION); - return entries.map((e) => e.content).join("\n\n"); + return entries.map(e => e.content).join("\n\n"); } } else { const newEntries = getNewEntries(entries, lastVersion); if (newEntries.length > 0) { settingsManager.setLastChangelogVersion(VERSION); - return newEntries.map((e) => e.content).join("\n\n"); + return newEntries.map(e => e.content).join("\n\n"); } } @@ -392,10 +391,10 @@ async function buildSessionOptions( const parsedModel = parseModelString(remembered); const rememberedModel = parsedModel ? scopedModels.find( - (scopedModel) => + scopedModel => scopedModel.model.provider === parsedModel.provider && scopedModel.model.id === parsedModel.id, ) - : scopedModels.find((scopedModel) => scopedModel.model.id.toLowerCase() === remembered.toLowerCase()); + : scopedModels.find(scopedModel => scopedModel.model.id.toLowerCase() === remembered.toLowerCase()); if (rememberedModel) { options.model = rememberedModel.model; } @@ -420,7 +419,7 @@ async function buildSessionOptions( // Scoped models for Ctrl+P cycling - fill in default thinking levels when not explicit if (scopedModels.length > 0) { const defaultThinkingLevel = settingsManager.getDefaultThinkingLevel() ?? "off"; - options.scopedModels = scopedModels.map((scopedModel) => ({ + options.scopedModels = scopedModels.map(scopedModel => ({ model: scopedModel.model, thinkingLevel: scopedModel.explicitThinkingLevel ? (scopedModel.thinkingLevel ?? defaultThinkingLevel) @@ -437,7 +436,7 @@ async function buildSessionOptions( } else if (resolvedSystemPrompt) { options.systemPrompt = resolvedSystemPrompt; } else if (resolvedAppendPrompt) { - options.systemPrompt = (defaultPrompt) => `${defaultPrompt}\n\n${resolvedAppendPrompt}`; + options.systemPrompt = defaultPrompt => `${defaultPrompt}\n\n${resolvedAppendPrompt}`; } // Tools @@ -749,7 +748,7 @@ export async function main(args: string[]) { const scopedModelsForDisplay = sessionOptions.scopedModels ?? scopedModels; if (scopedModelsForDisplay.length > 0) { const modelList = scopedModelsForDisplay - .map((scopedModel) => { + .map(scopedModel => { const thinkingStr = scopedModel.thinkingLevel !== "off" ? `:${scopedModel.thinkingLevel}` : ""; return `${scopedModel.model.id}${thinkingStr}`; }) diff --git a/packages/coding-agent/src/mcp/client.ts b/packages/coding-agent/src/mcp/client.ts index a650f11cf..45c8279c5 100644 --- a/packages/coding-agent/src/mcp/client.ts +++ b/packages/coding-agent/src/mcp/client.ts @@ -3,7 +3,6 @@ * * Handles connection initialization, tool listing, and tool calling. */ - import { createHttpTransport } from "./transports/http"; import { createStdioTransport } from "./transports/stdio"; import type { @@ -39,11 +38,11 @@ function withTimeout(promise: Promise, ms: number, message: string): Promi const { promise: wrapped, resolve, reject } = Promise.withResolvers(); const timer = setTimeout(() => reject(new Error(message)), ms); promise.then( - (value) => { + value => { clearTimeout(timer); resolve(value); }, - (error) => { + error => { clearTimeout(timer); reject(error); }, diff --git a/packages/coding-agent/src/mcp/config.ts b/packages/coding-agent/src/mcp/config.ts index f78844879..0762fd98c 100644 --- a/packages/coding-agent/src/mcp/config.ts +++ b/packages/coding-agent/src/mcp/config.ts @@ -3,7 +3,6 @@ * * Uses the capability system to load MCP servers from multiple sources. */ - import { mcpCapability } from "../capability/mcp"; import type { MCPServer } from "../discovery"; import { loadCapability } from "../discovery"; @@ -86,7 +85,7 @@ export async function loadAllMCPConfigs(cwd: string, options?: LoadMCPConfigsOpt // Filter out project-level configs if disabled const servers = enableProjectConfig ? result.items - : result.items.filter((server) => server._source.level !== "project"); + : result.items.filter(server => server._source.level !== "project"); // Convert to legacy format and preserve source metadata const configs: Record = {}; @@ -130,7 +129,7 @@ export function isExaMCPServer(name: string, config: MCPServerConfig): boolean { // Check by args for stdio servers (e.g., mcp-remote to exa) if (!config.type || config.type === "stdio") { const stdioConfig = config as { args?: string[] }; - if (stdioConfig.args?.some((arg) => EXA_MCP_URL_PATTERN.test(arg))) { + if (stdioConfig.args?.some(arg => EXA_MCP_URL_PATTERN.test(arg))) { return true; } } diff --git a/packages/coding-agent/src/mcp/json-rpc.ts b/packages/coding-agent/src/mcp/json-rpc.ts index 4843bd71b..9174846de 100644 --- a/packages/coding-agent/src/mcp/json-rpc.ts +++ b/packages/coding-agent/src/mcp/json-rpc.ts @@ -4,7 +4,6 @@ * Lightweight utilities for calling MCP servers directly via HTTP * without maintaining persistent connections. */ - import { logger } from "@oh-my-pi/pi-utils"; /** Parse SSE response format (lines starting with "data: ") */ diff --git a/packages/coding-agent/src/mcp/loader.ts b/packages/coding-agent/src/mcp/loader.ts index de5df5e12..283e01e8c 100644 --- a/packages/coding-agent/src/mcp/loader.ts +++ b/packages/coding-agent/src/mcp/loader.ts @@ -3,7 +3,6 @@ * * Integrates MCP tool discovery with the custom tools system. */ - import { logger } from "@oh-my-pi/pi-utils"; import type { LoadedCustomTool } from "../extensibility/custom-tools/types"; import { AgentStorage } from "../session/agent-storage"; @@ -78,7 +77,7 @@ export async function discoverAndLoadMCPTools(cwd: string, options?: MCPToolsLoa } // Convert MCP tools to LoadedCustomTool format - const loadedTools: LoadedCustomTool[] = result.tools.map((tool) => { + const loadedTools: LoadedCustomTool[] = result.tools.map(tool => { // MCPTool and DeferredMCPTool have these properties const mcpTool = tool as { mcpServerName?: string }; const serverName = mcpTool.mcpServerName; diff --git a/packages/coding-agent/src/mcp/manager.ts b/packages/coding-agent/src/mcp/manager.ts index 777d78e00..3a504b9f2 100644 --- a/packages/coding-agent/src/mcp/manager.ts +++ b/packages/coding-agent/src/mcp/manager.ts @@ -4,7 +4,6 @@ * Discovers, connects to, and manages MCP servers. * Handles tool loading and lifecycle. */ - import { logger } from "@oh-my-pi/pi-utils"; import type { TSchema } from "@sinclair/typebox"; import type { CustomTool } from "../extensibility/custom-tools/types"; @@ -34,11 +33,11 @@ const STARTUP_TIMEOUT_MS = 250; function trackPromise(promise: Promise): TrackedPromise { const tracked: TrackedPromise = { promise, status: "pending" }; promise.then( - (value) => { + value => { tracked.status = "fulfilled"; tracked.value = value; }, - (reason) => { + reason => { tracked.status = "rejected"; tracked.reason = reason; }, @@ -156,7 +155,7 @@ export class MCPManager { } const connectionPromise = connectToServer(name, config).then( - (connection) => { + connection => { if (sources[name]) { connection._source = sources[name]; } @@ -166,7 +165,7 @@ export class MCPManager { } return connection; }, - (error) => { + error => { if (this.pendingConnections.get(name) === connectionPromise) { this.pendingConnections.delete(name); } @@ -175,7 +174,7 @@ export class MCPManager { ); this.pendingConnections.set(name, connectionPromise); - const toolsPromise = connectionPromise.then(async (connection) => { + const toolsPromise = connectionPromise.then(async connection => { const serverTools = await listTools(connection); return { connection, serverTools }; }); @@ -192,7 +191,7 @@ export class MCPManager { this.replaceServerTools(name, customTools); void this.toolCache?.set(name, config, serverTools); }) - .catch((error) => { + .catch(error => { if (this.pendingToolLoads.get(name) !== toolsPromise) return; this.pendingToolLoads.delete(name); if (!allowBackgroundLogging || reportedErrors.has(name)) return; @@ -203,22 +202,22 @@ export class MCPManager { // Notify about servers we're connecting to if (connectionTasks.length > 0 && onConnecting) { - onConnecting(connectionTasks.map((task) => task.name)); + onConnecting(connectionTasks.map(task => task.name)); } if (connectionTasks.length > 0) { await Promise.race([ - Promise.allSettled(connectionTasks.map((task) => task.tracked.promise)), + Promise.allSettled(connectionTasks.map(task => task.tracked.promise)), delay(STARTUP_TIMEOUT_MS), ]); const cachedTools = new Map(); - const pendingTasks = connectionTasks.filter((task) => task.tracked.status === "pending"); + const pendingTasks = connectionTasks.filter(task => task.tracked.status === "pending"); if (pendingTasks.length > 0) { if (this.toolCache) { await Promise.all( - pendingTasks.map(async (task) => { + pendingTasks.map(async task => { const cached = await this.toolCache?.get(task.name, task.config); if (cached) { cachedTools.set(task.name, cached); @@ -227,9 +226,9 @@ export class MCPManager { ); } - const pendingWithoutCache = pendingTasks.filter((task) => !cachedTools.has(task.name)); + const pendingWithoutCache = pendingTasks.filter(task => !cachedTools.has(task.name)); if (pendingWithoutCache.length > 0) { - await Promise.allSettled(pendingWithoutCache.map((task) => task.tracked.promise)); + await Promise.allSettled(pendingWithoutCache.map(task => task.tracked.promise)); } } @@ -269,7 +268,7 @@ export class MCPManager { } private replaceServerTools(name: string, tools: CustomTool[]): void { - this.tools = this.tools.filter((t) => !t.name.startsWith(`mcp_${name}_`)); + this.tools = this.tools.filter(t => !t.name.startsWith(`mcp_${name}_`)); this.tools.push(...tools); } @@ -327,14 +326,14 @@ export class MCPManager { } // Remove tools from this server - this.tools = this.tools.filter((t) => !t.name.startsWith(`mcp_${name}_`)); + this.tools = this.tools.filter(t => !t.name.startsWith(`mcp_${name}_`)); } /** * Disconnect from all servers. */ async disconnectAll(): Promise { - const promises = Array.from(this.connections.values()).map((conn) => disconnectServer(conn)); + const promises = Array.from(this.connections.values()).map(conn => disconnectServer(conn)); await Promise.allSettled(promises); this.pendingConnections.clear(); @@ -367,7 +366,7 @@ export class MCPManager { * Refresh tools from all servers. */ async refreshAllTools(): Promise { - const promises = Array.from(this.connections.keys()).map((name) => this.refreshServerTools(name)); + const promises = Array.from(this.connections.keys()).map(name => this.refreshServerTools(name)); await Promise.allSettled(promises); } } diff --git a/packages/coding-agent/src/mcp/tool-bridge.ts b/packages/coding-agent/src/mcp/tool-bridge.ts index 14d67532b..8ef4a742e 100644 --- a/packages/coding-agent/src/mcp/tool-bridge.ts +++ b/packages/coding-agent/src/mcp/tool-bridge.ts @@ -3,7 +3,6 @@ * * Converts MCP tool definitions to CustomTool format for the agent. */ - import type { AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core"; import type { TSchema } from "@sinclair/typebox"; import type { SourceMeta } from "../capability/types"; @@ -121,7 +120,7 @@ export class MCPTool implements CustomTool { /** Create MCPTool instances for all tools from an MCP server connection */ static fromTools(connection: MCPServerConnection, tools: MCPToolDefinition[]): MCPTool[] { - return tools.map((tool) => new MCPTool(connection, tool)); + return tools.map(tool => new MCPTool(connection, tool)); } constructor( @@ -205,7 +204,7 @@ export class DeferredMCPTool implements CustomTool { getConnection: () => Promise, source?: SourceMeta, ): DeferredMCPTool[] { - return tools.map((tool) => new DeferredMCPTool(serverName, tool, getConnection, source)); + return tools.map(tool => new DeferredMCPTool(serverName, tool, getConnection, source)); } constructor( diff --git a/packages/coding-agent/src/mcp/tool-cache.ts b/packages/coding-agent/src/mcp/tool-cache.ts index bc10fdb1b..f9aecd4c0 100644 --- a/packages/coding-agent/src/mcp/tool-cache.ts +++ b/packages/coding-agent/src/mcp/tool-cache.ts @@ -3,7 +3,6 @@ * * Stores tool definitions per server in agent.db for fast startup. */ - import { logger } from "@oh-my-pi/pi-utils"; import type { AgentStorage } from "../session/agent-storage"; import type { MCPServerConfig, MCPToolDefinition } from "./types"; @@ -24,7 +23,7 @@ function isRecord(value: unknown): value is Record { function stableClone(value: unknown): unknown { if (Array.isArray(value)) { - return value.map((item) => stableClone(item)); + return value.map(item => stableClone(item)); } if (isRecord(value)) { const sorted: Record = {}; diff --git a/packages/coding-agent/src/mcp/transports/http.ts b/packages/coding-agent/src/mcp/transports/http.ts index 79d9a02b3..9f0bd479b 100644 --- a/packages/coding-agent/src/mcp/transports/http.ts +++ b/packages/coding-agent/src/mcp/transports/http.ts @@ -4,7 +4,7 @@ * Implements JSON-RPC 2.0 over HTTP POST with optional SSE streaming. * Based on MCP spec 2025-03-26. */ - +import { readSseEvents } from "@oh-my-pi/pi-utils"; import type { JsonRpcMessage, JsonRpcResponse, @@ -18,8 +18,6 @@ function generateId(): string { return Math.random().toString(36).slice(2) + Date.now().toString(36); } -import { readSseEvents } from "@oh-my-pi/pi-utils"; - /** * HTTP transport for MCP servers. * Uses POST for requests, supports SSE responses. diff --git a/packages/coding-agent/src/mcp/transports/stdio.ts b/packages/coding-agent/src/mcp/transports/stdio.ts index bfbedc453..f98260875 100644 --- a/packages/coding-agent/src/mcp/transports/stdio.ts +++ b/packages/coding-agent/src/mcp/transports/stdio.ts @@ -4,7 +4,6 @@ * Implements JSON-RPC 2.0 over subprocess stdin/stdout. * Messages are newline-delimited JSON. */ - import { type Subprocess, spawn } from "bun"; import type { JsonRpcResponse, MCPStdioServerConfig, MCPTransport } from "../../mcp/types"; diff --git a/packages/coding-agent/src/migrations.ts b/packages/coding-agent/src/migrations.ts index 442847a2d..7683246c3 100644 --- a/packages/coding-agent/src/migrations.ts +++ b/packages/coding-agent/src/migrations.ts @@ -1,13 +1,10 @@ /** * One-time migrations that run on startup. */ - import * as fs from "node:fs"; import * as path from "node:path"; - import { isEnoent, logger } from "@oh-my-pi/pi-utils"; import chalk from "chalk"; - import { getAgentDbPath, getAgentDir, getBinDir } from "./config"; import { AgentStorage } from "./session/agent-storage"; import type { AuthCredential } from "./session/auth-storage"; @@ -92,7 +89,7 @@ export async function migrateSessionsFromAgentRoot(): Promise { let files: string[]; try { const entries = await fs.promises.readdir(agentDir); - files = entries.filter((f) => f.endsWith(".jsonl")).map((f) => path.join(agentDir, f)); + files = entries.filter(f => f.endsWith(".jsonl")).map(f => path.join(agentDir, f)); } catch (error) { logger.warn("Failed to read agent directory for session migration", { path: agentDir, error: String(error) }); return; diff --git a/packages/coding-agent/src/modes/components/armin.ts b/packages/coding-agent/src/modes/components/armin.ts index b379e591a..b7852043a 100644 --- a/packages/coding-agent/src/modes/components/armin.ts +++ b/packages/coding-agent/src/modes/components/armin.ts @@ -1,7 +1,6 @@ /** * Armin says hi! A fun easter egg with animated XBM art. */ - import type { Component, TUI } from "@oh-my-pi/pi-tui"; import { theme } from "../../modes/theme/theme"; @@ -91,7 +90,7 @@ export class ArminComponent implements Component { const padding = 1; const availableWidth = width - padding; - this.cachedLines = this.currentGrid.map((row) => { + this.cachedLines = this.currentGrid.map(row => { // Clip row to available width before applying color const clipped = row.slice(0, availableWidth).join(""); const padRight = Math.max(0, width - padding - clipped.length); @@ -332,7 +331,7 @@ export class ArminComponent implements Component { if (state.phase < state.glitchFrames) { // Glitch phase: show corrupted version - this.currentGrid = this.finalGrid.map((row) => { + this.currentGrid = this.finalGrid.map(row => { const offset = Math.floor(Math.random() * 7) - 3; const glitchRow = [...row]; @@ -355,7 +354,7 @@ export class ArminComponent implements Component { } // Final frame: show clean image - this.currentGrid = this.finalGrid.map((row) => [...row]); + this.currentGrid = this.finalGrid.map(row => [...row]); return true; } diff --git a/packages/coding-agent/src/modes/components/assistant-message.ts b/packages/coding-agent/src/modes/components/assistant-message.ts index 6bcfb1ad2..f96a4fff5 100644 --- a/packages/coding-agent/src/modes/components/assistant-message.ts +++ b/packages/coding-agent/src/modes/components/assistant-message.ts @@ -42,7 +42,7 @@ export class AssistantMessageComponent extends Container { if (!caps.images || this.prerenderInFlight) return; // Check if any text content has pending mermaid blocks - const hasPending = message.content.some((c) => c.type === "text" && c.text.trim() && hasPendingMermaid(c.text)); + const hasPending = message.content.some(c => c.type === "text" && c.text.trim() && hasPendingMermaid(c.text)); if (!hasPending) return; this.prerenderInFlight = true; @@ -70,7 +70,7 @@ export class AssistantMessageComponent extends Container { this.triggerMermaidPrerender(message); const hasVisibleContent = message.content.some( - (c) => (c.type === "text" && c.text.trim()) || (c.type === "thinking" && c.thinking.trim()), + c => (c.type === "text" && c.text.trim()) || (c.type === "thinking" && c.thinking.trim()), ); if (hasVisibleContent) { @@ -86,7 +86,7 @@ export class AssistantMessageComponent extends Container { this.contentContainer.addChild(new Markdown(content.text.trim(), 1, 0, getMarkdownTheme())); } else if (content.type === "thinking" && content.thinking.trim()) { // Check if there's text content after this thinking block - const hasTextAfter = message.content.slice(i + 1).some((c) => c.type === "text" && c.text.trim()); + const hasTextAfter = message.content.slice(i + 1).some(c => c.type === "text" && c.text.trim()); if (this.hideThinkingBlock) { // Show static "Thinking..." label when hidden @@ -109,7 +109,7 @@ export class AssistantMessageComponent extends Container { // Check if aborted - show after partial content // But only if there are no tool calls (tool execution components will show the error) - const hasToolCalls = message.content.some((c) => c.type === "toolCall"); + const hasToolCalls = message.content.some(c => c.type === "toolCall"); if (!hasToolCalls) { if (message.stopReason === "aborted") { const abortMessage = diff --git a/packages/coding-agent/src/modes/components/bash-execution.ts b/packages/coding-agent/src/modes/components/bash-execution.ts index 5ce8a3709..5748c8ce6 100644 --- a/packages/coding-agent/src/modes/components/bash-execution.ts +++ b/packages/coding-agent/src/modes/components/bash-execution.ts @@ -1,7 +1,6 @@ /** * Component for displaying bash command execution with streaming output. */ - import { Container, Loader, Spacer, Text, type TUI } from "@oh-my-pi/pi-tui"; import stripAnsi from "strip-ansi"; import { getSymbolTheme, theme } from "../../modes/theme/theme"; @@ -48,8 +47,8 @@ export class BashExecutionComponent extends Container { // Loader this.loader = new Loader( ui, - (spinner) => theme.fg(colorKey, spinner), - (text) => theme.fg("muted", text), + spinner => theme.fg(colorKey, spinner), + text => theme.fg("muted", text), `Running${theme.format.ellipsis} (esc to cancel)`, getSymbolTheme().spinnerFrames, ); @@ -128,11 +127,11 @@ export class BashExecutionComponent extends Container { if (availableLines.length > 0) { if (this.expanded) { // Show all lines - const displayText = availableLines.map((line) => theme.fg("muted", line)).join("\n"); + const displayText = availableLines.map(line => theme.fg("muted", line)).join("\n"); this.contentContainer.addChild(new Text(`\n${displayText}`, 1, 0)); } else { // Use shared visual truncation utility, recomputed per render width - const styledOutput = previewLogicalLines.map((line) => theme.fg("muted", line)).join("\n"); + const styledOutput = previewLogicalLines.map(line => theme.fg("muted", line)).join("\n"); const previewText = `\n${styledOutput}`; this.contentContainer.addChild({ render: (width: number) => { diff --git a/packages/coding-agent/src/modes/components/bordered-loader.ts b/packages/coding-agent/src/modes/components/bordered-loader.ts index db28ef800..a164bee5d 100644 --- a/packages/coding-agent/src/modes/components/bordered-loader.ts +++ b/packages/coding-agent/src/modes/components/bordered-loader.ts @@ -12,8 +12,8 @@ export class BorderedLoader extends Container { this.addChild(new DynamicBorder(borderColor)); this.loader = new CancellableLoader( tui, - (s) => theme.fg("accent", s), - (s) => theme.fg("muted", s), + s => theme.fg("accent", s), + s => theme.fg("muted", s), message, ); this.addChild(this.loader); diff --git a/packages/coding-agent/src/modes/components/branch-summary-message.ts b/packages/coding-agent/src/modes/components/branch-summary-message.ts index e9666593a..09d313006 100644 --- a/packages/coding-agent/src/modes/components/branch-summary-message.ts +++ b/packages/coding-agent/src/modes/components/branch-summary-message.ts @@ -11,7 +11,7 @@ export class BranchSummaryMessageComponent extends Box { private message: BranchSummaryMessage; constructor(message: BranchSummaryMessage) { - super(1, 1, (t) => theme.bg("customMessageBg", t)); + super(1, 1, t => theme.bg("customMessageBg", t)); this.message = message; this.updateDisplay(); } diff --git a/packages/coding-agent/src/modes/components/compaction-summary-message.ts b/packages/coding-agent/src/modes/components/compaction-summary-message.ts index 532d16fba..d9754e0c1 100644 --- a/packages/coding-agent/src/modes/components/compaction-summary-message.ts +++ b/packages/coding-agent/src/modes/components/compaction-summary-message.ts @@ -11,7 +11,7 @@ export class CompactionSummaryMessageComponent extends Box { private message: CompactionSummaryMessage; constructor(message: CompactionSummaryMessage) { - super(1, 1, (t) => theme.bg("customMessageBg", t)); + super(1, 1, t => theme.bg("customMessageBg", t)); this.message = message; this.updateDisplay(); } diff --git a/packages/coding-agent/src/modes/components/countdown-timer.ts b/packages/coding-agent/src/modes/components/countdown-timer.ts index 18416814d..ef720cec3 100644 --- a/packages/coding-agent/src/modes/components/countdown-timer.ts +++ b/packages/coding-agent/src/modes/components/countdown-timer.ts @@ -1,7 +1,6 @@ /** * Reusable countdown timer for dialog components. */ - import type { TUI } from "@oh-my-pi/pi-tui"; export class CountdownTimer { diff --git a/packages/coding-agent/src/modes/components/custom-message.ts b/packages/coding-agent/src/modes/components/custom-message.ts index 2d5dbbd19..8bde96838 100644 --- a/packages/coding-agent/src/modes/components/custom-message.ts +++ b/packages/coding-agent/src/modes/components/custom-message.ts @@ -24,7 +24,7 @@ export class CustomMessageComponent extends Container { this.addChild(new Spacer(1)); // Create box with custom background (used for default rendering) - this.box = new Box(1, 1, (t) => theme.bg("customMessageBg", t)); + this.box = new Box(1, 1, t => theme.bg("customMessageBg", t)); this.rebuild(); } @@ -79,7 +79,7 @@ export class CustomMessageComponent extends Container { } else { text = this.message.content .filter((c): c is TextContent => c.type === "text") - .map((c) => c.text) + .map(c => c.text) .join("\n"); } diff --git a/packages/coding-agent/src/modes/components/dynamic-border.ts b/packages/coding-agent/src/modes/components/dynamic-border.ts index c3efb3806..684638581 100644 --- a/packages/coding-agent/src/modes/components/dynamic-border.ts +++ b/packages/coding-agent/src/modes/components/dynamic-border.ts @@ -11,7 +11,7 @@ import { theme } from "../../modes/theme/theme"; export class DynamicBorder implements Component { private color: (str: string) => string; - constructor(color: (str: string) => string = (str) => theme.fg("border", str)) { + constructor(color: (str: string) => string = str => theme.fg("border", str)) { this.color = color; } diff --git a/packages/coding-agent/src/modes/components/extensions/extension-dashboard.ts b/packages/coding-agent/src/modes/components/extensions/extension-dashboard.ts index 043625c34..c5df474a9 100644 --- a/packages/coding-agent/src/modes/components/extensions/extension-dashboard.ts +++ b/packages/coding-agent/src/modes/components/extensions/extension-dashboard.ts @@ -11,7 +11,6 @@ * - Space: Toggle selected item (or master switch) * - Esc: Close dashboard (clears search first if active) */ - import { type Component, Container, matchesKey, Spacer, Text, truncateToWidth, visibleWidth } from "@oh-my-pi/pi-tui"; import type { SettingsManager } from "../../../config/settings-manager"; import { DynamicBorder } from "../../../modes/components/dynamic-border"; @@ -60,14 +59,14 @@ export class ExtensionDashboard extends Container { this.mainList = new ExtensionList( this.state.searchFiltered, { - onSelectionChange: (ext) => { + onSelectionChange: ext => { this.state.selected = ext; this.inspector.setExtension(ext); }, onToggle: (extensionId, enabled) => { this.handleExtensionToggle(extensionId, enabled); }, - onMasterToggle: (providerId) => { + onMasterToggle: providerId => { this.handleProviderToggle(providerId); }, masterSwitchProvider: this.getActiveProviderId(), @@ -176,7 +175,7 @@ export class ExtensionDashboard extends Container { // Find the same tab in the new (re-sorted) list if (currentTabId) { - const newIndex = this.state.tabs.findIndex((t) => t.id === currentTabId); + const newIndex = this.state.tabs.findIndex(t => t.id === currentTabId); if (newIndex >= 0) { this.state.activeTabIndex = newIndex; } diff --git a/packages/coding-agent/src/modes/components/extensions/extension-list.ts b/packages/coding-agent/src/modes/components/extensions/extension-list.ts index 60ce9969e..cd4364977 100644 --- a/packages/coding-agent/src/modes/components/extensions/extension-list.ts +++ b/packages/coding-agent/src/modes/components/extensions/extension-list.ts @@ -5,7 +5,6 @@ * that toggles the entire provider. All items below are dimmed when the * master switch is off. */ - import { type Component, matchesKey, truncateToWidth, visibleWidth } from "@oh-my-pi/pi-tui"; import { isProviderEnabled } from "../../../discovery"; import { theme } from "../../../modes/theme/theme"; diff --git a/packages/coding-agent/src/modes/components/extensions/inspector-panel.ts b/packages/coding-agent/src/modes/components/extensions/inspector-panel.ts index 919b3c48e..87ffc3853 100644 --- a/packages/coding-agent/src/modes/components/extensions/inspector-panel.ts +++ b/packages/coding-agent/src/modes/components/extensions/inspector-panel.ts @@ -3,7 +3,6 @@ * * Shows name, description, origin, status, and kind-specific preview. */ - import * as os from "node:os"; import { type Component, truncateToWidth, wrapTextWithAnsi } from "@oh-my-pi/pi-tui"; import { theme } from "../../../modes/theme/theme"; diff --git a/packages/coding-agent/src/modes/components/extensions/state-manager.ts b/packages/coding-agent/src/modes/components/extensions/state-manager.ts index 8b739f73b..87fca5fee 100644 --- a/packages/coding-agent/src/modes/components/extensions/state-manager.ts +++ b/packages/coding-agent/src/modes/components/extensions/state-manager.ts @@ -2,7 +2,6 @@ * State manager for the Extension Control Center. * Handles data loading, tree building, filtering, and toggle persistence. */ - import { logger } from "@oh-my-pi/pi-utils"; import type { ContextFile } from "../../../capability/context-file"; import type { ExtensionModule } from "../../../capability/extension-module"; @@ -103,8 +102,8 @@ export async function loadAllExtensions(cwd?: string, disabledIds?: string[]): P try { const skills = await loadCapability("skills", loadOpts); addItems(skills.all, "skill", { - getDescription: (s) => s.frontmatter?.description, - getTrigger: (s) => s.frontmatter?.globs?.join(", "), + getDescription: s => s.frontmatter?.description, + getTrigger: s => s.frontmatter?.globs?.join(", "), }); } catch (error) { logger.warn("Failed to load skills capability", { error: String(error) }); @@ -114,8 +113,8 @@ export async function loadAllExtensions(cwd?: string, disabledIds?: string[]): P try { const rules = await loadCapability("rules", loadOpts); addItems(rules.all, "rule", { - getDescription: (r) => r.description, - getTrigger: (r) => r.globs?.join(", ") || (r.alwaysApply ? "always" : undefined), + getDescription: r => r.description, + getTrigger: r => r.globs?.join(", ") || (r.alwaysApply ? "always" : undefined), }); } catch (error) { logger.warn("Failed to load rules capability", { error: String(error) }); @@ -125,7 +124,7 @@ export async function loadAllExtensions(cwd?: string, disabledIds?: string[]): P try { const tools = await loadCapability("tools", loadOpts); addItems(tools.all, "tool", { - getDescription: (t) => t.description, + getDescription: t => t.description, }); } catch (error) { logger.warn("Failed to load tools capability", { error: String(error) }); @@ -134,7 +133,7 @@ export async function loadAllExtensions(cwd?: string, disabledIds?: string[]): P // Load extension modules try { const modules = await loadCapability("extension-modules", loadOpts); - const nativeModules = modules.all.filter((module) => module._source.provider === "native"); + const nativeModules = modules.all.filter(module => module._source.provider === "native"); addItems(nativeModules, "extension-module"); } catch (error) { logger.warn("Failed to load extension-modules capability", { error: String(error) }); @@ -188,7 +187,7 @@ export async function loadAllExtensions(cwd?: string, disabledIds?: string[]): P const prompts = await loadCapability("prompts", loadOpts); addItems(prompts.all, "prompt", { getDescription: () => undefined, - getTrigger: (p) => `/prompts:${p.name}`, + getTrigger: p => `/prompts:${p.name}`, }); } catch (error) { logger.warn("Failed to load prompts capability", { error: String(error) }); @@ -199,7 +198,7 @@ export async function loadAllExtensions(cwd?: string, disabledIds?: string[]): P const commands = await loadCapability("slash-commands", loadOpts); addItems(commands.all, "slash-command", { getDescription: () => undefined, - getTrigger: (c) => `/${c.name}`, + getTrigger: c => `/${c.name}`, }); } catch (error) { logger.warn("Failed to load slash-commands capability", { error: String(error) }); @@ -396,7 +395,7 @@ export function applyFilter(extensions: Extension[], query: string): Extension[] return extensions; } - return extensions.filter((ext) => { + return extensions.filter(ext => { const searchable = [ ext.name, ext.displayName, @@ -408,7 +407,7 @@ export function applyFilter(extensions: Extension[], query: string): Extension[] .join(" ") .toLowerCase(); - return tokens.every((token) => searchable.includes(token)); + return tokens.every(token => searchable.includes(token)); }); } @@ -506,7 +505,7 @@ export function filterByProvider(extensions: Extension[], providerId: string): E if (providerId === "all") { return extensions; } - return extensions.filter((ext) => ext.source.provider === providerId); + return extensions.filter(ext => ext.source.provider === providerId); } /** @@ -564,12 +563,12 @@ export async function refreshState( const searchFiltered = applyFilter(tabFiltered, state.searchQuery); // Find new index for current provider (tabs may have reordered) - const newActiveTabIndex = tabs.findIndex((t) => t.id === providerId); + const newActiveTabIndex = tabs.findIndex(t => t.id === providerId); const activeTabIndex = newActiveTabIndex >= 0 ? newActiveTabIndex : 0; // Try to preserve selection const selectedId = state.selected?.id; - let selected = selectedId ? searchFiltered.find((e) => e.id === selectedId) : null; + let selected = selectedId ? searchFiltered.find(e => e.id === selectedId) : null; if (!selected && searchFiltered.length > 0) { selected = searchFiltered[Math.min(state.listIndex, searchFiltered.length - 1)]; } diff --git a/packages/coding-agent/src/modes/components/extensions/types.ts b/packages/coding-agent/src/modes/components/extensions/types.ts index 32db236a5..cdfe76edb 100644 --- a/packages/coding-agent/src/modes/components/extensions/types.ts +++ b/packages/coding-agent/src/modes/components/extensions/types.ts @@ -1,7 +1,6 @@ /** * Types for the Extension Control Center dashboard. */ - import type { SourceMeta } from "../../../capability/types"; /** diff --git a/packages/coding-agent/src/modes/components/footer.ts b/packages/coding-agent/src/modes/components/footer.ts index 061a98da8..d958c4ae3 100644 --- a/packages/coding-agent/src/modes/components/footer.ts +++ b/packages/coding-agent/src/modes/components/footer.ts @@ -4,7 +4,6 @@ import type { AssistantMessage } from "@oh-my-pi/pi-ai"; import { type Component, truncateToWidth, visibleWidth } from "@oh-my-pi/pi-tui"; import { isEnoent } from "@oh-my-pi/pi-utils"; import { theme } from "../../modes/theme/theme"; - import type { AgentSession } from "../../session/agent-session"; import { shortenPath } from "../../tools/render-utils"; @@ -89,7 +88,7 @@ export class FooterComponent implements Component { this.gitWatcher = null; } - findGitHeadPath().then((result) => { + findGitHeadPath().then(result => { if (!result) { return; } @@ -134,7 +133,7 @@ export class FooterComponent implements Component { // Note: fire-and-forget async call - will return undefined on first call // This is acceptable since it's a cached value that will update on next render - findGitHeadPath().then((result) => { + findGitHeadPath().then(result => { if (!result) { this.cachedBranch = null; if (this.onBranchChange) { @@ -182,7 +181,7 @@ export class FooterComponent implements Component { const lastAssistantMessage = state.messages .slice() .reverse() - .find((m) => m.role === "assistant" && m.stopReason !== "aborted") as AssistantMessage | undefined; + .find(m => m.role === "assistant" && m.stopReason !== "aborted") as AssistantMessage | undefined; // Calculate context percentage from last message (input + output + cacheRead + cacheWrite) const contextTokens = lastAssistantMessage diff --git a/packages/coding-agent/src/modes/components/hook-editor.ts b/packages/coding-agent/src/modes/components/hook-editor.ts index 692a7f41b..ed2f7d8b7 100644 --- a/packages/coding-agent/src/modes/components/hook-editor.ts +++ b/packages/coding-agent/src/modes/components/hook-editor.ts @@ -2,7 +2,6 @@ * Multi-line editor component for hooks. * Supports Ctrl+G for external editor. */ - import * as fs from "node:fs/promises"; import * as os from "node:os"; import * as path from "node:path"; diff --git a/packages/coding-agent/src/modes/components/hook-input.ts b/packages/coding-agent/src/modes/components/hook-input.ts index 50280a8c9..b3cf862c4 100644 --- a/packages/coding-agent/src/modes/components/hook-input.ts +++ b/packages/coding-agent/src/modes/components/hook-input.ts @@ -1,7 +1,6 @@ /** * Simple text input component for hooks. */ - import { Container, Input, matchesKey, Spacer, Text, type TUI } from "@oh-my-pi/pi-tui"; import { theme } from "../../modes/theme/theme"; import { CountdownTimer } from "./countdown-timer"; @@ -44,7 +43,7 @@ export class HookInputComponent extends Container { this.countdown = new CountdownTimer( opts.timeout, opts.tui, - (s) => this.titleText.setText(theme.fg("accent", `${this.baseTitle} (${s}s)`)), + s => this.titleText.setText(theme.fg("accent", `${this.baseTitle} (${s}s)`)), () => this.onCancelCallback(), ); } diff --git a/packages/coding-agent/src/modes/components/hook-message.ts b/packages/coding-agent/src/modes/components/hook-message.ts index 7311b177a..ae13c9867 100644 --- a/packages/coding-agent/src/modes/components/hook-message.ts +++ b/packages/coding-agent/src/modes/components/hook-message.ts @@ -24,7 +24,7 @@ export class HookMessageComponent extends Container { this.addChild(new Spacer(1)); // Create box with purple background (used for default rendering) - this.box = new Box(1, 1, (t) => theme.bg("customMessageBg", t)); + this.box = new Box(1, 1, t => theme.bg("customMessageBg", t)); this.rebuild(); } @@ -80,7 +80,7 @@ export class HookMessageComponent extends Container { } else { text = this.message.content .filter((c): c is TextContent => c.type === "text") - .map((c) => c.text) + .map(c => c.text) .join("\n"); } diff --git a/packages/coding-agent/src/modes/components/hook-selector.ts b/packages/coding-agent/src/modes/components/hook-selector.ts index 98ecf2afb..eaa904c34 100644 --- a/packages/coding-agent/src/modes/components/hook-selector.ts +++ b/packages/coding-agent/src/modes/components/hook-selector.ts @@ -2,7 +2,6 @@ * Generic selector component for hooks. * Displays a list of string options with keyboard navigation. */ - import { Container, matchesKey, Spacer, Text, type TUI } from "@oh-my-pi/pi-tui"; import { theme } from "../../modes/theme/theme"; import { CountdownTimer } from "./countdown-timer"; @@ -50,7 +49,7 @@ export class HookSelectorComponent extends Container { this.countdown = new CountdownTimer( opts.timeout, opts.tui, - (s) => this.titleText.setText(theme.fg("accent", `${this.baseTitle} (${s}s)`)), + s => this.titleText.setText(theme.fg("accent", `${this.baseTitle} (${s}s)`)), () => this.onCancelCallback(), ); } diff --git a/packages/coding-agent/src/modes/components/keybinding-hints.ts b/packages/coding-agent/src/modes/components/keybinding-hints.ts index dfe287c7e..da567878a 100644 --- a/packages/coding-agent/src/modes/components/keybinding-hints.ts +++ b/packages/coding-agent/src/modes/components/keybinding-hints.ts @@ -1,7 +1,6 @@ /** * Utilities for formatting keybinding hints in the UI. */ - import { type EditorAction, getEditorKeybindings, type KeyId } from "@oh-my-pi/pi-tui"; import type { AppAction, KeybindingsManager } from "../../config/keybindings"; import { theme } from "../../modes/theme/theme"; diff --git a/packages/coding-agent/src/modes/components/login-dialog.ts b/packages/coding-agent/src/modes/components/login-dialog.ts index 726ce7065..af27120f3 100644 --- a/packages/coding-agent/src/modes/components/login-dialog.ts +++ b/packages/coding-agent/src/modes/components/login-dialog.ts @@ -23,7 +23,7 @@ export class LoginDialogComponent extends Container { super(); this.tui = tui; - const providerInfo = getOAuthProviders().find((p) => p.id === providerId); + const providerInfo = getOAuthProviders().find(p => p.id === providerId); const providerName = providerInfo?.name || providerId; // Top border diff --git a/packages/coding-agent/src/modes/components/model-selector.ts b/packages/coding-agent/src/modes/components/model-selector.ts index f531c6747..a1ecab397 100644 --- a/packages/coding-agent/src/modes/components/model-selector.ts +++ b/packages/coding-agent/src/modes/components/model-selector.ts @@ -190,7 +190,7 @@ export class ModelSelectorComponent extends Container { if (defaultStr) { const parsed = parseModelString(defaultStr); if (parsed) { - this.defaultModel = allModels.find((m) => m.provider === parsed.provider && m.id === parsed.id); + this.defaultModel = allModels.find(m => m.provider === parsed.provider && m.id === parsed.id); } } @@ -199,7 +199,7 @@ export class ModelSelectorComponent extends Container { if (smolStr) { const parsed = parseModelString(smolStr); if (parsed) { - this.smolModel = allModels.find((m) => m.provider === parsed.provider && m.id === parsed.id); + this.smolModel = allModels.find(m => m.provider === parsed.provider && m.id === parsed.id); } } @@ -208,7 +208,7 @@ export class ModelSelectorComponent extends Container { if (slowStr) { const parsed = parseModelString(slowStr); if (parsed) { - this.slowModel = allModels.find((m) => m.provider === parsed.provider && m.id === parsed.id); + this.slowModel = allModels.find(m => m.provider === parsed.provider && m.id === parsed.id); } } } @@ -218,7 +218,7 @@ export class ModelSelectorComponent extends Container { // Use scoped models if provided via --models flag if (this.scopedModels.length > 0) { - models = this.scopedModels.map((scoped) => ({ + models = this.scopedModels.map(scoped => ({ provider: scoped.model.provider, id: scoped.model.id, model: scoped.model, @@ -279,7 +279,7 @@ export class ModelSelectorComponent extends Container { private updateTabBar(): void { this.headerContainer.clear(); - const tabs: Tab[] = this.providers.map((provider) => ({ id: provider, label: provider })); + const tabs: Tab[] = this.providers.map(provider => ({ id: provider, label: provider })); const tabBar = new TabBar("Models", tabs, getTabBarTheme(), this.activeTabIndex); tabBar.onTabChange = (_tab, index) => { this.activeTabIndex = index; @@ -300,7 +300,7 @@ export class ModelSelectorComponent extends Container { // Start with all models or filter by provider let baseModels = this.allModels; if (activeProvider !== ALL_TAB) { - baseModels = this.allModels.filter((m) => m.provider.toUpperCase() === activeProvider); + baseModels = this.allModels.filter(m => m.provider.toUpperCase() === activeProvider); } // Apply fuzzy filter if query is present @@ -426,7 +426,7 @@ export class ModelSelectorComponent extends Container { const menuWidth = Math.max( visibleWidth(headerText), visibleWidth(hintText), - ...actionLines.map((line) => visibleWidth(line)), + ...actionLines.map(line => visibleWidth(line)), ); // Menu header diff --git a/packages/coding-agent/src/modes/components/plugin-settings.ts b/packages/coding-agent/src/modes/components/plugin-settings.ts index d055b1b1f..2c13bbd73 100644 --- a/packages/coding-agent/src/modes/components/plugin-settings.ts +++ b/packages/coding-agent/src/modes/components/plugin-settings.ts @@ -7,7 +7,6 @@ * - Feature toggles * - Config value editor */ - import { Container, Input, @@ -62,7 +61,7 @@ export class PluginListComponent extends Container { return; } - const items: SelectItem[] = plugins.map((p) => { + const items: SelectItem[] = plugins.map(p => { const status = p.enabled ? theme.fg("success", theme.status.enabled) : theme.fg("muted", theme.status.disabled); @@ -83,8 +82,8 @@ export class PluginListComponent extends Container { this.selectList = new SelectList(items, Math.min(items.length, 8), getSelectListTheme()); - this.selectList.onSelect = (item) => { - const plugin = this.plugins.find((p) => p.name === item.value); + this.selectList.onSelect = item => { + const plugin = this.plugins.find(p => p.name === item.value); if (plugin) { callbacks.onPluginSelect(plugin); } @@ -210,7 +209,7 @@ export class PluginDetailComponent extends Container { schema.description || `Select value for ${key}`, schema.values, cv, - (value) => { + value => { this.callbacks.onConfigChange(key, value); done(value); }, @@ -229,7 +228,7 @@ export class PluginDetailComponent extends Container { key, schema, cv === "(not set)" ? "" : cv, - (value) => { + value => { const parsed = schema.type === "number" ? Number(value) : value; this.callbacks.onConfigChange(key, parsed); done(String(value)); @@ -310,7 +309,7 @@ class ConfigEnumSubmenu extends Container { } this.addChild(new Spacer(1)); - const items: SelectItem[] = values.map((v) => ({ value: v, label: v })); + const items: SelectItem[] = values.map(v => ({ value: v, label: v })); this.selectList = new SelectList(items, Math.min(items.length, 8), getSelectListTheme()); const currentIndex = values.indexOf(currentValue); @@ -318,7 +317,7 @@ class ConfigEnumSubmenu extends Container { this.selectList.setSelectedIndex(currentIndex); } - this.selectList.onSelect = (item) => onSelect(item.value); + this.selectList.onSelect = item => onSelect(item.value); this.selectList.onCancel = onCancel; this.addChild(this.selectList); @@ -375,7 +374,7 @@ class ConfigInputSubmenu extends Container { this.input.setValue(currentValue); } - this.input.onSubmit = (value) => { + this.input.onSubmit = value => { if (value.trim()) { this.onSubmit(value); } else { @@ -439,7 +438,7 @@ export class PluginSettingsComponent extends Container { const plugins = await this.manager.list(); this.viewComponent = new PluginListComponent(plugins, { - onPluginSelect: (plugin) => this.showPluginDetail(plugin), + onPluginSelect: plugin => this.showPluginDetail(plugin), onCancel: () => this.callbacks.onClose(), }); @@ -452,7 +451,7 @@ export class PluginSettingsComponent extends Container { this.clear(); this.viewComponent = new PluginDetailComponent(plugin, this.manager, { - onEnabledChange: async (enabled) => { + onEnabledChange: async enabled => { await this.manager.setEnabled(plugin.name, enabled); this.callbacks.onPluginChanged(); }, diff --git a/packages/coding-agent/src/modes/components/python-execution.ts b/packages/coding-agent/src/modes/components/python-execution.ts index 697f2b280..c9869f97b 100644 --- a/packages/coding-agent/src/modes/components/python-execution.ts +++ b/packages/coding-agent/src/modes/components/python-execution.ts @@ -2,7 +2,6 @@ * Component for displaying user-initiated Python execution with streaming output. * Shares the same kernel session as the agent's Python tool. */ - import { Container, Loader, Spacer, Text, type TUI } from "@oh-my-pi/pi-tui"; import stripAnsi from "strip-ansi"; import { getSymbolTheme, highlightCode, theme } from "../../modes/theme/theme"; @@ -51,8 +50,8 @@ export class PythonExecutionComponent extends Container { this.loader = new Loader( ui, - (spinner) => theme.fg(colorKey, spinner), - (text) => theme.fg("muted", text), + spinner => theme.fg(colorKey, spinner), + text => theme.fg("muted", text), `Running${theme.format.ellipsis} (esc to cancel)`, getSymbolTheme().spinnerFrames, ); @@ -117,10 +116,10 @@ export class PythonExecutionComponent extends Container { if (availableLines.length > 0) { if (this.expanded) { - const displayText = availableLines.map((line) => theme.fg("muted", line)).join("\n"); + const displayText = availableLines.map(line => theme.fg("muted", line)).join("\n"); this.contentContainer.addChild(new Text(`\n${displayText}`, 1, 0)); } else { - const styledOutput = previewLogicalLines.map((line) => theme.fg("muted", line)).join("\n"); + const styledOutput = previewLogicalLines.map(line => theme.fg("muted", line)).join("\n"); const previewText = `\n${styledOutput}`; this.contentContainer.addChild({ render: (width: number) => { diff --git a/packages/coding-agent/src/modes/components/queue-mode-selector.ts b/packages/coding-agent/src/modes/components/queue-mode-selector.ts index 00fd141de..ae631113e 100644 --- a/packages/coding-agent/src/modes/components/queue-mode-selector.ts +++ b/packages/coding-agent/src/modes/components/queue-mode-selector.ts @@ -31,12 +31,12 @@ export class QueueModeSelectorComponent extends Container { this.selectList = new SelectList(queueModes, 2, getSelectListTheme()); // Preselect current mode - const currentIndex = queueModes.findIndex((item) => item.value === currentMode); + const currentIndex = queueModes.findIndex(item => item.value === currentMode); if (currentIndex !== -1) { this.selectList.setSelectedIndex(currentIndex); } - this.selectList.onSelect = (item) => { + this.selectList.onSelect = item => { onSelect(item.value as "all" | "one-at-a-time"); }; diff --git a/packages/coding-agent/src/modes/components/session-selector.ts b/packages/coding-agent/src/modes/components/session-selector.ts index c27ab4cf4..527961a6f 100644 --- a/packages/coding-agent/src/modes/components/session-selector.ts +++ b/packages/coding-agent/src/modes/components/session-selector.ts @@ -45,7 +45,7 @@ class SessionList implements Component { } private filterSessions(query: string): void { - this.filteredSessions = fuzzyFilter(this.allSessions, query, (session) => { + this.filteredSessions = fuzzyFilter(this.allSessions, query, session => { const parts = [ session.id, session.title ?? "", diff --git a/packages/coding-agent/src/modes/components/settings-defs.ts b/packages/coding-agent/src/modes/components/settings-defs.ts index 3b66e073e..b2593efd6 100644 --- a/packages/coding-agent/src/modes/components/settings-defs.ts +++ b/packages/coding-agent/src/modes/components/settings-defs.ts @@ -7,7 +7,6 @@ * 2. Add the definition here * 3. Add the handler in interactive-mode.ts settingsHandlers */ - import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core"; import { getCapabilities } from "@oh-my-pi/pi-tui"; import type { @@ -98,7 +97,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "boolean", label: "Auto-compact", description: "Automatically compact context when it gets too large", - get: (sm) => sm.getCompactionEnabled(), + get: sm => sm.getCompactionEnabled(), set: (sm, v) => sm.setCompactionEnabled(v), }, { @@ -107,7 +106,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "boolean", label: "Branch summaries", description: "Prompt to summarize when leaving a branch", - get: (sm) => sm.getBranchSummaryEnabled(), + get: sm => sm.getBranchSummaryEnabled(), set: (sm, v) => sm.setBranchSummaryEnabled(v), }, { @@ -116,7 +115,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "boolean", label: "Todo completion", description: "Remind agent to complete todos before stopping", - get: (sm) => sm.getTodoCompletionEnabled(), + get: sm => sm.getTodoCompletionEnabled(), set: (sm, v) => sm.setTodoCompletionEnabled(v), }, { @@ -125,7 +124,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "submenu", label: "Todo max reminders", description: "Maximum reminders to complete todos before giving up", - get: (sm) => String(sm.getTodoCompletionMaxReminders()), + get: sm => String(sm.getTodoCompletionMaxReminders()), set: (sm, v) => sm.setTodoCompletionMaxReminders(Number.parseInt(v, 10)), getOptions: () => [ { value: "1", label: "1 reminder" }, @@ -141,7 +140,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ label: "Steering mode", description: "How to process queued messages while agent is working", values: ["one-at-a-time", "all"], - get: (sm) => sm.getSteeringMode(), + get: sm => sm.getSteeringMode(), set: (sm, v) => sm.setSteeringMode(v as "all" | "one-at-a-time"), }, { @@ -151,7 +150,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ label: "Follow-up mode", description: "How to drain follow-up messages after a turn completes", values: ["one-at-a-time", "all"], - get: (sm) => sm.getFollowUpMode(), + get: sm => sm.getFollowUpMode(), set: (sm, v) => sm.setFollowUpMode(v as "one-at-a-time" | "all"), }, { @@ -161,7 +160,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ label: "Interrupt mode", description: "When steering messages interrupt tool execution", values: ["immediate", "wait"], - get: (sm) => sm.getInterruptMode(), + get: sm => sm.getInterruptMode(), set: (sm, v) => sm.setInterruptMode(v as "immediate" | "wait"), }, { @@ -170,7 +169,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "submenu", label: "Retry max attempts", description: "Maximum retry attempts on API errors", - get: (sm) => String(sm.getRetryMaxRetries()), + get: sm => String(sm.getRetryMaxRetries()), set: (sm, v) => sm.setRetryMaxRetries(Number.parseInt(v, 10)), getOptions: () => [ { value: "1", label: "1 retry" }, @@ -187,7 +186,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ label: "Completion notification", description: "Notify when the agent completes", values: ["auto", "bell", "osc99", "osc9", "off"], - get: (sm) => sm.getNotificationOnComplete(), + get: sm => sm.getNotificationOnComplete(), set: (sm, v) => sm.setNotificationOnComplete(v as NotificationMethod), }, { @@ -196,7 +195,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "boolean", label: "Startup quiet", description: "Skip welcome screen and startup status messages", - get: (sm) => sm.getStartupQuiet(), + get: sm => sm.getStartupQuiet(), set: (sm, v) => sm.setStartupQuiet(v), }, { @@ -205,7 +204,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "boolean", label: "Collapse changelog", description: "Show condensed changelog after updates", - get: (sm) => sm.getCollapseChangelog(), + get: sm => sm.getCollapseChangelog(), set: (sm, v) => sm.setCollapseChangelog(v), }, { @@ -214,7 +213,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "boolean", label: "Normative rewrite", description: "Rewrite tool call arguments to normalized format in session history", - get: (sm) => sm.getNormativeRewrite(), + get: sm => sm.getNormativeRewrite(), set: (sm, v) => sm.setNormativeRewrite(v), }, { @@ -224,7 +223,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ label: "Double-escape action", description: "Action when pressing Escape twice with empty editor", values: ["tree", "branch"], - get: (sm) => sm.getDoubleEscapeAction(), + get: sm => sm.getDoubleEscapeAction(), set: (sm, v) => sm.setDoubleEscapeAction(v as "branch" | "tree"), }, @@ -237,7 +236,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "boolean", label: "Bash interceptor", description: "Block shell commands that have dedicated tools (grep, cat, etc.)", - get: (sm) => sm.getBashInterceptorEnabled(), + get: sm => sm.getBashInterceptorEnabled(), set: (sm, v) => sm.setBashInterceptorEnabled(v), }, { @@ -246,7 +245,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "boolean", label: "Intercept simple ls", description: "Intercept bare ls commands (when bash interceptor is enabled)", - get: (sm) => sm.getBashInterceptorSimpleLsEnabled(), + get: sm => sm.getBashInterceptorSimpleLsEnabled(), set: (sm, v) => sm.setBashInterceptorSimpleLsEnabled(v), }, { @@ -256,7 +255,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ label: "Python tool mode", description: "How Python code is executed", values: ["ipy-only", "bash-only", "both"], - get: (sm) => sm.getPythonToolMode(), + get: sm => sm.getPythonToolMode(), set: (sm, v) => sm.setPythonToolMode(v as PythonToolMode), }, { @@ -266,7 +265,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ label: "Python kernel mode", description: "Whether to keep IPython kernel alive across calls", values: ["session", "per-call"], - get: (sm) => sm.getPythonKernelMode(), + get: sm => sm.getPythonKernelMode(), set: (sm, v) => sm.setPythonKernelMode(v as PythonKernelMode), }, { @@ -275,7 +274,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "boolean", label: "Python shared gateway", description: "Share IPython kernel gateway across pi instances", - get: (sm) => sm.getPythonSharedGateway(), + get: sm => sm.getPythonSharedGateway(), set: (sm, v) => sm.setPythonSharedGateway(v), }, { @@ -284,7 +283,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "boolean", label: "Edit fuzzy match", description: "Accept high-confidence fuzzy matches for whitespace/indentation differences", - get: (sm) => sm.getEditFuzzyMatch(), + get: sm => sm.getEditFuzzyMatch(), set: (sm, v) => sm.setEditFuzzyMatch(v), }, { @@ -293,7 +292,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "submenu", label: "Edit fuzzy threshold", description: "Similarity threshold for fuzzy matches (higher = stricter)", - get: (sm) => sm.getEditFuzzyThreshold().toFixed(2), + get: sm => sm.getEditFuzzyThreshold().toFixed(2), set: (sm, v) => sm.setEditFuzzyThreshold(Number(v)), getOptions: () => [ { value: "0.85", label: "0.85", description: "Lenient" }, @@ -308,7 +307,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "boolean", label: "Edit patch mode", description: "Use codex-style apply-patch format instead of old_text/new_text for edits", - get: (sm) => sm.getEditPatchMode(), + get: sm => sm.getEditPatchMode(), set: (sm, v) => sm.setEditPatchMode(v), }, { @@ -317,7 +316,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "boolean", label: "Edit streaming abort", description: "Abort streaming edit tool calls when patch preview fails", - get: (sm) => sm.getEditStreamingAbort(), + get: sm => sm.getEditStreamingAbort(), set: (sm, v) => sm.setEditStreamingAbort(v), }, { @@ -326,7 +325,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "boolean", label: "Read line numbers", description: "Prepend line numbers to read tool output by default", - get: (sm) => sm.getReadLineNumbers(), + get: sm => sm.getReadLineNumbers(), set: (sm, v) => sm.setReadLineNumbers(v), }, { @@ -335,7 +334,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "boolean", label: "MCP project config", description: "Load .mcp.json/mcp.json from project root", - get: (sm) => sm.getMCPProjectConfigEnabled(), + get: sm => sm.getMCPProjectConfigEnabled(), set: (sm, v) => sm.setMCPProjectConfigEnabled(v), }, { @@ -344,7 +343,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "boolean", label: "Skill commands", description: "Register skills as /skill:name commands", - get: (sm) => sm.getEnableSkillCommands(), + get: sm => sm.getEnableSkillCommands(), set: (sm, v) => sm.setEnableSkillCommands(v), }, { @@ -353,7 +352,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "boolean", label: "Claude user commands", description: "Load commands from ~/.claude/commands/", - get: (sm) => sm.getCommandsEnableClaudeUser(), + get: sm => sm.getCommandsEnableClaudeUser(), set: (sm, v) => sm.setCommandsEnableClaudeUser(v), }, { @@ -362,7 +361,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "boolean", label: "Claude project commands", description: "Load commands from .claude/commands/", - get: (sm) => sm.getCommandsEnableClaudeProject(), + get: sm => sm.getCommandsEnableClaudeProject(), set: (sm, v) => sm.setCommandsEnableClaudeProject(v), }, { @@ -371,7 +370,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "submenu", label: "Web search provider", description: "Provider for web search tool", - get: (sm) => sm.getWebSearchProvider(), + get: sm => sm.getWebSearchProvider(), set: (sm, v) => sm.setWebSearchProvider(v as WebSearchProviderOption), getOptions: () => [ { value: "auto", label: "Auto", description: "Priority: Exa > Perplexity > Anthropic" }, @@ -386,7 +385,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "submenu", label: "Image provider", description: "Provider for image generation tool", - get: (sm) => sm.getImageProvider(), + get: sm => sm.getImageProvider(), set: (sm, v) => sm.setImageProvider(v as ImageProviderOption), getOptions: () => [ { value: "auto", label: "Auto", description: "Priority: OpenRouter > Gemini" }, @@ -404,7 +403,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "submenu", label: "Theme", description: "Color theme for the interface", - get: (sm) => sm.getTheme() ?? "dark", + get: sm => sm.getTheme() ?? "dark", set: (sm, v) => sm.setTheme(v), getOptions: () => [], // Filled dynamically from context }, @@ -414,7 +413,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "submenu", label: "Symbol preset", description: "Icon/symbol style (overrides theme default)", - get: (sm) => sm.getSymbolPreset() ?? "unicode", + get: sm => sm.getSymbolPreset() ?? "unicode", set: (sm, v) => sm.setSymbolPreset(v as SymbolPreset), getOptions: () => [ { value: "unicode", label: "Unicode", description: "Standard Unicode symbols (default)" }, @@ -428,7 +427,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "boolean", label: "Color blind mode", description: "Use blue instead of green for diff additions (red-green color blindness)", - get: (sm) => sm.getColorBlindMode(), + get: sm => sm.getColorBlindMode(), set: (sm, v) => sm.setColorBlindMode(v), }, { @@ -437,10 +436,10 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "submenu", label: "Thinking level", description: "Reasoning depth for thinking-capable models", - get: (sm) => sm.getDefaultThinkingLevel() ?? "off", + get: sm => sm.getDefaultThinkingLevel() ?? "off", set: (sm, v) => sm.setDefaultThinkingLevel(v as ThinkingLevel), getOptions: () => - (["off", "minimal", "low", "medium", "high", "xhigh"] as ThinkingLevel[]).map((level) => ({ + (["off", "minimal", "low", "medium", "high", "xhigh"] as ThinkingLevel[]).map(level => ({ value: level, label: level, description: THINKING_DESCRIPTIONS[level], @@ -452,7 +451,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "boolean", label: "Hide thinking", description: "Hide thinking blocks in assistant responses", - get: (sm) => sm.getHideThinkingBlock(), + get: sm => sm.getHideThinkingBlock(), set: (sm, v) => sm.setHideThinkingBlock(v), }, { @@ -461,7 +460,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "boolean", label: "Show images", description: "Render images inline in terminal", - get: (sm) => sm.getShowImages(), + get: sm => sm.getShowImages(), set: (sm, v) => sm.setShowImages(v), condition: () => !!getCapabilities().images, }, @@ -471,7 +470,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "boolean", label: "Auto-resize images", description: "Resize large images to 2000x2000 max for better model compatibility", - get: (sm) => sm.getImageAutoResize(), + get: sm => sm.getImageAutoResize(), set: (sm, v) => sm.setImageAutoResize(v), }, { @@ -480,7 +479,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "boolean", label: "Block images", description: "Prevent images from being sent to LLM providers", - get: (sm) => sm.getBlockImages(), + get: sm => sm.getBlockImages(), set: (sm, v) => sm.setBlockImages(v), }, { @@ -489,7 +488,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "boolean", label: "Hardware cursor", description: "Show terminal cursor for IME support (default: on for Linux/macOS)", - get: (sm) => sm.getShowHardwareCursor(), + get: sm => sm.getShowHardwareCursor(), set: (sm, v) => sm.setShowHardwareCursor(v), }, @@ -502,7 +501,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "boolean", label: "TTSR enabled", description: "Time Traveling Stream Rules: interrupt agent when output matches patterns", - get: (sm) => sm.getTtsrEnabled(), + get: sm => sm.getTtsrEnabled(), set: (sm, v) => sm.setTtsrEnabled(v), }, { @@ -512,7 +511,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ label: "TTSR context mode", description: "What to do with partial output when TTSR triggers", values: ["discard", "keep"], - get: (sm) => sm.getTtsrContextMode(), + get: sm => sm.getTtsrContextMode(), set: (sm, v) => sm.setTtsrContextMode(v as "keep" | "discard"), }, { @@ -522,7 +521,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ label: "TTSR repeat mode", description: "How rules can repeat: once per session or after a message gap", values: ["once", "after-gap"], - get: (sm) => sm.getTtsrRepeatMode(), + get: sm => sm.getTtsrRepeatMode(), set: (sm, v) => sm.setTtsrRepeatMode(v as "once" | "after-gap"), }, { @@ -531,7 +530,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "submenu", label: "TTSR repeat gap", description: "Messages before a rule can trigger again (when repeat mode is after-gap)", - get: (sm) => String(sm.getTtsrRepeatGap()), + get: sm => String(sm.getTtsrRepeatGap()), set: (sm, v) => sm.setTtsrRepeatGap(Number.parseInt(v, 10)), getOptions: () => [ { value: "5", label: "5 messages" }, @@ -551,7 +550,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "submenu", label: "Preset", description: "Pre-built status line configurations", - get: (sm) => sm.getStatusLinePreset(), + get: sm => sm.getStatusLinePreset(), set: (sm, v) => sm.setStatusLinePreset(v as StatusLinePreset), getOptions: () => [ { value: "default", label: "Default", description: "Model, path, git, context, tokens, cost" }, @@ -569,7 +568,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "submenu", label: "Separator style", description: "Style of separators between segments", - get: (sm) => { + get: sm => { const settings = sm.getStatusLineSettings(); if (settings.separator) return settings.separator; return getPreset(sm.getStatusLinePreset()).separator; @@ -591,7 +590,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "boolean", label: "Show extension status", description: "Display hook status messages below status line", - get: (sm) => sm.getStatusLineShowHookStatus(), + get: sm => sm.getStatusLineShowHookStatus(), set: (sm, v) => sm.setStatusLineShowHookStatus(v), }, { @@ -611,7 +610,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ label: "Model thinking level", description: "Show thinking level in the model segment", values: ["default", "on", "off"], - get: (sm) => { + get: sm => { const value = sm.getStatusLineSegmentOptions().model?.showThinkingLevel; if (value === undefined) return "default"; return value ? "on" : "off"; @@ -631,7 +630,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ label: "Path abbreviate", description: "Use ~ and strip home prefix in path segment", values: ["default", "on", "off"], - get: (sm) => { + get: sm => { const value = sm.getStatusLineSegmentOptions().path?.abbreviate; if (value === undefined) return "default"; return value ? "on" : "off"; @@ -650,7 +649,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "submenu", label: "Path max length", description: "Maximum length for displayed path", - get: (sm) => { + get: sm => { const value = sm.getStatusLineSegmentOptions().path?.maxLength; return typeof value === "number" ? String(value) : "default"; }, @@ -678,7 +677,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ label: "Path strip /work", description: "Strip /work prefix in path segment", values: ["default", "on", "off"], - get: (sm) => { + get: sm => { const value = sm.getStatusLineSegmentOptions().path?.stripWorkPrefix; if (value === undefined) return "default"; return value ? "on" : "off"; @@ -698,7 +697,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ label: "Git show branch", description: "Show branch name in git segment", values: ["default", "on", "off"], - get: (sm) => { + get: sm => { const value = sm.getStatusLineSegmentOptions().git?.showBranch; if (value === undefined) return "default"; return value ? "on" : "off"; @@ -718,7 +717,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ label: "Git show staged", description: "Show staged file count in git segment", values: ["default", "on", "off"], - get: (sm) => { + get: sm => { const value = sm.getStatusLineSegmentOptions().git?.showStaged; if (value === undefined) return "default"; return value ? "on" : "off"; @@ -738,7 +737,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ label: "Git show unstaged", description: "Show unstaged file count in git segment", values: ["default", "on", "off"], - get: (sm) => { + get: sm => { const value = sm.getStatusLineSegmentOptions().git?.showUnstaged; if (value === undefined) return "default"; return value ? "on" : "off"; @@ -758,7 +757,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ label: "Git show untracked", description: "Show untracked file count in git segment", values: ["default", "on", "off"], - get: (sm) => { + get: sm => { const value = sm.getStatusLineSegmentOptions().git?.showUntracked; if (value === undefined) return "default"; return value ? "on" : "off"; @@ -778,7 +777,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ label: "Time format", description: "Clock segment time format", values: ["default", "12h", "24h"], - get: (sm) => sm.getStatusLineSegmentOptions().time?.format ?? "default", + get: sm => sm.getStatusLineSegmentOptions().time?.format ?? "default", set: (sm, v) => { if (v === "default") { sm.clearStatusLineSegmentOption("time", "format"); @@ -794,7 +793,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ label: "Time show seconds", description: "Include seconds in clock segment", values: ["default", "on", "off"], - get: (sm) => { + get: sm => { const value = sm.getStatusLineSegmentOptions().time?.showSeconds; if (value === undefined) return "default"; return value ? "on" : "off"; @@ -817,7 +816,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "boolean", label: "Format on write", description: "Automatically format code files using LSP after writing", - get: (sm) => sm.getLspFormatOnWrite(), + get: sm => sm.getLspFormatOnWrite(), set: (sm, v) => sm.setLspFormatOnWrite(v), }, { @@ -826,7 +825,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "boolean", label: "Diagnostics on write", description: "Return LSP diagnostics (errors/warnings) after writing code files", - get: (sm) => sm.getLspDiagnosticsOnWrite(), + get: sm => sm.getLspDiagnosticsOnWrite(), set: (sm, v) => sm.setLspDiagnosticsOnWrite(v), }, { @@ -835,7 +834,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "boolean", label: "Diagnostics on edit", description: "Return LSP diagnostics (errors/warnings) after editing code files", - get: (sm) => sm.getLspDiagnosticsOnEdit(), + get: sm => sm.getLspDiagnosticsOnEdit(), set: (sm, v) => sm.setLspDiagnosticsOnEdit(v), }, @@ -848,7 +847,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "boolean", label: "Exa enabled", description: "Master toggle for all Exa search tools", - get: (sm) => sm.getExaSettings().enabled, + get: sm => sm.getExaSettings().enabled, set: (sm, v) => sm.setExaEnabled(v), }, { @@ -857,7 +856,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "boolean", label: "Exa search", description: "Basic search, deep search, code search, crawl", - get: (sm) => sm.getExaSettings().enableSearch, + get: sm => sm.getExaSettings().enableSearch, set: (sm, v) => sm.setExaSearchEnabled(v), }, { @@ -866,7 +865,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "boolean", label: "Exa LinkedIn", description: "Search LinkedIn for people and companies", - get: (sm) => sm.getExaSettings().enableLinkedin, + get: sm => sm.getExaSettings().enableLinkedin, set: (sm, v) => sm.setExaLinkedinEnabled(v), }, { @@ -875,7 +874,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "boolean", label: "Exa company", description: "Comprehensive company research tool", - get: (sm) => sm.getExaSettings().enableCompany, + get: sm => sm.getExaSettings().enableCompany, set: (sm, v) => sm.setExaCompanyEnabled(v), }, { @@ -884,7 +883,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "boolean", label: "Exa researcher", description: "AI-powered deep research tasks", - get: (sm) => sm.getExaSettings().enableResearcher, + get: sm => sm.getExaSettings().enableResearcher, set: (sm, v) => sm.setExaResearcherEnabled(v), }, { @@ -893,7 +892,7 @@ export const SETTINGS_DEFS: SettingDef[] = [ type: "boolean", label: "Exa websets", description: "Webset management and enrichment tools", - get: (sm) => sm.getExaSettings().enableWebsets, + get: sm => sm.getExaSettings().enableWebsets, set: (sm, v) => sm.setExaWebsetsEnabled(v), }, ]; @@ -907,10 +906,10 @@ function getAllSettings(): SettingDef[] { /** Get settings for a specific tab */ export function getSettingsForTab(tab: string): SettingDef[] { - return getAllSettings().filter((def) => def.tab === tab); + return getAllSettings().filter(def => def.tab === tab); } /** Get a setting definition by id */ export function getSettingDef(id: string): SettingDef | undefined { - return getAllSettings().find((def) => def.id === id); + return getAllSettings().find(def => def.id === id); } diff --git a/packages/coding-agent/src/modes/components/settings-selector.ts b/packages/coding-agent/src/modes/components/settings-selector.ts index f3a2c9dc0..3daf52150 100644 --- a/packages/coding-agent/src/modes/components/settings-selector.ts +++ b/packages/coding-agent/src/modes/components/settings-selector.ts @@ -22,10 +22,10 @@ import { StatusLineSegmentEditorComponent } from "./status-line-segment-editor"; function getTabBarTheme(): TabBarTheme { return { - label: (text) => theme.bold(theme.fg("accent", text)), - activeTab: (text) => theme.bold(theme.bg("selectedBg", theme.fg("text", text))), - inactiveTab: (text) => theme.fg("muted", text), - hint: (text) => theme.fg("dim", text), + label: text => theme.bold(theme.fg("accent", text)), + activeTab: text => theme.bold(theme.bg("selectedBg", theme.fg("text", text))), + inactiveTab: text => theme.fg("muted", text), + hint: text => theme.fg("dim", text), }; } @@ -74,19 +74,19 @@ class SelectSubmenu extends Container { this.selectList = new SelectList(options, Math.min(options.length, 10), getSelectListTheme()); // Pre-select current value - const currentIndex = options.findIndex((o) => o.value === currentValue); + const currentIndex = options.findIndex(o => o.value === currentValue); if (currentIndex !== -1) { this.selectList.setSelectedIndex(currentIndex); } - this.selectList.onSelect = (item) => { + this.selectList.onSelect = item => { onSelect(item.value); }; this.selectList.onCancel = onCancel; if (onSelectionChange) { - this.selectList.onSelectionChange = (item) => { + this.selectList.onSelectionChange = item => { onSelectionChange(item.value); // Update preview after the preview callback has applied changes this.updatePreview(); @@ -307,12 +307,12 @@ export class SettingsSelectorComponent extends Container { // Special case: inject runtime options if (def.id === "thinkingLevel") { - options = this.context.availableThinkingLevels.map((level) => { - const baseOpt = def.getOptions(this.settingsManager).find((o) => o.value === level); + options = this.context.availableThinkingLevels.map(level => { + const baseOpt = def.getOptions(this.settingsManager).find(o => o.value === level); return baseOpt || { value: level, label: level }; }); } else if (def.id === "theme") { - options = this.context.availableThemes.map((t) => ({ value: t, label: t })); + options = this.context.availableThemes.map(t => ({ value: t, label: t })); } // Preview handlers @@ -323,7 +323,7 @@ export class SettingsSelectorComponent extends Container { onPreview = this.callbacks.onThemePreview; onPreviewCancel = () => this.callbacks.onThemePreview?.(currentValue); } else if (def.id === "statusLinePreset") { - onPreview = (value) => { + onPreview = value => { const presetDef = getPreset((value as StatusLineSettings["preset"]) ?? "default"); this.callbacks.onStatusLinePreview?.({ preset: value as StatusLineSettings["preset"], @@ -345,7 +345,7 @@ export class SettingsSelectorComponent extends Container { this.updateStatusPreview(); }; } else if (def.id === "statusLineSeparator") { - onPreview = (value) => { + onPreview = value => { this.callbacks.onStatusLinePreview?.({ separator: value as StatusLineSettings["separator"], }); @@ -370,7 +370,7 @@ export class SettingsSelectorComponent extends Container { def.description, options, currentValue, - (value) => { + value => { // Persist to SettingsManager def.set(this.settingsManager, value); // Notify for side effects @@ -454,7 +454,7 @@ export class SettingsSelectorComponent extends Container { 10, getSettingsListTheme(), (id, newValue) => { - const def = defs.find((d) => d.id === id); + const def = defs.find(d => d.id === id); if (!def) return; // Persist to SettingsManager based on type diff --git a/packages/coding-agent/src/modes/components/show-images-selector.ts b/packages/coding-agent/src/modes/components/show-images-selector.ts index 05b3073f4..740519f98 100644 --- a/packages/coding-agent/src/modes/components/show-images-selector.ts +++ b/packages/coding-agent/src/modes/components/show-images-selector.ts @@ -25,7 +25,7 @@ export class ShowImagesSelectorComponent extends Container { // Preselect current value this.selectList.setSelectedIndex(currentValue ? 0 : 1); - this.selectList.onSelect = (item) => { + this.selectList.onSelect = item => { onSelect(item.value === "yes"); }; diff --git a/packages/coding-agent/src/modes/components/status-line-segment-editor.ts b/packages/coding-agent/src/modes/components/status-line-segment-editor.ts index 3197baa1e..1f722eca2 100644 --- a/packages/coding-agent/src/modes/components/status-line-segment-editor.ts +++ b/packages/coding-agent/src/modes/components/status-line-segment-editor.ts @@ -8,7 +8,6 @@ * - Shift+J/K: Reorder segment within column * - Live preview shown in the actual status line above */ - import { Container, matchesKey } from "@oh-my-pi/pi-tui"; import type { StatusLineSegmentId } from "../../config/settings-manager"; import { theme } from "../../modes/theme/theme"; @@ -93,7 +92,7 @@ export class StatusLineSegmentEditorComponent extends Container { } private getSegmentsForColumn(column: Column): SegmentState[] { - return this.segments.filter((s) => s.column === column).sort((a, b) => a.order - b.order); + return this.segments.filter(s => s.column === column).sort((a, b) => a.order - b.order); } private getCurrentColumnSegments(): SegmentState[] { @@ -101,8 +100,8 @@ export class StatusLineSegmentEditorComponent extends Container { } private triggerPreview(): void { - const left = this.getSegmentsForColumn("left").map((s) => s.id); - const right = this.getSegmentsForColumn("right").map((s) => s.id); + const left = this.getSegmentsForColumn("left").map(s => s.id); + const right = this.getSegmentsForColumn("right").map(s => s.id); this.callbacks.onPreview?.(left, right); } @@ -236,8 +235,8 @@ export class StatusLineSegmentEditorComponent extends Container { this.triggerPreview(); } } else if (matchesKey(data, "enter") || matchesKey(data, "return") || data === "\n") { - const left = this.getSegmentsForColumn("left").map((s) => s.id); - const right = this.getSegmentsForColumn("right").map((s) => s.id); + const left = this.getSegmentsForColumn("left").map(s => s.id); + const right = this.getSegmentsForColumn("right").map(s => s.id); this.callbacks.onSave(left, right); } else if (matchesKey(data, "escape") || matchesKey(data, "esc")) { this.callbacks.onCancel(); diff --git a/packages/coding-agent/src/modes/components/status-line.ts b/packages/coding-agent/src/modes/components/status-line.ts index 401eafd63..c0c4c92f0 100644 --- a/packages/coding-agent/src/modes/components/status-line.ts +++ b/packages/coding-agent/src/modes/components/status-line.ts @@ -98,7 +98,7 @@ export class StatusLineComponent implements Component { this.gitWatcher = null; } - findGitHeadPath().then((gitHeadPath) => { + findGitHeadPath().then(gitHeadPath => { if (!gitHeadPath) return; try { @@ -132,7 +132,7 @@ export class StatusLineComponent implements Component { // Note: synchronous call to async function - will return undefined on first call // This is acceptable since it's a cached value that will update on next render - findGitHeadPath().then(async (gitHeadPath) => { + findGitHeadPath().then(async gitHeadPath => { if (!gitHeadPath) { this.cachedBranch = null; return; @@ -222,7 +222,7 @@ export class StatusLineComponent implements Component { const lastAssistantMessage = state.messages .slice() .reverse() - .find((m) => m.role === "assistant" && m.stopReason !== "aborted") as AssistantMessage | undefined; + .find(m => m.role === "assistant" && m.stopReason !== "aborted") as AssistantMessage | undefined; const contextTokens = lastAssistantMessage ? lastAssistantMessage.usage.input + diff --git a/packages/coding-agent/src/modes/components/theme-selector.ts b/packages/coding-agent/src/modes/components/theme-selector.ts index 00547de00..91bf99610 100644 --- a/packages/coding-agent/src/modes/components/theme-selector.ts +++ b/packages/coding-agent/src/modes/components/theme-selector.ts @@ -21,7 +21,7 @@ export class ThemeSelectorComponent extends Container { this.onPreview = onPreview; // Create select items from provided themes - const themeItems: SelectItem[] = themes.map((name) => ({ + const themeItems: SelectItem[] = themes.map(name => ({ value: name, label: name, description: name === currentTheme ? "(current)" : undefined, @@ -39,7 +39,7 @@ export class ThemeSelectorComponent extends Container { this.selectList.setSelectedIndex(currentIndex); } - this.selectList.onSelect = (item) => { + this.selectList.onSelect = item => { onSelect(item.value); }; @@ -47,7 +47,7 @@ export class ThemeSelectorComponent extends Container { onCancel(); }; - this.selectList.onSelectionChange = (item) => { + this.selectList.onSelectionChange = item => { this.onPreview(item.value); }; diff --git a/packages/coding-agent/src/modes/components/thinking-selector.ts b/packages/coding-agent/src/modes/components/thinking-selector.ts index f64450bd5..2d484eb8c 100644 --- a/packages/coding-agent/src/modes/components/thinking-selector.ts +++ b/packages/coding-agent/src/modes/components/thinking-selector.ts @@ -26,7 +26,7 @@ export class ThinkingSelectorComponent extends Container { ) { super(); - const thinkingLevels: SelectItem[] = availableLevels.map((level) => ({ + const thinkingLevels: SelectItem[] = availableLevels.map(level => ({ value: level, label: level, description: LEVEL_DESCRIPTIONS[level], @@ -39,12 +39,12 @@ export class ThinkingSelectorComponent extends Container { this.selectList = new SelectList(thinkingLevels, thinkingLevels.length, getSelectListTheme()); // Preselect current level - const currentIndex = thinkingLevels.findIndex((item) => item.value === currentLevel); + const currentIndex = thinkingLevels.findIndex(item => item.value === currentLevel); if (currentIndex !== -1) { this.selectList.setSelectedIndex(currentIndex); } - this.selectList.onSelect = (item) => { + this.selectList.onSelect = item => { onSelect(item.value as ThinkingLevel); }; diff --git a/packages/coding-agent/src/modes/components/todo-reminder.ts b/packages/coding-agent/src/modes/components/todo-reminder.ts index 6a3677efa..786116855 100644 --- a/packages/coding-agent/src/modes/components/todo-reminder.ts +++ b/packages/coding-agent/src/modes/components/todo-reminder.ts @@ -20,7 +20,7 @@ export class TodoReminderComponent extends Container { this.addChild(new Spacer(1)); - this.box = new Box(1, 1, (t) => theme.inverse(theme.fg("warning", t))); + this.box = new Box(1, 1, t => theme.inverse(theme.fg("warning", t))); this.addChild(this.box); this.rebuild(); @@ -36,7 +36,7 @@ export class TodoReminderComponent extends Container { this.box.addChild(new Text(header, 0, 0)); this.box.addChild(new Spacer(1)); - const todoList = this.todos.map((t) => ` ${theme.checkbox.unchecked} ${t.content}`).join("\n"); + const todoList = this.todos.map(t => ` ${theme.checkbox.unchecked} ${t.content}`).join("\n"); this.box.addChild(new Text(theme.italic(todoList), 0, 0)); } } diff --git a/packages/coding-agent/src/modes/components/tool-execution.ts b/packages/coding-agent/src/modes/components/tool-execution.ts index 8b05e735b..5500aae2e 100644 --- a/packages/coding-agent/src/modes/components/tool-execution.ts +++ b/packages/coding-agent/src/modes/components/tool-execution.ts @@ -36,7 +36,7 @@ function formatCompactValue(value: unknown, maxLength: number): string { } else if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") { rendered = String(value); } else if (Array.isArray(value)) { - const previewItems = value.slice(0, 3).map((item) => formatCompactValue(item, maxLength)); + const previewItems = value.slice(0, 3).map(item => formatCompactValue(item, maxLength)); rendered = `[${previewItems.join(", ")}${value.length > 3 ? ", ..." : ""}]`; } else if (typeof value === "object") { try { @@ -213,7 +213,7 @@ export class ToolExecutionComponent extends Container { computePatchDiff({ path, op, rename, diff }, this.cwd, { fuzzyThreshold: this.editFuzzyThreshold, allowFuzzy: this.editAllowFuzzy, - }).then((result) => { + }).then(result => { if (this.editDiffArgsKey === argsKey) { this.editDiffPreview = result; this.updateDisplay(); @@ -239,7 +239,7 @@ export class ToolExecutionComponent extends Container { this.editDiffArgsKey = argsKey; // Compute diff async - computeEditDiff(path, oldText, newText, this.cwd, true, all, this.editFuzzyThreshold).then((result) => { + computeEditDiff(path, oldText, newText, this.cwd, true, all, this.editFuzzyThreshold).then(result => { // Only update if args haven't changed since we started if (this.editDiffArgsKey === argsKey) { this.editDiffPreview = result; @@ -303,7 +303,7 @@ export class ToolExecutionComponent extends Container { // Convert async - catch errors from WASM processing const index = i; convertToPng(img.data, img.mimeType) - .then((converted) => { + .then(converted => { if (converted) { this.convertedImages.set(index, converted); this.updateDisplay(); @@ -629,7 +629,7 @@ export class ToolExecutionComponent extends Container { const displayLines = lines.slice(-maxLines); const remaining = lines.length - displayLines.length; text += ` ${theme.fg("dim", `(${lines.length} lines)`)}`; - text += `\n${displayLines.map((line) => theme.fg("toolOutput", line)).join("\n")}`; + text += `\n${displayLines.map(line => theme.fg("toolOutput", line)).join("\n")}`; if (remaining > 0) { text += theme.fg("dim", `\n${theme.format.ellipsis} (${remaining} earlier lines) (ctrl+o to expand)`); } diff --git a/packages/coding-agent/src/modes/components/tree-selector.ts b/packages/coding-agent/src/modes/components/tree-selector.ts index 8f768a5bf..33a3d7c4c 100644 --- a/packages/coding-agent/src/modes/components/tree-selector.ts +++ b/packages/coding-agent/src/modes/components/tree-selector.ts @@ -71,7 +71,7 @@ class TreeList implements Component { this.applyFilter(); // Start with current leaf selected - const leafIndex = this.filteredNodes.findIndex((n) => n.node.entry.id === currentLeafId); + const leafIndex = this.filteredNodes.findIndex(n => n.node.entry.id === currentLeafId); if (leafIndex !== -1) { this.selectedIndex = leafIndex; } else { @@ -236,7 +236,7 @@ class TreeList implements Component { const searchTokens = this.searchQuery.toLowerCase().split(/\s+/).filter(Boolean); - this.filteredNodes = this.flatNodes.filter((flatNode) => { + this.filteredNodes = this.flatNodes.filter(flatNode => { const entry = flatNode.node.entry; const isCurrentLeaf = entry.id === this.currentLeafId; @@ -289,7 +289,7 @@ class TreeList implements Component { // Apply search filter if (searchTokens.length > 0) { const nodeText = this.getSearchableText(flatNode.node).toLowerCase(); - return searchTokens.every((token) => nodeText.includes(token)); + return searchTokens.every(token => nodeText.includes(token)); } return true; @@ -297,7 +297,7 @@ class TreeList implements Component { // Try to preserve cursor on the same node after filtering if (previouslySelectedId) { - const newIndex = this.filteredNodes.findIndex((n) => n.node.entry.id === previouslySelectedId); + const newIndex = this.filteredNodes.findIndex(n => n.node.entry.id === previouslySelectedId); if (newIndex !== -1) { this.selectedIndex = newIndex; return; @@ -442,7 +442,7 @@ class TreeList implements Component { const posInLevel = i % 3; // Check if there's a gutter at this level - const gutter = flatNode.gutters.find((g) => g.position === level); + const gutter = flatNode.gutters.find(g => g.position === level); if (gutter) { if (posInLevel === 0) { prefixChars.push(gutter.show ? theme.tree.vertical : " "); @@ -537,7 +537,7 @@ class TreeList implements Component { ? entry.content : entry.content .filter((c): c is { type: "text"; text: string } => c.type === "text") - .map((c) => c.text) + .map(c => c.text) .join(""); result = theme.fg("customMessageLabel", `[${entry.customType}]: `) + normalize(content); break; @@ -713,7 +713,7 @@ class TreeList implements Component { this.onLabelEdit(selected.node.entry.id, selected.node.label); } } else { - const hasControlChars = [...keyData].some((ch) => { + const hasControlChars = [...keyData].some(ch => { const code = ch.charCodeAt(0); return code < 32 || code === 0x7f || (code >= 0x80 && code <= 0x9f); }); @@ -764,7 +764,7 @@ class LabelInput implements Component { const indent = " "; const availableWidth = width - indent.length; lines.push(truncateToWidth(`${indent}${theme.fg("muted", "Label (empty to remove):")}`, width)); - lines.push(...this.input.render(availableWidth).map((line) => truncateToWidth(`${indent}${line}`, width))); + lines.push(...this.input.render(availableWidth).map(line => truncateToWidth(`${indent}${line}`, width))); lines.push(truncateToWidth(`${indent}${theme.fg("dim", "enter: save esc: cancel")}`, width)); return lines; } diff --git a/packages/coding-agent/src/modes/components/ttsr-notification.ts b/packages/coding-agent/src/modes/components/ttsr-notification.ts index 4cf72263d..00958b25c 100644 --- a/packages/coding-agent/src/modes/components/ttsr-notification.ts +++ b/packages/coding-agent/src/modes/components/ttsr-notification.ts @@ -18,7 +18,7 @@ export class TtsrNotificationComponent extends Container { this.addChild(new Spacer(1)); // Use inverse warning color for yellow background effect - this.box = new Box(1, 1, (t) => theme.inverse(theme.fg("warning", t))); + this.box = new Box(1, 1, t => theme.inverse(theme.fg("warning", t))); this.addChild(this.box); this.rebuild(); @@ -39,7 +39,7 @@ export class TtsrNotificationComponent extends Container { this.box.clear(); // Build header: warning symbol + rule name + rewind icon - const ruleNames = this.rules.map((r) => theme.bold(r.name)).join(", "); + const ruleNames = this.rules.map(r => theme.bold(r.name)).join(", "); const label = this.rules.length === 1 ? "rule" : "rules"; const header = `${theme.icon.warning} Injecting ${label}: ${ruleNames}`; @@ -70,7 +70,7 @@ export class TtsrNotificationComponent extends Container { // Show expand hint if collapsed and there's more content if (!this._expanded) { - const hasMoreContent = this.rules.some((r) => { + const hasMoreContent = this.rules.some(r => { const desc = r.description || r.content; return desc && desc.split("\n").length > 2; }); diff --git a/packages/coding-agent/src/modes/components/visual-truncate.ts b/packages/coding-agent/src/modes/components/visual-truncate.ts index 3131fae95..d18444927 100644 --- a/packages/coding-agent/src/modes/components/visual-truncate.ts +++ b/packages/coding-agent/src/modes/components/visual-truncate.ts @@ -2,7 +2,6 @@ * Shared utility for truncating text to visual lines (accounting for line wrapping). * Used by both tool-execution.ts and bash-execution.ts for consistent behavior. */ - import { Text } from "@oh-my-pi/pi-tui"; export interface VisualTruncateResult { diff --git a/packages/coding-agent/src/modes/components/welcome.ts b/packages/coding-agent/src/modes/components/welcome.ts index c414d756e..b783fc930 100644 --- a/packages/coding-agent/src/modes/components/welcome.ts +++ b/packages/coding-agent/src/modes/components/welcome.ts @@ -65,14 +65,14 @@ export class WelcomeComponent implements Component { const piLogo = ["▀████████████▀", " ╘███ ███ ", " ███ ███ ", " ███ ███ ", " ▄███▄ ▄███▄ "]; // Apply gradient to logo - const logoColored = piLogo.map((line) => this.gradientLine(line)); + const logoColored = piLogo.map(line => this.gradientLine(line)); // Left column - centered content const leftLines = [ "", this.centerText(theme.bold("Welcome back!"), leftCol), "", - ...logoColored.map((l) => this.centerText(l, leftCol)), + ...logoColored.map(l => this.centerText(l, leftCol)), "", this.centerText(theme.fg("muted", this.modelName), leftCol), this.centerText(theme.fg("borderMuted", this.providerName), leftCol), diff --git a/packages/coding-agent/src/modes/controllers/command-controller.ts b/packages/coding-agent/src/modes/controllers/command-controller.ts index 5360c5215..4d220d7d8 100644 --- a/packages/coding-agent/src/modes/controllers/command-controller.ts +++ b/packages/coding-agent/src/modes/controllers/command-controller.ts @@ -324,7 +324,7 @@ export class CommandController { allEntries.length > 0 ? allEntries .reverse() - .map((e) => e.content) + .map(e => e.content) .join("\n\n") : "No changelog entries found."; @@ -434,7 +434,7 @@ export class CommandController { }), "", "=== Agent messages (JSONL) ===", - ...this.ctx.session.messages.map((msg) => JSON.stringify(msg)), + ...this.ctx.session.messages.map(msg => JSON.stringify(msg)), "", ].join("\n"); @@ -477,7 +477,7 @@ export class CommandController { try { const result = await this.ctx.session.executeBash( command, - (chunk) => { + chunk => { if (this.ctx.bashComponent) { this.ctx.bashComponent.appendOutput(chunk); this.ctx.ui.requestRender(); @@ -519,7 +519,7 @@ export class CommandController { try { const result = await this.ctx.session.executePython( code, - (chunk) => { + chunk => { if (this.ctx.pythonComponent) { this.ctx.pythonComponent.appendOutput(chunk); this.ctx.ui.requestRender(); @@ -548,7 +548,7 @@ export class CommandController { async handleCompactCommand(customInstructions?: string): Promise { const entries = this.ctx.sessionManager.getEntries(); - const messageCount = entries.filter((e) => e.type === "message").length; + const messageCount = entries.filter(e => e.type === "message").length; if (messageCount < 2) { this.ctx.showWarning("Nothing to compact (no messages yet)"); @@ -589,8 +589,8 @@ export class CommandController { const label = isAuto ? "Auto-compacting context... (esc to cancel)" : "Compacting context... (esc to cancel)"; const compactingLoader = new Loader( this.ctx.ui, - (spinner) => theme.fg("accent", spinner), - (text) => theme.fg("muted", text), + spinner => theme.fg("accent", spinner), + text => theme.fg("muted", text), label, getSymbolTheme().spinnerFrames, ); @@ -634,7 +634,7 @@ const COLUMN_WIDTH = BAR_WIDTH + 2; function formatProviderName(provider: string): string { return provider .split(/[-_]/g) - .map((part) => (part ? part[0].toUpperCase() + part.slice(1) : "")) + .map(part => (part ? part[0].toUpperCase() + part.slice(1) : "")) .join(" "); } @@ -687,8 +687,8 @@ function resolveFraction(limit: UsageLimit): number | undefined { function resolveProviderUsageTotal(reports: UsageReport[]): number { return reports - .flatMap((report) => report.limits) - .map((limit) => resolveFraction(limit) ?? 0) + .flatMap(report => report.limits) + .map(limit => resolveFraction(limit) ?? 0) .reduce((sum, value) => sum + value, 0); } @@ -740,9 +740,9 @@ function padColumn(text: string, width: number): string { } function resolveAggregateStatus(limits: UsageLimit[]): UsageLimit["status"] { - const hasOk = limits.some((limit) => limit.status === "ok"); - const hasWarning = limits.some((limit) => limit.status === "warning"); - const hasExhausted = limits.some((limit) => limit.status === "exhausted"); + const hasOk = limits.some(limit => limit.status === "ok"); + const hasWarning = limits.some(limit => limit.status === "warning"); + const hasExhausted = limits.some(limit => limit.status === "exhausted"); if (!hasOk && !hasWarning && !hasExhausted) return "unknown"; if (hasOk) { return hasWarning || hasExhausted ? "warning" : "ok"; @@ -761,12 +761,12 @@ function isZeroUsage(limit: UsageLimit): boolean { } function isZeroUsageGroup(limits: UsageLimit[]): boolean { - return limits.length > 0 && limits.every((limit) => isZeroUsage(limit)); + return limits.length > 0 && limits.every(limit => isZeroUsage(limit)); } function formatAggregateAmount(limits: UsageLimit[]): string { const fractions = limits - .map((limit) => resolveFraction(limit)) + .map(limit => resolveFraction(limit)) .filter((value): value is number => value !== undefined); if (fractions.length === limits.length && fractions.length > 0) { const sum = fractions.reduce((total, value) => total + value, 0); @@ -777,8 +777,8 @@ function formatAggregateAmount(limits: UsageLimit[]): string { } const amounts = limits - .map((limit) => limit.amount) - .filter((amount) => amount.used !== undefined && amount.limit !== undefined && amount.limit > 0); + .map(limit => limit.amount) + .filter(amount => amount.used !== undefined && amount.limit !== undefined && amount.limit > 0); if (amounts.length === limits.length && amounts.length > 0) { const totalUsed = amounts.reduce((sum, amount) => sum + (amount.used ?? 0), 0); const totalLimit = amounts.reduce((sum, amount) => sum + (amount.limit ?? 0), 0); @@ -793,11 +793,11 @@ function formatAggregateAmount(limits: UsageLimit[]): string { function resolveResetRange(limits: UsageLimit[], nowMs: number): string | null { const resets = limits - .map((limit) => limit.window?.resetInMs ?? undefined) + .map(limit => limit.window?.resetInMs ?? undefined) .filter((value): value is number => value !== undefined && Number.isFinite(value) && value > 0); if (resets.length === 0) { const absolute = limits - .map((limit) => limit.window?.resetsAt) + .map(limit => limit.window?.resetsAt) .filter((value): value is number => value !== undefined && Number.isFinite(value) && value > nowMs); if (absolute.length === 0) return null; const earliest = Math.min(...absolute); @@ -840,7 +840,7 @@ function renderUsageBar(limit: UsageLimit, uiTheme: typeof theme): string { function renderUsageReports(reports: UsageReport[], uiTheme: typeof theme, nowMs: number): string { const lines: string[] = []; - const latestFetchedAt = Math.max(...reports.map((report) => report.fetchedAt ?? 0)); + const latestFetchedAt = Math.max(...reports.map(report => report.fetchedAt ?? 0)); const headerSuffix = latestFetchedAt ? ` (${formatDuration(nowMs - latestFetchedAt)} ago)` : ""; lines.push(uiTheme.bold(uiTheme.fg("accent", `Usage${headerSuffix}`))); const grouped = new Map(); @@ -885,7 +885,7 @@ function renderUsageReports(reports: UsageReport[], uiTheme: typeof theme, nowMs } } - const providerAllZero = isZeroUsageGroup(Array.from(limitGroups.values()).flatMap((group) => group.limits)); + const providerAllZero = isZeroUsageGroup(Array.from(limitGroups.values()).flatMap(group => group.limits)); if (providerAllZero) { const providerTitle = `${resolveStatusIcon("ok", uiTheme)} ${uiTheme.fg("accent", `${providerName} (0%)`)}`; lines.push(uiTheme.bold(providerTitle)); @@ -907,8 +907,8 @@ function renderUsageReports(reports: UsageReport[], uiTheme: typeof theme, nowMs if (aFraction !== bFraction) return bFraction - aFraction; return a.index - b.index; }); - const sortedLimits = entries.map((entry) => entry.limit); - const sortedReports = entries.map((entry) => entry.report); + const sortedLimits = entries.map(entry => entry.limit); + const sortedReports = entries.map(entry => entry.report); const status = resolveAggregateStatus(sortedLimits); const statusIcon = resolveStatusIcon(status, uiTheme); @@ -931,13 +931,13 @@ function renderUsageReports(reports: UsageReport[], uiTheme: typeof theme, nowMs padColumn(formatAccountHeader(limit, sortedReports[index], index, nowMs), COLUMN_WIDTH), ); lines.push(` ${accountLabels.join(" ")}`.trimEnd()); - const bars = sortedLimits.map((limit) => padColumn(renderUsageBar(limit, uiTheme), COLUMN_WIDTH)); + const bars = sortedLimits.map(limit => padColumn(renderUsageBar(limit, uiTheme), COLUMN_WIDTH)); lines.push(` ${bars.join(" ")} ${formatAggregateAmount(sortedLimits)}`.trimEnd()); const resetText = sortedLimits.length <= 1 ? resolveResetRange(sortedLimits, nowMs) : null; if (resetText) { lines.push(` ${uiTheme.fg("dim", resetText)}`.trimEnd()); } - const notes = sortedLimits.flatMap((limit) => limit.notes ?? []); + const notes = sortedLimits.flatMap(limit => limit.notes ?? []); if (notes.length > 0) { lines.push(` ${uiTheme.fg("dim", notes.join(" • "))}`.trimEnd()); } diff --git a/packages/coding-agent/src/modes/controllers/event-controller.ts b/packages/coding-agent/src/modes/controllers/event-controller.ts index 6ba0e0db9..820466766 100644 --- a/packages/coding-agent/src/modes/controllers/event-controller.ts +++ b/packages/coding-agent/src/modes/controllers/event-controller.ts @@ -62,8 +62,8 @@ export class EventController { this.ctx.statusContainer.clear(); this.ctx.loadingAnimation = new Loader( this.ctx.ui, - (spinner) => theme.fg("accent", spinner), - (text) => theme.fg("muted", text), + spinner => theme.fg("accent", spinner), + text => theme.fg("muted", text), `Working${theme.format.ellipsis} (esc to interrupt)`, getSymbolTheme().spinnerFrames, ); @@ -107,7 +107,7 @@ export class EventController { this.ctx.streamingComponent.updateContent(this.ctx.streamingMessage); const thinkingCount = this.ctx.streamingMessage.content.filter( - (content) => content.type === "thinking" && content.thinking.trim(), + content => content.type === "thinking" && content.thinking.trim(), ).length; if (thinkingCount > this.lastThinkingCount) { this.resetReadGroup(); @@ -273,8 +273,8 @@ export class EventController { const reasonText = event.reason === "overflow" ? "Context overflow detected, " : ""; this.ctx.autoCompactionLoader = new Loader( this.ctx.ui, - (spinner) => theme.fg("accent", spinner), - (text) => theme.fg("muted", text), + spinner => theme.fg("accent", spinner), + text => theme.fg("muted", text), `${reasonText}Auto-compacting${theme.format.ellipsis} (esc to cancel)`, getSymbolTheme().spinnerFrames, ); @@ -323,8 +323,8 @@ export class EventController { const delaySeconds = Math.round(event.delayMs / 1000); this.ctx.retryLoader = new Loader( this.ctx.ui, - (spinner) => theme.fg("warning", spinner), - (text) => theme.fg("muted", text), + spinner => theme.fg("warning", spinner), + text => theme.fg("muted", text), `Retrying (${event.attempt}/${event.maxAttempts}) in ${delaySeconds}s${theme.format.ellipsis} (esc to cancel)`, getSymbolTheme().spinnerFrames, ); diff --git a/packages/coding-agent/src/modes/controllers/extension-ui-controller.ts b/packages/coding-agent/src/modes/controllers/extension-ui-controller.ts index a3e2d00d2..2ef36a91d 100644 --- a/packages/coding-agent/src/modes/controllers/extension-ui-controller.ts +++ b/packages/coding-agent/src/modes/controllers/extension-ui-controller.ts @@ -31,17 +31,17 @@ export class ExtensionUiController { notify: (message, type) => this.showHookNotify(message, type), setStatus: (key, text) => this.setHookStatus(key, text), setWidget: (key, content) => this.setHookWidget(key, content), - setTitle: (title) => setTerminalTitle(title), + setTitle: title => setTerminalTitle(title), custom: (factory, _options) => this.showHookCustom(factory), - setEditorText: (text) => this.ctx.editor.setText(text), + setEditorText: text => this.ctx.editor.setText(text), getEditorText: () => this.ctx.editor.getText(), editor: (title, prefill) => this.showHookEditor(title, prefill), get theme() { return theme; }, - getAllThemes: async () => (await getAvailableThemesWithPaths()).map((t) => ({ name: t.name, path: t.path })), - getTheme: (name) => getThemeByName(name), - setTheme: async (themeArg) => { + getAllThemes: async () => (await getAvailableThemesWithPaths()).map(t => ({ name: t.name, path: t.path })), + getTheme: name => getThemeByName(name), + setTheme: async themeArg => { if (typeof themeArg === "string") { return await setTheme(themeArg, true); } @@ -92,15 +92,15 @@ export class ExtensionUiController { }, getActiveTools: () => this.ctx.session.getActiveToolNames(), getAllTools: () => this.ctx.session.getAllToolNames(), - setActiveTools: (toolNames) => this.ctx.session.setActiveToolsByName(toolNames), - setModel: async (model) => { + setActiveTools: toolNames => this.ctx.session.setActiveToolsByName(toolNames), + setModel: async model => { const key = await this.ctx.session.modelRegistry.getApiKey(model); if (!key) return false; await this.ctx.session.setModel(model); return true; }, getThinkingLevel: () => this.ctx.session.thinkingLevel, - setThinkingLevel: (level) => this.ctx.session.setThinkingLevel(level), + setThinkingLevel: level => this.ctx.session.setThinkingLevel(level), }; const contextActions: ExtensionContextActions = { getModel: () => this.ctx.session.model, @@ -111,7 +111,7 @@ export class ExtensionUiController { // Signal shutdown request (will be handled by main loop) }, getContextUsage: () => this.ctx.session.getContextUsage(), - compact: async (instructionsOrOptions) => { + compact: async instructionsOrOptions => { const instructions = typeof instructionsOrOptions === "string" ? instructionsOrOptions : undefined; const options = instructionsOrOptions && typeof instructionsOrOptions === "object" ? instructionsOrOptions : undefined; @@ -121,7 +121,7 @@ export class ExtensionUiController { const commandActions: ExtensionCommandContextActions = { getContextUsage: () => this.ctx.session.getContextUsage(), waitForIdle: () => this.ctx.session.agent.waitForIdle(), - newSession: async (options) => { + newSession: async options => { // Stop any loading animation if (this.ctx.loadingAnimation) { this.ctx.loadingAnimation.stop(); @@ -157,7 +157,7 @@ export class ExtensionUiController { return { cancelled: false }; }, - branch: async (entryId) => { + branch: async entryId => { const result = await this.ctx.session.branch(entryId); if (result.cancelled) { return { cancelled: true }; @@ -189,7 +189,7 @@ export class ExtensionUiController { return { cancelled: false }; }, - compact: async (instructionsOrOptions) => { + compact: async instructionsOrOptions => { const instructions = typeof instructionsOrOptions === "string" ? instructionsOrOptions : undefined; const options = instructionsOrOptions && typeof instructionsOrOptions === "object" ? instructionsOrOptions : undefined; @@ -261,15 +261,15 @@ export class ExtensionUiController { }, getActiveTools: () => this.ctx.session.getActiveToolNames(), getAllTools: () => this.ctx.session.getAllToolNames(), - setActiveTools: (toolNames) => this.ctx.session.setActiveToolsByName(toolNames), - setModel: async (model) => { + setActiveTools: toolNames => this.ctx.session.setActiveToolsByName(toolNames), + setModel: async model => { const key = await this.ctx.session.modelRegistry.getApiKey(model); if (!key) return false; await this.ctx.session.setModel(model); return true; }, getThinkingLevel: () => this.ctx.session.thinkingLevel, - setThinkingLevel: (level) => this.ctx.session.setThinkingLevel(level), + setThinkingLevel: level => this.ctx.session.setThinkingLevel(level), }; const contextActions: ExtensionContextActions = { getModel: () => this.ctx.session.model, @@ -280,7 +280,7 @@ export class ExtensionUiController { // Signal shutdown request (will be handled by main loop) }, getContextUsage: () => this.ctx.session.getContextUsage(), - compact: async (instructionsOrOptions) => { + compact: async instructionsOrOptions => { const instructions = typeof instructionsOrOptions === "string" ? instructionsOrOptions : undefined; const options = instructionsOrOptions && typeof instructionsOrOptions === "object" ? instructionsOrOptions : undefined; @@ -290,7 +290,7 @@ export class ExtensionUiController { const commandActions: ExtensionCommandContextActions = { getContextUsage: () => this.ctx.session.getContextUsage(), waitForIdle: () => this.ctx.session.agent.waitForIdle(), - newSession: async (options) => { + newSession: async options => { if (this.ctx.isBackgrounded) { return { cancelled: true }; } @@ -329,7 +329,7 @@ export class ExtensionUiController { return { cancelled: false }; }, - branch: async (entryId) => { + branch: async entryId => { if (this.ctx.isBackgrounded) { return { cancelled: true }; } @@ -367,7 +367,7 @@ export class ExtensionUiController { return { cancelled: false }; }, - compact: async (instructionsOrOptions) => { + compact: async instructionsOrOptions => { const instructions = typeof instructionsOrOptions === "string" ? instructionsOrOptions : undefined; const options = instructionsOrOptions && typeof instructionsOrOptions === "object" ? instructionsOrOptions : undefined; @@ -425,7 +425,7 @@ export class ExtensionUiController { await registeredTool.definition.onSession(event, { ui: uiContext, getContextUsage: () => this.ctx.session.getContextUsage(), - compact: async (instructionsOrOptions) => { + compact: async instructionsOrOptions => { const instructions = typeof instructionsOrOptions === "string" ? instructionsOrOptions : undefined; const options = instructionsOrOptions && typeof instructionsOrOptions === "object" @@ -487,7 +487,7 @@ export class ExtensionUiController { this.ctx.hookSelector = new HookSelectorComponent( title, options, - (option) => { + option => { this.hideHookSelector(); resolve(option); }, @@ -532,7 +532,7 @@ export class ExtensionUiController { this.ctx.hookInput = new HookInputComponent( title, placeholder, - (value) => { + value => { this.hideHookInput(); resolve(value); }, @@ -569,7 +569,7 @@ export class ExtensionUiController { this.ctx.ui, title, prefill, - (value) => { + value => { this.hideHookEditor(); resolve(value); }, @@ -637,7 +637,7 @@ export class ExtensionUiController { resolve(result); }; - Promise.try(() => factory(this.ctx.ui, theme, keybindings, close)).then((c) => { + Promise.try(() => factory(this.ctx.ui, theme, keybindings, close)).then(c => { component = c; this.ctx.editorContainer.clear(); this.ctx.editorContainer.addChild(component); diff --git a/packages/coding-agent/src/modes/controllers/input-controller.ts b/packages/coding-agent/src/modes/controllers/input-controller.ts index c8da7ac7e..b4fe7be26 100644 --- a/packages/coding-agent/src/modes/controllers/input-controller.ts +++ b/packages/coding-agent/src/modes/controllers/input-controller.ts @@ -385,7 +385,7 @@ export class InputController { const registry = this.ctx.session.modelRegistry; const smolModel = this.ctx.settingsManager.getModelRole("smol"); generateSessionTitle(text, registry, smolModel, this.ctx.session.sessionId) - .then(async (title) => { + .then(async title => { if (title) { await this.ctx.sessionManager.setSessionTitle(title); setTerminalTitle(`π: ${title}`); @@ -454,7 +454,7 @@ export class InputController { } const queuedText = allQueued.join("\n\n"); const currentText = options?.currentText ?? this.ctx.editor.getText(); - const combinedText = [queuedText, currentText].filter((t) => t.trim()).join("\n\n"); + const combinedText = [queuedText, currentText].filter(t => t.trim()).join("\n\n"); this.ctx.editor.setText(combinedText); this.ctx.updatePendingMessagesDisplay(); if (options?.abort) { @@ -585,7 +585,7 @@ export class InputController { const tempLabel = options?.temporary ? " (temporary)" : ""; const cycleSeparator = theme.fg("dim", " > "); const cycleLabel = roleOrder - .map((role) => { + .map(role => { if (role === result.role) { return theme.bold(theme.fg("accent", role)); } @@ -679,7 +679,7 @@ export class InputController { const child = spawn(editor, [...editorArgs, tmpFile], { stdio }); const exitCode = await new Promise((resolve, reject) => { child.once("exit", (code, signal) => resolve(code ?? (signal ? -1 : 0))); - child.once("error", (error) => reject(error)); + child.once("error", error => reject(error)); }); // On successful exit (exitCode 0), replace editor content diff --git a/packages/coding-agent/src/modes/controllers/selector-controller.ts b/packages/coding-agent/src/modes/controllers/selector-controller.ts index ed6be23c6..a1ca75744 100644 --- a/packages/coding-agent/src/modes/controllers/selector-controller.ts +++ b/packages/coding-agent/src/modes/controllers/selector-controller.ts @@ -47,8 +47,8 @@ export class SelectorController { } showSettingsSelector(): void { - getAvailableThemes().then((availableThemes) => { - this.showSelector((done) => { + getAvailableThemes().then(availableThemes => { + this.showSelector(done => { const selector = new SettingsSelectorComponent( this.ctx.settingsManager, { @@ -59,15 +59,15 @@ export class SelectorController { }, { onChange: (id, value) => this.handleSettingChange(id, value), - onThemePreview: (themeName) => { - setTheme(themeName, true).then((result) => { + onThemePreview: themeName => { + setTheme(themeName, true).then(result => { if (result.success) { this.ctx.ui.invalidate(); this.ctx.ui.requestRender(); } }); }, - onStatusLinePreview: (settings) => { + onStatusLinePreview: settings => { // Update status line with preview settings const currentSettings = this.ctx.settingsManager.getStatusLineSettings(); this.ctx.statusLine.updateSettings({ ...currentSettings, ...settings }); @@ -100,10 +100,10 @@ export class SelectorController { const historyStorage = this.ctx.historyStorage; if (!historyStorage) return; - this.showSelector((done) => { + this.showSelector(done => { const component = new HistorySearchComponent( historyStorage, - (prompt) => { + prompt => { done(); this.ctx.editor.setText(prompt); this.ctx.ui.requestRender(); @@ -127,7 +127,7 @@ export class SelectorController { this.ctx.settingsManager, this.ctx.ui.terminal.rows, ); - this.showSelector((done) => { + this.showSelector(done => { dashboard.onClose = () => { done(); this.ctx.ui.requestRender(); @@ -193,7 +193,7 @@ export class SelectorController { this.ctx.rebuildChatFromMessages(); break; case "theme": { - setTheme(value as string, true).then((result) => { + setTheme(value as string, true).then(result => { this.ctx.statusLine.invalidate(); this.ctx.updateEditorTopBorder(); this.ctx.ui.invalidate(); @@ -251,7 +251,7 @@ export class SelectorController { } showModelSelector(options?: { temporaryOnly?: boolean }): void { - this.showSelector((done) => { + this.showSelector(done => { const selector = new ModelSelectorComponent( this.ctx.ui, this.ctx.session.model, @@ -303,10 +303,10 @@ export class SelectorController { return; } - this.showSelector((done) => { + this.showSelector(done => { const selector = new UserMessageSelectorComponent( - userMessages.map((m) => ({ id: m.entryId, text: m.text })), - async (entryId) => { + userMessages.map(m => ({ id: m.entryId, text: m.text })), + async entryId => { const result = await this.ctx.session.branch(entryId); if (result.cancelled) { // Hook cancelled the branch @@ -348,12 +348,12 @@ export class SelectorController { return; } - this.showSelector((done) => { + this.showSelector(done => { const selector = new TreeSelectorComponent( tree, visibleLeafId, this.ctx.ui.terminal.rows, - async (entryId) => { + async entryId => { // Selecting the visible leaf is a no-op (already there) if (entryId === visibleLeafId) { done(); @@ -408,8 +408,8 @@ export class SelectorController { this.ctx.chatContainer.addChild(new Spacer(1)); summaryLoader = new Loader( this.ctx.ui, - (spinner) => theme.fg("accent", spinner), - (text) => theme.fg("muted", text), + spinner => theme.fg("accent", spinner), + text => theme.fg("muted", text), "Summarizing branch... (esc to cancel)", getSymbolTheme().spinnerFrames, ); @@ -470,10 +470,10 @@ export class SelectorController { this.ctx.sessionManager.getCwd(), this.ctx.sessionManager.getSessionDir(), ); - this.showSelector((done) => { + this.showSelector(done => { const selector = new SessionSelectorComponent( sessions, - async (sessionPath) => { + async sessionPath => { done(); await this.handleResumeSession(sessionPath); }, @@ -517,14 +517,14 @@ export class SelectorController { async showOAuthSelector(mode: "login" | "logout"): Promise { if (mode === "logout") { const providers = this.ctx.session.modelRegistry.authStorage.list(); - const loggedInProviders = providers.filter((p) => this.ctx.session.modelRegistry.authStorage.hasOAuth(p)); + const loggedInProviders = providers.filter(p => this.ctx.session.modelRegistry.authStorage.hasOAuth(p)); if (loggedInProviders.length === 0) { this.ctx.showStatus("No OAuth providers logged in. Use /login first."); return; } } - this.showSelector((done) => { + this.showSelector(done => { const selector = new OAuthSelectorComponent( mode, this.ctx.session.modelRegistry.authStorage, @@ -558,7 +558,7 @@ export class SelectorController { } this.ctx.ui.requestRender(); - return new Promise((resolve) => { + return new Promise(resolve => { const codeInput = new Input(); codeInput.onSubmit = () => { const code = codeInput.getValue(); diff --git a/packages/coding-agent/src/modes/index.ts b/packages/coding-agent/src/modes/index.ts index 23ff796ea..677b2cab3 100644 --- a/packages/coding-agent/src/modes/index.ts +++ b/packages/coding-agent/src/modes/index.ts @@ -1,3 +1,6 @@ +import { emergencyTerminalRestore } from "@oh-my-pi/pi-tui"; +import { postmortem } from "@oh-my-pi/pi-utils"; + /** * Run modes for the coding agent. */ @@ -7,9 +10,6 @@ export { type ModelInfo, RpcClient, type RpcClientOptions, type RpcEventListener export { runRpcMode } from "./rpc/rpc-mode"; export type { RpcCommand, RpcResponse, RpcSessionState } from "./rpc/rpc-types"; -import { emergencyTerminalRestore } from "@oh-my-pi/pi-tui"; -import { postmortem } from "@oh-my-pi/pi-utils"; - postmortem.register("terminal-restore", () => { emergencyTerminalRestore(); }); diff --git a/packages/coding-agent/src/modes/interactive-mode.ts b/packages/coding-agent/src/modes/interactive-mode.ts index c487a56f4..2fdb06c9a 100644 --- a/packages/coding-agent/src/modes/interactive-mode.ts +++ b/packages/coding-agent/src/modes/interactive-mode.ts @@ -2,7 +2,6 @@ * Interactive mode for the coding agent. * Handles TUI rendering and user interaction, delegating business logic to AgentSession. */ - import * as path from "node:path"; import type { AgentMessage } from "@oh-my-pi/pi-agent-core"; import type { AssistantMessage, ImageContent, Message, UsageReport } from "@oh-my-pi/pi-ai"; @@ -207,14 +206,14 @@ export class InteractiveMode implements InteractiveModeContext { ]; // Convert hook commands to SlashCommand format - const hookCommands: SlashCommand[] = (this.session.extensionRunner?.getRegisteredCommands() ?? []).map((cmd) => ({ + const hookCommands: SlashCommand[] = (this.session.extensionRunner?.getRegisteredCommands() ?? []).map(cmd => ({ name: cmd.name, description: cmd.description ?? "(hook command)", getArgumentCompletions: cmd.getArgumentCompletions, })); // Convert custom commands (TypeScript) to SlashCommand format - const customCommands: SlashCommand[] = this.session.customCommands.map((loaded) => ({ + const customCommands: SlashCommand[] = this.session.customCommands.map(loaded => ({ name: loaded.command.name, description: `${loaded.command.description} (${loaded.source})`, })); @@ -250,8 +249,8 @@ export class InteractiveMode implements InteractiveModeContext { // Load and convert file commands to SlashCommand format (async) const fileCommands = await loadSlashCommands({ cwd: process.cwd() }); - this.fileSlashCommands = new Set(fileCommands.map((cmd) => cmd.name)); - const fileSlashCommands: SlashCommand[] = fileCommands.map((cmd) => ({ + this.fileSlashCommands = new Set(fileCommands.map(cmd => cmd.name)); + const fileSlashCommands: SlashCommand[] = fileCommands.map(cmd => ({ name: cmd.name, description: cmd.description, })); @@ -268,14 +267,14 @@ export class InteractiveMode implements InteractiveModeContext { const providerName = this.session.model?.provider ?? "Unknown"; // Get recent sessions - const recentSessions = (await getRecentSessions(this.sessionManager.getSessionDir())).map((s) => ({ + const recentSessions = (await getRecentSessions(this.sessionManager.getSessionDir())).map(s => ({ name: s.name, timeAgo: s.timeAgo, })); // Convert LSP servers to welcome format const lspServerInfo = - this.lspServers?.map((s) => ({ + this.lspServers?.map(s => ({ name: s.name, status: s.status as "ready" | "error" | "connecting", fileTypes: s.fileTypes, @@ -364,7 +363,7 @@ export class InteractiveMode implements InteractiveModeContext { async getUserInput(): Promise<{ text: string; images?: ImageContent[] }> { const { promise, resolve } = Promise.withResolvers<{ text: string; images?: ImageContent[] }>(); - this.onInputCallback = (input) => { + this.onInputCallback = input => { this.onInputCallback = undefined; resolve(input); }; diff --git a/packages/coding-agent/src/modes/print-mode.ts b/packages/coding-agent/src/modes/print-mode.ts index 3f5e18738..339d6b61e 100644 --- a/packages/coding-agent/src/modes/print-mode.ts +++ b/packages/coding-agent/src/modes/print-mode.ts @@ -5,7 +5,6 @@ * - `omp -p "prompt"` - text output * - `omp --mode json "prompt"` - JSON event stream */ - import type { AssistantMessage, ImageContent } from "@oh-my-pi/pi-ai"; import type { AgentSession } from "../session/agent-session"; @@ -44,12 +43,12 @@ export async function runPrintMode(session: AgentSession, options: PrintModeOpti // ExtensionActions { sendMessage: (message, options) => { - session.sendCustomMessage(message, options).catch((e) => { + session.sendCustomMessage(message, options).catch(e => { process.stderr.write(`Extension sendMessage failed: ${e instanceof Error ? e.message : String(e)}\n`); }); }, sendUserMessage: (content, options) => { - session.sendUserMessage(content, options).catch((e) => { + session.sendUserMessage(content, options).catch(e => { process.stderr.write( `Extension sendUserMessage failed: ${e instanceof Error ? e.message : String(e)}\n`, ); @@ -64,14 +63,14 @@ export async function runPrintMode(session: AgentSession, options: PrintModeOpti getActiveTools: () => session.getActiveToolNames(), getAllTools: () => session.getAllToolNames(), setActiveTools: (toolNames: string[]) => session.setActiveToolsByName(toolNames), - setModel: async (model) => { + setModel: async model => { const key = await session.modelRegistry.getApiKey(model); if (!key) return false; await session.setModel(model); return true; }, getThinkingLevel: () => session.thinkingLevel, - setThinkingLevel: (level) => session.setThinkingLevel(level), + setThinkingLevel: level => session.setThinkingLevel(level), }, // ExtensionContextActions { @@ -81,7 +80,7 @@ export async function runPrintMode(session: AgentSession, options: PrintModeOpti hasPendingMessages: () => session.queuedMessageCount > 0, shutdown: () => {}, getContextUsage: () => session.getContextUsage(), - compact: async (instructionsOrOptions) => { + compact: async instructionsOrOptions => { const instructions = typeof instructionsOrOptions === "string" ? instructionsOrOptions : undefined; const options = instructionsOrOptions && typeof instructionsOrOptions === "object" @@ -94,14 +93,14 @@ export async function runPrintMode(session: AgentSession, options: PrintModeOpti { getContextUsage: () => session.getContextUsage(), waitForIdle: () => session.agent.waitForIdle(), - newSession: async (options) => { + newSession: async options => { const success = await session.newSession({ parentSession: options?.parentSession }); if (success && options?.setup) { await options.setup(session.sessionManager); } return { cancelled: !success }; }, - branch: async (entryId) => { + branch: async entryId => { const result = await session.branch(entryId); return { cancelled: result.cancelled }; }, @@ -109,7 +108,7 @@ export async function runPrintMode(session: AgentSession, options: PrintModeOpti const result = await session.navigateTree(targetId, { summarize: options?.summarize }); return { cancelled: result.cancelled }; }, - compact: async (instructionsOrOptions) => { + compact: async instructionsOrOptions => { const instructions = typeof instructionsOrOptions === "string" ? instructionsOrOptions : undefined; const options = instructionsOrOptions && typeof instructionsOrOptions === "object" @@ -120,7 +119,7 @@ export async function runPrintMode(session: AgentSession, options: PrintModeOpti }, // No UI context ); - extensionRunner.onError((err) => { + extensionRunner.onError(err => { process.stderr.write(`Extension error (${err.extensionPath}): ${err.error}\n`); }); // Emit session_start event @@ -130,7 +129,7 @@ export async function runPrintMode(session: AgentSession, options: PrintModeOpti } // Always subscribe to enable session persistence via _handleAgentEvent - session.subscribe((event) => { + session.subscribe(event => { // In JSON mode, output all events if (mode === "json") { process.stdout.write(`${JSON.stringify(event)}\n`); @@ -173,7 +172,7 @@ export async function runPrintMode(session: AgentSession, options: PrintModeOpti // Ensure stdout is fully flushed before returning // This prevents race conditions where the process exits before all output is written await new Promise((resolve, reject) => { - process.stdout.write("", (err) => { + process.stdout.write("", err => { if (err) reject(err); else resolve(); }); diff --git a/packages/coding-agent/src/modes/rpc/rpc-client.ts b/packages/coding-agent/src/modes/rpc/rpc-client.ts index 0e9a19f26..7e368f728 100644 --- a/packages/coding-agent/src/modes/rpc/rpc-client.ts +++ b/packages/coding-agent/src/modes/rpc/rpc-client.ts @@ -3,7 +3,6 @@ * * Spawns the agent in RPC mode and provides a typed API for all operations. */ - import type { AgentEvent, AgentMessage, ThinkingLevel } from "@oh-my-pi/pi-agent-core"; import type { ImageContent } from "@oh-my-pi/pi-ai"; import { createSanitizerStream, createSplitterStream, createTextDecoderStream, ptree } from "@oh-my-pi/pi-utils"; @@ -372,7 +371,7 @@ export class RpcClient { waitForIdle(timeout = 60000): Promise { const { promise, resolve, reject } = Promise.withResolvers(); let settled = false; - const unsubscribe = this.onEvent((event) => { + const unsubscribe = this.onEvent(event => { if (event.type === "agent_end") { settled = true; unsubscribe(); @@ -397,7 +396,7 @@ export class RpcClient { const { promise, resolve, reject } = Promise.withResolvers(); const events: AgentEvent[] = []; let settled = false; - const unsubscribe = this.onEvent((event) => { + const unsubscribe = this.onEvent(event => { events.push(event); if (event.type === "agent_end") { settled = true; @@ -470,12 +469,12 @@ export class RpcClient { })(); this.pendingRequests.set(id, { - resolve: (response) => { + resolve: response => { if (settled) return; settled = true; resolve(response); }, - reject: (error) => { + reject: error => { if (settled) return; settled = true; reject(error); diff --git a/packages/coding-agent/src/modes/rpc/rpc-mode.ts b/packages/coding-agent/src/modes/rpc/rpc-mode.ts index 6293aba58..a508d3f55 100644 --- a/packages/coding-agent/src/modes/rpc/rpc-mode.ts +++ b/packages/coding-agent/src/modes/rpc/rpc-mode.ts @@ -10,7 +10,6 @@ * - Events: AgentSessionEvent objects streamed as they occur * - Extension UI: Extension UI requests are emitted, client responds with extension_ui_response */ - import { readLines } from "@oh-my-pi/pi-utils"; import { nanoid } from "nanoid"; import type { ExtensionUIContext, ExtensionUIDialogOptions } from "../../extensibility/extensions"; @@ -125,7 +124,7 @@ export async function runRpcMode(session: AgentSession): Promise { dialogOptions, undefined, { method: "select", title, options, timeout: dialogOptions?.timeout }, - (response) => + response => "cancelled" in response && response.cancelled ? undefined : "value" in response @@ -139,7 +138,7 @@ export async function runRpcMode(session: AgentSession): Promise { dialogOptions, false, { method: "confirm", title, message, timeout: dialogOptions?.timeout }, - (response) => + response => "cancelled" in response && response.cancelled ? false : "confirmed" in response @@ -157,7 +156,7 @@ export async function runRpcMode(session: AgentSession): Promise { dialogOptions, undefined, { method: "input", title, placeholder, timeout: dialogOptions?.timeout }, - (response) => + response => "cancelled" in response && response.cancelled ? undefined : "value" in response @@ -296,12 +295,12 @@ export async function runRpcMode(session: AgentSession): Promise { // ExtensionActions { sendMessage: (message, options) => { - session.sendCustomMessage(message, options).catch((e) => { + session.sendCustomMessage(message, options).catch(e => { output(error(undefined, "extension_send", e.message)); }); }, sendUserMessage: (content, options) => { - session.sendUserMessage(content, options).catch((e) => { + session.sendUserMessage(content, options).catch(e => { output(error(undefined, "extension_send_user", e.message)); }); }, @@ -314,14 +313,14 @@ export async function runRpcMode(session: AgentSession): Promise { getActiveTools: () => session.getActiveToolNames(), getAllTools: () => session.getAllToolNames(), setActiveTools: (toolNames: string[]) => session.setActiveToolsByName(toolNames), - setModel: async (model) => { + setModel: async model => { const key = await session.modelRegistry.getApiKey(model); if (!key) return false; await session.setModel(model); return true; }, getThinkingLevel: () => session.thinkingLevel, - setThinkingLevel: (level) => session.setThinkingLevel(level), + setThinkingLevel: level => session.setThinkingLevel(level), }, // ExtensionContextActions { @@ -333,7 +332,7 @@ export async function runRpcMode(session: AgentSession): Promise { shutdownState.requested = true; }, getContextUsage: () => session.getContextUsage(), - compact: async (instructionsOrOptions) => { + compact: async instructionsOrOptions => { const instructions = typeof instructionsOrOptions === "string" ? instructionsOrOptions : undefined; const options = instructionsOrOptions && typeof instructionsOrOptions === "object" @@ -346,7 +345,7 @@ export async function runRpcMode(session: AgentSession): Promise { { getContextUsage: () => session.getContextUsage(), waitForIdle: () => session.agent.waitForIdle(), - newSession: async (options) => { + newSession: async options => { const success = await session.newSession({ parentSession: options?.parentSession }); // Note: setup callback runs but no UI feedback in RPC mode if (success && options?.setup) { @@ -354,7 +353,7 @@ export async function runRpcMode(session: AgentSession): Promise { } return { cancelled: !success }; }, - branch: async (entryId) => { + branch: async entryId => { const result = await session.branch(entryId); return { cancelled: result.cancelled }; }, @@ -362,7 +361,7 @@ export async function runRpcMode(session: AgentSession): Promise { const result = await session.navigateTree(targetId, { summarize: options?.summarize }); return { cancelled: result.cancelled }; }, - compact: async (instructionsOrOptions) => { + compact: async instructionsOrOptions => { const instructions = typeof instructionsOrOptions === "string" ? instructionsOrOptions : undefined; const options = instructionsOrOptions && typeof instructionsOrOptions === "object" @@ -373,7 +372,7 @@ export async function runRpcMode(session: AgentSession): Promise { }, new RpcExtensionUIContext(pendingExtensionRequests, output), ); - extensionRunner.onError((err) => { + extensionRunner.onError(err => { output({ type: "extension_error", extensionPath: err.extensionPath, event: err.event, error: err.error }); }); // Emit session_start event @@ -383,7 +382,7 @@ export async function runRpcMode(session: AgentSession): Promise { } // Output all agent events as JSON - session.subscribe((event) => { + session.subscribe(event => { output(event); }); @@ -405,7 +404,7 @@ export async function runRpcMode(session: AgentSession): Promise { images: command.images, streamingBehavior: command.streamingBehavior, }) - .catch((e) => output(error(id, "prompt", e.message))); + .catch(e => output(error(id, "prompt", e.message))); return success(id, "prompt"); } @@ -458,7 +457,7 @@ export async function runRpcMode(session: AgentSession): Promise { case "set_model": { const models = session.getAvailableModels(); - const model = models.find((m) => m.provider === command.provider && m.id === command.modelId); + const model = models.find(m => m.provider === command.provider && m.id === command.modelId); if (!model) { return error(id, "set_model", `Model not found: ${command.provider}/${command.modelId}`); } diff --git a/packages/coding-agent/src/modes/rpc/rpc-types.ts b/packages/coding-agent/src/modes/rpc/rpc-types.ts index 21191251f..786ecd979 100644 --- a/packages/coding-agent/src/modes/rpc/rpc-types.ts +++ b/packages/coding-agent/src/modes/rpc/rpc-types.ts @@ -4,7 +4,6 @@ * Commands are sent as JSON lines on stdin. * Responses and events are emitted as JSON lines on stdout. */ - import type { AgentMessage, ThinkingLevel } from "@oh-my-pi/pi-agent-core"; import type { ImageContent, Model } from "@oh-my-pi/pi-ai"; import type { BashResult } from "../../exec/bash-executor"; diff --git a/packages/coding-agent/src/modes/theme/mermaid-cache.ts b/packages/coding-agent/src/modes/theme/mermaid-cache.ts index 0499d5b6f..a3aa9b731 100644 --- a/packages/coding-agent/src/modes/theme/mermaid-cache.ts +++ b/packages/coding-agent/src/modes/theme/mermaid-cache.ts @@ -54,7 +54,7 @@ export async function prerenderMermaid( } promises.push( - promise.then((image) => { + promise.then(image => { pending.delete(hash); if (image) { cache.set(hash, image); @@ -66,7 +66,7 @@ export async function prerenderMermaid( } const results = await Promise.all(promises); - const newImages = results.some((added) => added); + const newImages = results.some(added => added); if (newImages && onRenderNeeded) { onRenderNeeded(); diff --git a/packages/coding-agent/src/modes/theme/theme.ts b/packages/coding-agent/src/modes/theme/theme.ts index 69ba359ef..456f6c0c4 100644 --- a/packages/coding-agent/src/modes/theme/theme.ts +++ b/packages/coding-agent/src/modes/theme/theme.ts @@ -1636,7 +1636,7 @@ export async function getAvailableThemesWithPaths(): Promise { for (const file of files) { if (file.endsWith(".json")) { const name = file.slice(0, -5); - if (!result.some((themeInfo) => themeInfo.name === name)) { + if (!result.some(themeInfo => themeInfo.name === name)) { result.push({ name, path: path.join(customThemesDir, file) }); } } @@ -1686,7 +1686,7 @@ async function loadThemeJson(name: string): Promise { let errorMessage = `Invalid theme "${name}":\n`; if (missingColors.length > 0) { errorMessage += `\nMissing required color tokens:\n`; - errorMessage += missingColors.map((c) => ` - ${c}`).join("\n"); + errorMessage += missingColors.map(c => ` - ${c}`).join("\n"); errorMessage += `\n\nPlease add these colors to your theme's "colors" object.`; errorMessage += `\nSee the built-in themes (dark.json, light.json) for reference values.`; } @@ -1942,18 +1942,18 @@ async function startThemeWatcher(): Promise { } try { - themeWatcher = fs.watch(themeFile, (eventType) => { + themeWatcher = fs.watch(themeFile, eventType => { if (eventType === "change") { // Debounce rapid changes setTimeout(() => { loadTheme(currentThemeName!, getCurrentThemeOptions()) - .then((loadedTheme) => { + .then(loadedTheme => { theme = loadedTheme; if (onThemeChangeCallback) { onThemeChangeCallback(); } }) - .catch((err) => { + .catch(err => { logger.debug("Theme reload error during file change", { error: String(err) }); }); }, 100); @@ -1963,13 +1963,13 @@ async function startThemeWatcher(): Promise { if (!fs.existsSync(themeFile)) { currentThemeName = "dark"; loadTheme("dark", getCurrentThemeOptions()) - .then((loadedTheme) => { + .then(loadedTheme => { theme = loadedTheme; if (onThemeChangeCallback) { onThemeChangeCallback(); } }) - .catch((err) => { + .catch(err => { logger.debug("Theme reload error during rename fallback", { error: String(err) }); }); if (themeWatcher) { @@ -2312,7 +2312,7 @@ export function getMarkdownTheme(): MarkdownTheme { try { return highlight(code, opts).split("\n"); } catch { - return code.split("\n").map((line) => theme.fg("mdCodeBlock", line)); + return code.split("\n").map(line => theme.fg("mdCodeBlock", line)); } }, }; diff --git a/packages/coding-agent/src/modes/utils/ui-helpers.ts b/packages/coding-agent/src/modes/utils/ui-helpers.ts index e51ea33a6..26878b2ed 100644 --- a/packages/coding-agent/src/modes/utils/ui-helpers.ts +++ b/packages/coding-agent/src/modes/utils/ui-helpers.ts @@ -33,7 +33,7 @@ export class UiHelpers { typeof message.content === "string" ? [{ type: "text", text: message.content }] : message.content.filter((content): content is TextBlock => content.type === "text"); - return textBlocks.map((block) => block.text).join(""); + return textBlocks.map(block => block.text).join(""); } /** @@ -306,7 +306,7 @@ export class UiHelpers { showNewVersionNotification(newVersion: string): void { this.ctx.chatContainer.addChild(new Spacer(1)); - this.ctx.chatContainer.addChild(new DynamicBorder((text) => theme.fg("warning", text))); + this.ctx.chatContainer.addChild(new DynamicBorder(text => theme.fg("warning", text))); this.ctx.chatContainer.addChild( new Text( theme.bold(theme.fg("warning", "Update Available")) + @@ -317,7 +317,7 @@ export class UiHelpers { 0, ), ); - this.ctx.chatContainer.addChild(new DynamicBorder((text) => theme.fg("warning", text))); + this.ctx.chatContainer.addChild(new DynamicBorder(text => theme.fg("warning", text))); this.ctx.ui.requestRender(); } diff --git a/packages/coding-agent/src/patch/applicator.ts b/packages/coding-agent/src/patch/applicator.ts index dac7cf0d0..158a23eca 100644 --- a/packages/coding-agent/src/patch/applicator.ts +++ b/packages/coding-agent/src/patch/applicator.ts @@ -4,7 +4,6 @@ * Applies parsed diff hunks to file content using fuzzy matching * for robust handling of whitespace and formatting differences. */ - import * as fs from "node:fs/promises"; import * as path from "node:path"; import { resolveToCwd } from "../tools/path-utils"; @@ -204,14 +203,14 @@ function adjustLinesIndentation(patternLines: string[], actualLines: string[], n deltas.push(aIndent - pIndent); } - if (deltas.length > 0 && deltas.every((value) => value === deltas[0])) { + if (deltas.length > 0 && deltas.every(value => value === deltas[0])) { delta = deltas[0]; } // Track which actual lines we've used to handle duplicate content correctly const usedActualLines = new Map(); // trimmed content -> count used - return newLines.map((newLine) => { + return newLines.map(newLine => { if (newLine.trim().length === 0) { return newLine; } @@ -277,7 +276,7 @@ function trimCommonContext(oldLines: string[], newLines: string[]): HunkVariant } function collapseConsecutiveSharedLines(oldLines: string[], newLines: string[]): HunkVariant | undefined { - const shared = new Set(oldLines.filter((line) => newLines.includes(line))); + const shared = new Set(oldLines.filter(line => newLines.includes(line))); const collapse = (lines: string[]): string[] => { const out: string[] = []; let i = 0; @@ -302,7 +301,7 @@ function collapseConsecutiveSharedLines(oldLines: string[], newLines: string[]): } function collapseRepeatedBlocks(oldLines: string[], newLines: string[]): HunkVariant | undefined { - const shared = new Set(oldLines.filter((line) => newLines.includes(line))); + const shared = new Set(oldLines.filter(line => newLines.includes(line))); const collapse = (lines: string[]): string[] => { const output = [...lines]; let changed = false; @@ -313,7 +312,7 @@ function collapseRepeatedBlocks(oldLines: string[], newLines: string[]): HunkVar const first = output.slice(i, i + size); const second = output.slice(i + size, i + size * 2); if (first.length !== second.length || first.length === 0) continue; - if (!first.every((line) => shared.has(line))) continue; + if (!first.every(line => shared.has(line))) continue; let same = true; for (let idx = 0; idx < size; idx++) { if (first[idx] !== second[idx]) { @@ -379,7 +378,7 @@ function buildFallbackVariants(hunk: DiffHunk): HunkVariant[] { if (singleLine) variants.push(singleLine); const seen = new Set(); - return variants.filter((variant) => { + return variants.filter(variant => { if (variant.oldLines.length === 0 && variant.newLines.length === 0) return false; const key = `${variant.oldLines.join("\n")}||${variant.newLines.join("\n")}`; if (seen.has(key)) return false; @@ -443,8 +442,8 @@ function findHierarchicalContext( if (context.includes("\n")) { const parts = context .split("\n") - .map((p) => p.trim()) - .filter((p) => p.length > 0); + .map(p => p.trim()) + .filter(p => p.length > 0); let currentStart = startFrom; for (let i = 0; i < parts.length; i++) { @@ -488,7 +487,7 @@ function findHierarchicalContext( } // Try literal context first - const spaceParts = context.split(/\s+/).filter((p) => p.length > 0); + const spaceParts = context.split(/\s+/).filter(p => p.length > 0); const hasSignatureChars = /[(){}[\]]/.test(context); if (!hasSignatureChars && spaceParts.length > 2) { const outer = spaceParts.slice(0, -1).join(" "); @@ -899,7 +898,7 @@ function computeReplacements( for (const variant of buildFallbackVariants(hunk)) { if (variant.oldLines.length !== 1 || variant.newLines.length !== 1) continue; const removedLine = variant.oldLines[0]; - const hasSharedDuplicate = hunk.newLines.some((line) => line.trim() === removedLine.trim()); + const hasSharedDuplicate = hunk.newLines.some(line => line.trim() === removedLine.trim()); const adjacentIndex = findContextRelativeMatch( originalLines, removedLine, @@ -922,7 +921,7 @@ function computeReplacements( if (line.trim() === trimmed) occurrenceCount++; } if (occurrenceCount > 1) { - const hasSharedDuplicate = hunk.newLines.some((line) => line.trim() === trimmed); + const hasSharedDuplicate = hunk.newLines.some(line => line.trim() === trimmed); const contextMatch = findContextRelativeMatch(originalLines, pattern[0], contextIndex, hasSharedDuplicate); if (contextMatch !== undefined) { searchResult = { index: contextMatch, confidence: searchResult.confidence ?? 0.95 }; diff --git a/packages/coding-agent/src/patch/diff.ts b/packages/coding-agent/src/patch/diff.ts index ea18b518c..a0250898b 100644 --- a/packages/coding-agent/src/patch/diff.ts +++ b/packages/coding-agent/src/patch/diff.ts @@ -4,7 +4,6 @@ * Provides diff string generation and the replace-mode edit logic * used when not in patch mode. */ - import * as Diff from "diff"; import { resolveToCwd } from "../tools/path-utils"; import { previewPatch } from "./applicator"; diff --git a/packages/coding-agent/src/patch/fuzzy.ts b/packages/coding-agent/src/patch/fuzzy.ts index 58a0d7280..c75f6c88e 100644 --- a/packages/coding-agent/src/patch/fuzzy.ts +++ b/packages/coding-agent/src/patch/fuzzy.ts @@ -4,7 +4,6 @@ * Provides both character-level and line-level fuzzy matching with progressive * fallback strategies for finding text in files. */ - import { countLeadingWhitespace, normalizeForFuzzy, normalizeUnicode } from "./normalize"; import type { ContextLineResult, FuzzyMatch, MatchOutcome, SequenceSearchResult } from "./types"; @@ -89,7 +88,7 @@ function computeRelativeIndentDepths(lines: string[]): number[] { } } const minIndent = nonEmptyIndents.length > 0 ? Math.min(...nonEmptyIndents) : 0; - const indentSteps = nonEmptyIndents.map((indent) => indent - minIndent).filter((step) => step > 0); + const indentSteps = nonEmptyIndents.map(indent => indent - minIndent).filter(step => step > 0); const indentUnit = indentSteps.length > 0 ? Math.min(...indentSteps) : 1; return lines.map((line, index) => { diff --git a/packages/coding-agent/src/patch/index.ts b/packages/coding-agent/src/patch/index.ts index 102d7b0b5..8f145cd0b 100644 --- a/packages/coding-agent/src/patch/index.ts +++ b/packages/coding-agent/src/patch/index.ts @@ -7,7 +7,6 @@ * * The mode is determined by the `edit.patchMode` setting. */ - import * as fs from "node:fs/promises"; import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core"; import { StringEnum } from "@oh-my-pi/pi-ai"; diff --git a/packages/coding-agent/src/patch/normalize.ts b/packages/coding-agent/src/patch/normalize.ts index 550666d76..386bca218 100644 --- a/packages/coding-agent/src/patch/normalize.ts +++ b/packages/coding-agent/src/patch/normalize.ts @@ -189,7 +189,7 @@ export function convertLeadingTabsToSpaces(text: string, spacesPerTab: number): if (spacesPerTab <= 0) return text; return text .split("\n") - .map((line) => { + .map(line => { const trimmed = line.trimStart(); if (trimmed.length === 0) return line; const leading = getLeadingWhitespace(line); @@ -212,7 +212,7 @@ export function normalizeUnicode(s: string): string { return s .trim() .split("") - .map((c) => { + .map(c => { const code = c.charCodeAt(0); // Various dash/hyphen code-points → ASCII '-' @@ -367,7 +367,7 @@ export function adjustIndentation(oldText: string, actualText: string, newText: } const delta = deltas[0]; - if (!deltas.every((value) => value === delta)) { + if (!deltas.every(value => value === delta)) { return newText; } @@ -380,7 +380,7 @@ export function adjustIndentation(oldText: string, actualText: string, newText: } const indentChar = actualProfile.char ?? oldProfile.char ?? detectIndentChar(actualText); - const adjusted = newText.split("\n").map((line) => { + const adjusted = newText.split("\n").map(line => { if (line.trim().length === 0) { return line; } diff --git a/packages/coding-agent/src/patch/normative.ts b/packages/coding-agent/src/patch/normative.ts index 28f53803c..7e5db0143 100644 --- a/packages/coding-agent/src/patch/normative.ts +++ b/packages/coding-agent/src/patch/normative.ts @@ -1,7 +1,6 @@ /** * Normalize applied patch output into a canonical edit tool payload. */ - import { generateUnifiedDiffString } from "./diff"; import { normalizeToLF, stripBom } from "./normalize"; import { parseHunks } from "./parser"; @@ -37,7 +36,7 @@ function applyAnchors(diff: string, anchors: Array | undefin function deriveAnchors(diff: string): Array { const hunks = parseHunks(diff); - return hunks.map((hunk) => { + return hunks.map(hunk => { if (hunk.oldLines.length === 0 || hunk.newLines.length === 0) { return undefined; } diff --git a/packages/coding-agent/src/patch/parser.ts b/packages/coding-agent/src/patch/parser.ts index 72f9d4e2a..ff09c5a5f 100644 --- a/packages/coding-agent/src/patch/parser.ts +++ b/packages/coding-agent/src/patch/parser.ts @@ -6,7 +6,6 @@ * - Unified diff format (@@ -X,Y +A,B @@) * - Codex-style wrapped patches (*** Begin Patch / *** End Patch) */ - import type { DiffHunk } from "./types"; import { ApplyPatchError, ParseError } from "./types"; @@ -90,7 +89,7 @@ export function normalizeDiff(diff: string): string { // Layer 2: Strip Codex-style file operation markers and unified diff metadata // NOTE: Do NOT strip "*** End of File" - that's a valid marker within hunks, not a wrapper // IMPORTANT: Only strip actual metadata lines, NOT diff content lines (starting with space, +, or -) - lines = lines.filter((line) => { + lines = lines.filter(line => { // Preserve diff content lines even if their content looks like metadata // Note: `--- ` and `+++ ` are metadata, not content lines if (isDiffContentLine(line)) { @@ -130,12 +129,12 @@ export function normalizeDiff(diff: string): string { */ export function normalizeCreateContent(content: string): string { const lines = content.split("\n"); - const nonEmptyLines = lines.filter((l) => l.length > 0); + const nonEmptyLines = lines.filter(l => l.length > 0); // Check if all non-empty lines start with "+ " or "+" - if (nonEmptyLines.length > 0 && nonEmptyLines.every((l) => l.startsWith("+ ") || l.startsWith("+"))) { + if (nonEmptyLines.length > 0 && nonEmptyLines.every(l => l.startsWith("+ ") || l.startsWith("+"))) { return lines - .map((l) => { + .map(l => { if (l.startsWith("+ ")) return l.slice(2); if (l.startsWith("+")) return l.slice(1); return l; @@ -390,18 +389,18 @@ function parseOneHunk(lines: string[], lineNumber: number, allowMissingContext: } function stripLineNumberPrefixes(hunk: DiffHunk): void { - const allLines = [...hunk.oldLines, ...hunk.newLines].filter((line) => line.trim().length > 0); + const allLines = [...hunk.oldLines, ...hunk.newLines].filter(line => line.trim().length > 0); if (allLines.length < 2) return; const numberMatches = allLines - .map((line) => line.match(/^\s*(\d{1,6})\s+(.+)$/u)) + .map(line => line.match(/^\s*(\d{1,6})\s+(.+)$/u)) .filter((match): match is RegExpMatchArray => match !== null); if (numberMatches.length < Math.max(2, Math.ceil(allLines.length * 0.6))) { return; } - const numbers = numberMatches.map((match) => Number(match[1])); + const numbers = numberMatches.map(match => Number(match[1])); let sequential = 0; for (let i = 1; i < numbers.length; i++) { if (numbers[i] === numbers[i - 1] + 1) { @@ -515,7 +514,7 @@ export function parseHunks(diff: string): DiffHunk[] { continue; } - if (trimmed.startsWith("@@") && lines.slice(i + 1).every((l) => l.trim() === "")) { + if (trimmed.startsWith("@@") && lines.slice(i + 1).every(l => l.trim() === "")) { break; } diff --git a/packages/coding-agent/src/patch/shared.ts b/packages/coding-agent/src/patch/shared.ts index a3cde2af5..6aa0d3c9b 100644 --- a/packages/coding-agent/src/patch/shared.ts +++ b/packages/coding-agent/src/patch/shared.ts @@ -1,7 +1,6 @@ /** * Shared utilities for edit tool TUI rendering. */ - import type { ToolCallContext } from "@oh-my-pi/pi-agent-core"; import type { Component } from "@oh-my-pi/pi-tui"; import { Text } from "@oh-my-pi/pi-tui"; @@ -38,7 +37,7 @@ export function getLspBatchRequest(toolCall: ToolCallContext | undefined): { id: if (!hasOtherWrites) { return undefined; } - const hasLaterWrites = toolCall.toolCalls.slice(toolCall.index + 1).some((call) => LSP_BATCH_TOOLS.has(call.name)); + const hasLaterWrites = toolCall.toolCalls.slice(toolCall.index + 1).some(call => LSP_BATCH_TOOLS.has(call.name)); return { id: toolCall.batchId, flush: !hasLaterWrites }; } @@ -259,7 +258,7 @@ export const editToolRenderer = { if (result.isError) { // Show error from result - const errorText = result.content?.find((c) => c.type === "text")?.text ?? ""; + const errorText = result.content?.find(c => c.type === "text")?.text ?? ""; if (errorText) { text += `\n\n${uiTheme.fg("error", errorText)}`; } diff --git a/packages/coding-agent/src/sdk.ts b/packages/coding-agent/src/sdk.ts index 957ac66a7..cf0c9ab5d 100644 --- a/packages/coding-agent/src/sdk.ts +++ b/packages/coding-agent/src/sdk.ts @@ -25,7 +25,6 @@ * }); * ``` */ - import * as fs from "node:fs"; import * as path from "node:path"; import { Agent, type AgentEvent, type AgentMessage, type AgentTool, type ThinkingLevel } from "@oh-my-pi/pi-agent-core"; @@ -253,7 +252,7 @@ export async function discoverAuthStorage(agentDir: string = getDefaultAgentDir( const primaryPath = path.join(agentDir, "auth.json"); // Get all auth.json paths (user-level only), excluding the primary const allPaths = getConfigDirPaths("auth.json", { project: false }); - const fallbackPaths = allPaths.filter((p) => p !== primaryPath); + const fallbackPaths = allPaths.filter(p => p !== primaryPath); logger.debug("discoverAuthStorage", { agentDir, primaryPath, allPaths, fallbackPaths }); @@ -296,7 +295,7 @@ export function discoverModels(authStorage: AuthStorage, agentDir: string = getD const yamlPaths = getConfigDirPaths("models.yml", { project: false }); const jsonPaths = getConfigDirPaths("models.json", { project: false }); const allPaths = [...yamlPaths, ...jsonPaths]; - const existenceResults = allPaths.map((p) => { + const existenceResults = allPaths.map(p => { return { p, exists: fs.existsSync(p) }; }); const fallbackPaths = existenceResults.filter(({ p, exists }) => p !== primaryPath && exists).map(({ p }) => p); @@ -524,7 +523,7 @@ function customToolToDefinition(tool: CustomTool): ToolDefinition { } function createCustomToolsExtension(tools: CustomTool[]): ExtensionFactory { - return (api) => { + return api => { for (const tool of tools) { api.registerTool(customToolToDefinition(tool)); } @@ -659,9 +658,9 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {} if (!model) { const allModels = modelRegistry.getAll(); const keyResults = await Promise.all( - allModels.map(async (m) => ({ model: m, hasKey: !!(await modelRegistry.getApiKey(m, sessionId)) })), + allModels.map(async m => ({ model: m, hasKey: !!(await modelRegistry.getApiKey(m, sessionId)) })), ); - model = keyResults.find((r) => r.hasKey)?.model; + model = keyResults.find(r => r.hasKey)?.model; time("findAvailableModel"); if (model) { if (modelFallbackMessage) { @@ -784,7 +783,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {} const customTools: CustomTool[] = []; if (enableMCP) { const mcpResult = await discoverAndLoadMCPTools(cwd, { - onConnecting: (serverNames) => { + onConnecting: serverNames => { if (options.hasUI && serverNames.length > 0) { process.stderr.write( chalk.gray(`Connecting to MCP servers: ${serverNames.join(", ")}... @@ -813,7 +812,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {} if (mcpResult.tools.length > 0) { // MCP tools are LoadedCustomTool, extract the tool property - customTools.push(...mcpResult.tools.map((loaded) => loaded.tool)); + customTools.push(...mcpResult.tools.map(loaded => loaded.tool)); } } @@ -832,7 +831,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {} enableCompany: exaSettings.enableCompany, }); // Filter out the base web_search (already in built-in tools), add specialized Exa tools - const specializedTools = exaWebSearchTools.filter((t) => t.name !== "web_search"); + const specializedTools = exaWebSearchTools.filter(t => t.name !== "web_search"); if (specializedTools.length > 0) { customTools.push(...specializedTools); } @@ -922,7 +921,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {} const registeredTools = extensionRunner?.getAllRegisteredTools() ?? []; const allCustomTools = [ ...registeredTools, - ...(options.customTools?.map((tool) => { + ...(options.customTools?.map(tool => { const definition = isCustomTool(tool) ? customToolToDefinition(tool) : tool; return { definition, extensionPath: "" }; }) ?? []), @@ -952,7 +951,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {} cwd, tools: toolRegistry, getToolContext: () => toolContextStore.getContext(), - emitEvent: (event) => cursorEventEmitter?.(event), + emitEvent: event => cursorEventEmitter?.(event), }); const rebuildSystemPrompt = async (toolNames: string[], tools: Map): Promise => { @@ -1002,16 +1001,14 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {} return converted; } // Filter out ImageContent from all messages, replacing with text placeholder - return converted.map((msg) => { + return converted.map(msg => { if (msg.role === "user" || msg.role === "toolResult") { const content = msg.content; if (Array.isArray(content)) { - const hasImages = content.some((c) => c.type === "image"); + const hasImages = content.some(c => c.type === "image"); if (hasImages) { const filteredContent = content - .map((c) => - c.type === "image" ? { type: "text" as const, text: "Image reading is disabled." } : c, - ) + .map(c => (c.type === "image" ? { type: "text" as const, text: "Image reading is disabled." } : c)) .filter( (c, i, arr) => // Dedupe consecutive "Image reading is disabled." texts @@ -1045,7 +1042,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {} convertToLlm: convertToLlmWithBlockImages, sessionId: sessionManager.getSessionId(), transformContext: extensionRunner - ? async (messages) => { + ? async messages => { return extensionRunner.emitContext(messages); } : undefined, @@ -1053,7 +1050,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {} followUpMode: settingsManager.getFollowUpMode(), interruptMode: settingsManager.getInterruptMode(), thinkingBudgets: settingsManager.getThinkingBudgets(), - getToolContext: (tc) => toolContextStore.getContext(tc), + getToolContext: tc => toolContextStore.getContext(tc), getApiKey: async () => { const currentModel = agent.state.model; if (!currentModel) { @@ -1067,7 +1064,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {} }, cursorExecHandlers, }); - cursorEventEmitter = (event) => agent.emitExternalEvent(event); + cursorEventEmitter = event => agent.emitExternalEvent(event); time("createAgent"); // Restore messages if session has existing data @@ -1105,7 +1102,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {} if (enableLsp && settingsManager.getLspDiagnosticsOnWrite()) { try { const result = await warmupLspServers(cwd, { - onConnecting: (serverNames) => { + onConnecting: serverNames => { if (options.hasUI && serverNames.length > 0) { process.stderr.write(chalk.gray(`Starting LSP servers: ${serverNames.join(", ")}...\n`)); } diff --git a/packages/coding-agent/src/session/agent-session.ts b/packages/coding-agent/src/session/agent-session.ts index 48f507672..6da86e4f9 100644 --- a/packages/coding-agent/src/session/agent-session.ts +++ b/packages/coding-agent/src/session/agent-session.ts @@ -13,6 +13,7 @@ * Modes use this class and add their own I/O layer on top. */ +import * as fs from "node:fs"; import type { Agent, AgentEvent, AgentMessage, AgentState, AgentTool, ThinkingLevel } from "@oh-my-pi/pi-agent-core"; import type { AssistantMessage, @@ -27,15 +28,14 @@ import type { import { isContextOverflow, modelsAreEqual, supportsXhigh } from "@oh-my-pi/pi-ai"; import { abortableSleep, isEnoent, logger } from "@oh-my-pi/pi-utils"; import { YAML } from "bun"; -import * as fs from "node:fs"; import type { Rule } from "../capability/rule"; import { getAgentDbPath } from "../config"; import type { ModelRegistry } from "../config/model-registry"; import { parseModelString } from "../config/model-resolver"; import { expandPromptTemplate, - parseCommandArgs, type PromptTemplate, + parseCommandArgs, renderPromptTemplate, } from "../config/prompt-templates"; import type { SettingsManager, SkillsSettings } from "../config/settings-manager"; @@ -70,10 +70,10 @@ import { resolveToCwd } from "../tools/path-utils"; import type { TodoItem } from "../tools/todo-write"; import { extractFileMentions, generateFileMentionMessages } from "../utils/file-mentions"; import { + type CompactionResult, calculateContextTokens, collectEntriesForBranchSummary, compact, - type CompactionResult, estimateTokens, generateBranchSummary, prepareCompaction, @@ -81,8 +81,8 @@ import { } from "./compaction"; import { type BashExecutionMessage, - bashExecutionToText, type BranchSummaryMessage, + bashExecutionToText, type CompactionSummaryMessage, type CustomMessage, type FileMentionMessage, @@ -216,7 +216,7 @@ const noOpUIContext: ExtensionUIContext = { }, getAllThemes: () => Promise.resolve([]), getTheme: () => Promise.resolve(undefined), - setTheme: (_theme) => Promise.resolve({ success: false, error: "UI not available" }), + setTheme: _theme => Promise.resolve({ success: false, error: "UI not available" }), setFooter: () => {}, setHeader: () => {}, setEditorComponent: () => {}, @@ -542,7 +542,7 @@ export class AgentSession { private _getTtsrInjectionContent(): string | undefined { if (this._pendingTtsrInjections.length === 0) return undefined; const content = this._pendingTtsrInjections - .map((r) => renderPromptTemplate(ttsrInterruptTemplate, { name: r.name, path: r.path, content: r.content })) + .map(r => renderPromptTemplate(ttsrInterruptTemplate, { name: r.name, path: r.path, content: r.content })) .join("\n\n"); this._pendingTtsrInjections = []; return content; @@ -553,10 +553,10 @@ export class AgentSession { if (message.role !== "user") return ""; const content = message.content; if (typeof content === "string") return content; - const textBlocks = content.filter((c) => c.type === "text"); - const text = textBlocks.map((c) => (c as TextContent).text).join(""); + const textBlocks = content.filter(c => c.type === "text"); + const text = textBlocks.map(c => (c as TextContent).text).join(""); if (text.length > 0) return text; - const hasImages = content.some((c) => c.type === "image"); + const hasImages = content.some(c => c.type === "image"); return hasImages ? "[Image]" : ""; } @@ -653,7 +653,7 @@ export class AgentSession { const normalizedDiff = normalizeDiff(diffForCheck.replace(/\r/g, "")); if (!normalizedDiff) return; const lines = normalizedDiff.split("\n"); - const hasChangeLine = lines.some((line) => line.startsWith("+") || line.startsWith("-")); + const hasChangeLine = lines.some(line => line.startsWith("+") || line.startsWith("-")); if (!hasChangeLine) return; const lineCount = lines.length; @@ -664,13 +664,13 @@ export class AgentSession { const rename = typeof args.rename === "string" ? args.rename : undefined; const removedLines = lines - .filter((line) => line.startsWith("-") && !line.startsWith("--- ")) - .map((line) => line.slice(1)); + .filter(line => line.startsWith("-") && !line.startsWith("--- ")) + .map(line => line.slice(1)); if (removedLines.length > 0) { const resolvedPath = resolveToCwd(path, this.sessionManager.getCwd()); const cachedContent = this._streamingEditFileCache.get(resolvedPath); if (cachedContent !== undefined) { - const missing = removedLines.find((line) => !cachedContent.includes(normalizeToLF(line))); + const missing = removedLines.find(line => !cachedContent.includes(normalizeToLF(line))); if (missing) { this._streamingEditAbortTriggered = true; logger.warn("Streaming edit aborted due to patch preview failure", { @@ -701,7 +701,7 @@ export class AgentSession { try { const { text } = stripBom(await Bun.file(resolvedPath).text()); const normalizedContent = normalizeToLF(text); - const missing = removedLines.find((line) => !normalizedContent.includes(normalizeToLF(line))); + const missing = removedLines.find(line => !normalizedContent.includes(normalizeToLF(line))); if (missing) { this._streamingEditAbortTriggered = true; logger.warn("Streaming edit aborted due to patch preview failure", { @@ -886,7 +886,7 @@ export class AgentSession { * Returns the names of tools currently set on the agent. */ getActiveToolNames(): string[] { - return this.agent.state.tools.map((t) => t.name); + return this.agent.state.tools.map(t => t.name); } /** @@ -1179,7 +1179,7 @@ export class AgentSession { hasQueuedMessages: () => this.queuedMessageCount > 0, getContextUsage: () => this.getContextUsage(), waitForIdle: () => this.agent.waitForIdle(), - newSession: async (options) => { + newSession: async options => { const success = await this.newSession({ parentSession: options?.parentSession }); if (!success) { return { cancelled: true }; @@ -1189,7 +1189,7 @@ export class AgentSession { } return { cancelled: false }; }, - branch: async (entryId) => { + branch: async entryId => { const result = await this.branch(entryId); return { cancelled: result.cancelled }; }, @@ -1197,7 +1197,7 @@ export class AgentSession { const result = await this.navigateTree(targetId, { summarize: options?.summarize }); return { cancelled: result.cancelled }; }, - compact: async (instructionsOrOptions) => { + compact: async instructionsOrOptions => { const instructions = typeof instructionsOrOptions === "string" ? instructionsOrOptions : undefined; const options = instructionsOrOptions && typeof instructionsOrOptions === "object" ? instructionsOrOptions : undefined; @@ -1219,7 +1219,7 @@ export class AgentSession { const argsString = spaceIndex === -1 ? "" : text.slice(spaceIndex + 1); // Find matching command - const loaded = this._customCommands.find((c) => c.command.name === commandName); + const loaded = this._customCommands.find(c => c.command.name === commandName); if (!loaded) return null; // Get command context from extension runner (includes session control methods) @@ -1606,10 +1606,10 @@ export class AgentSession { const parsed = parseModelString(roleModelStr); let match: Model | undefined; if (parsed) { - match = availableModels.find((m) => m.provider === parsed.provider && m.id === parsed.id); + match = availableModels.find(m => m.provider === parsed.provider && m.id === parsed.id); } if (!match) { - match = availableModels.find((m) => m.id.toLowerCase() === roleModelStr.toLowerCase()); + match = availableModels.find(m => m.id.toLowerCase() === roleModelStr.toLowerCase()); } if (!match) continue; @@ -1620,8 +1620,8 @@ export class AgentSession { const lastRole = this.sessionManager.getLastModelChangeRole(); let currentIndex = lastRole - ? roleModels.findIndex((entry) => entry.role === lastRole) - : roleModels.findIndex((entry) => modelsAreEqual(entry.model, currentModel)); + ? roleModels.findIndex(entry => entry.role === lastRole) + : roleModels.findIndex(entry => modelsAreEqual(entry.model, currentModel)); if (currentIndex === -1) currentIndex = 0; const nextIndex = (currentIndex + 1) % roleModels.length; @@ -1640,7 +1640,7 @@ export class AgentSession { if (this._scopedModels.length <= 1) return undefined; const currentModel = this.model; - let currentIndex = this._scopedModels.findIndex((sm) => modelsAreEqual(sm.model, currentModel)); + let currentIndex = this._scopedModels.findIndex(sm => modelsAreEqual(sm.model, currentModel)); if (currentIndex === -1) currentIndex = 0; const len = this._scopedModels.length; @@ -1669,7 +1669,7 @@ export class AgentSession { if (availableModels.length <= 1) return undefined; const currentModel = this.model; - let currentIndex = availableModels.findIndex((m) => modelsAreEqual(m, currentModel)); + let currentIndex = availableModels.findIndex(m => modelsAreEqual(m, currentModel)); if (currentIndex === -1) currentIndex = 0; const len = availableModels.length; @@ -1899,7 +1899,7 @@ export class AgentSession { this.agent.replaceMessages(sessionContext.messages); // Get the saved compaction entry for the hook - const savedCompactionEntry = newEntries.find((e) => e.type === "compaction" && e.summary === summary) as + const savedCompactionEntry = newEntries.find(e => e.type === "compaction" && e.summary === summary) as | CompactionEntry | undefined; @@ -1976,7 +1976,7 @@ export class AgentSession { // The error shouldn't trigger another compaction since we already compacted. // Example: opus fails → switch to codex → compact → switch back to opus → opus error // is still in context but shouldn't trigger compaction again. - const compactionEntry = this.sessionManager.getBranch().find((e) => e.type === "compaction"); + const compactionEntry = this.sessionManager.getBranch().find(e => e.type === "compaction"); const errorIsFromBeforeCompaction = compactionEntry && assistantMessage.timestamp < new Date(compactionEntry.timestamp).getTime(); @@ -2036,7 +2036,7 @@ export class AgentSession { } // Check for incomplete todos - const incomplete = todos.filter((t) => t.status !== "completed"); + const incomplete = todos.filter(t => t.status !== "completed"); if (incomplete.length === 0) { this._todoReminderCount = 0; return; @@ -2044,7 +2044,7 @@ export class AgentSession { // Build reminder message this._todoReminderCount++; - const todoList = incomplete.map((t) => `- ${t.content}`).join("\n"); + const todoList = incomplete.map(t => `- ${t.content}`).join("\n"); const reminder = `\n` + `You stopped with ${incomplete.length} incomplete todo item(s):\n${todoList}\n\n` + @@ -2093,10 +2093,10 @@ export class AgentSession { const parsed = parseModelString(roleModelStr); if (parsed) { - return availableModels.find((m) => m.provider === parsed.provider && m.id === parsed.id); + return availableModels.find(m => m.provider === parsed.provider && m.id === parsed.id); } const roleLower = roleModelStr.toLowerCase(); - return availableModels.find((m) => m.id.toLowerCase() === roleLower); + return availableModels.find(m => m.id.toLowerCase() === roleLower); } private _getCompactionModelCandidates(availableModels: Model[]): Model[] { @@ -2294,7 +2294,7 @@ export class AgentSession { this.agent.replaceMessages(sessionContext.messages); // Get the saved compaction entry for the hook - const savedCompactionEntry = newEntries.find((e) => e.type === "compaction" && e.summary === summary) as + const savedCompactionEntry = newEntries.find(e => e.type === "compaction" && e.summary === summary) as | CompactionEntry | undefined; @@ -2821,7 +2821,7 @@ export class AgentSession { const provider = defaultModelStr.slice(0, slashIdx); const modelId = defaultModelStr.slice(slashIdx + 1); const availableModels = this._modelRegistry.getAvailable(); - const match = availableModels.find((m) => m.provider === provider && m.id === modelId); + const match = availableModels.find(m => m.provider === provider && m.id === modelId); if (match) { this.agent.setModel(match); } @@ -3025,7 +3025,7 @@ export class AgentSession { ? targetEntry.content : targetEntry.content .filter((c): c is { type: "text"; text: string } => c.type === "text") - .map((c) => c.text) + .map(c => c.text) .join(""); } else { // Non-user message: leaf = selected node @@ -3091,7 +3091,7 @@ export class AgentSession { if (Array.isArray(content)) { return content .filter((c): c is { type: "text"; text: string } => c.type === "text") - .map((c) => c.text) + .map(c => c.text) .join(""); } return ""; @@ -3102,9 +3102,9 @@ export class AgentSession { */ getSessionStats(): SessionStats { const state = this.state; - const userMessages = state.messages.filter((m) => m.role === "user").length; - const assistantMessages = state.messages.filter((m) => m.role === "assistant").length; - const toolResults = state.messages.filter((m) => m.role === "toolResult").length; + const userMessages = state.messages.filter(m => m.role === "user").length; + const assistantMessages = state.messages.filter(m => m.role === "assistant").length; + const toolResults = state.messages.filter(m => m.role === "toolResult").length; let toolCalls = 0; let totalInput = 0; @@ -3124,7 +3124,7 @@ export class AgentSession { for (const message of state.messages) { if (message.role === "assistant") { const assistantMsg = message as AssistantMessage; - toolCalls += assistantMsg.content.filter((c) => c.type === "toolCall").length; + toolCalls += assistantMsg.content.filter(c => c.type === "toolCall").length; totalInput += assistantMsg.usage.input; totalOutput += assistantMsg.usage.output; totalCacheRead += assistantMsg.usage.cacheRead; @@ -3192,7 +3192,7 @@ export class AgentSession { const authStorage = this._modelRegistry.authStorage; if (!authStorage.fetchUsageReports) return null; return authStorage.fetchUsageReports({ - baseUrlResolver: (provider) => this._modelRegistry.getProviderBaseUrl?.(provider), + baseUrlResolver: provider => this._modelRegistry.getProviderBaseUrl?.(provider), }); } @@ -3273,7 +3273,7 @@ export class AgentSession { const lastAssistant = this.messages .slice() .reverse() - .find((m) => { + .find(m => { if (m.role !== "assistant") return false; const msg = m as AssistantMessage; // Skip aborted messages with no content diff --git a/packages/coding-agent/src/session/agent-storage.ts b/packages/coding-agent/src/session/agent-storage.ts index 24b8f5f8c..824e6b93f 100644 --- a/packages/coding-agent/src/session/agent-storage.ts +++ b/packages/coding-agent/src/session/agent-storage.ts @@ -191,8 +191,8 @@ CREATE TABLE IF NOT EXISTS schema_version (version INTEGER PRIMARY KEY); const settingsInfo = this.db.prepare("PRAGMA table_info(settings)").all() as Array<{ name?: string }>; const hasSettingsTable = settingsInfo.length > 0; - const hasKey = settingsInfo.some((column) => column.name === "key"); - const hasValue = settingsInfo.some((column) => column.name === "value"); + const hasKey = settingsInfo.some(column => column.name === "key"); + const hasValue = settingsInfo.some(column => column.name === "value"); if (!hasSettingsTable) { this.db.exec(` diff --git a/packages/coding-agent/src/session/artifacts.ts b/packages/coding-agent/src/session/artifacts.ts index 730272c79..cd255a078 100644 --- a/packages/coding-agent/src/session/artifacts.ts +++ b/packages/coding-agent/src/session/artifacts.ts @@ -4,7 +4,6 @@ * Artifacts are stored in a directory alongside the session file, * accessible via artifact:// URLs or the $ARTIFACTS environment variable. */ - import * as fs from "node:fs/promises"; import * as path from "node:path"; @@ -104,7 +103,7 @@ export class ArtifactManager { */ async exists(id: string): Promise { const files = await this.listFiles(); - return files.some((f) => f.startsWith(`${id}.`)); + return files.some(f => f.startsWith(`${id}.`)); } /** @@ -127,7 +126,7 @@ export class ArtifactManager { */ async getPath(id: string): Promise { const files = await this.listFiles(); - const match = files.find((f) => f.startsWith(`${id}.`)); + const match = files.find(f => f.startsWith(`${id}.`)); return match ? path.join(this.#dir, match) : null; } } diff --git a/packages/coding-agent/src/session/auth-storage.ts b/packages/coding-agent/src/session/auth-storage.ts index 3cd373063..d82615c1a 100644 --- a/packages/coding-agent/src/session/auth-storage.ts +++ b/packages/coding-agent/src/session/auth-storage.ts @@ -2,7 +2,6 @@ * Credential storage for API keys and OAuth tokens. * Handles loading, saving, and refreshing credentials from agent.db. */ - import { Buffer } from "node:buffer"; import * as path from "node:path"; import { @@ -94,7 +93,7 @@ const DEFAULT_USAGE_PROVIDERS: UsageProvider[] = [ ]; const DEFAULT_USAGE_PROVIDER_MAP = new Map( - DEFAULT_USAGE_PROVIDERS.map((provider) => [provider.id, provider]), + DEFAULT_USAGE_PROVIDERS.map(provider => [provider.id, provider]), ); const USAGE_CACHE_PREFIX = "usage_cache:"; @@ -230,7 +229,7 @@ export class AuthStorage { for (const [provider, creds] of Object.entries(data.credentials)) { instance.data.set( provider, - creds.map((c) => ({ + creds.map(c => ({ id: c.id, credential: c.type === "api_key" @@ -255,7 +254,7 @@ export class AuthStorage { serialize(): SerializedAuthStorage { const credentials: SerializedAuthStorage["credentials"] = {}; for (const [provider, creds] of this.data.entries()) { - credentials[provider] = creds.map((c) => ({ + credentials[provider] = creds.map(c => ({ id: c.id, type: c.credential.type, data: c.credential.type === "api_key" ? { key: c.credential.key } : { ...c.credential }, @@ -425,7 +424,7 @@ export class AuthStorage { deduped.push(credential); continue; } - if (identifiers.some((identifier) => seen.has(identifier))) { + if (identifiers.some(identifier => seen.has(identifier))) { continue; } for (const identifier of identifiers) { @@ -452,7 +451,7 @@ export class AuthStorage { kept.push(entry); continue; } - if (identifiers.some((identifier) => seen.has(identifier))) { + if (identifiers.some(identifier => seen.has(identifier))) { removed.push(entry); continue; } @@ -472,7 +471,7 @@ export class AuthStorage { /** Returns all credentials for a provider as an array */ private getCredentialsForProvider(provider: string): AuthCredential[] { - return this.getStoredCredentials(provider).map((entry) => entry.credential); + return this.getStoredCredentials(provider).map(entry => entry.credential); } /** Composite key for round-robin tracking: "anthropic:oauth" or "openai:api_key" */ @@ -660,7 +659,7 @@ export class AuthStorage { const stored = this.storage.replaceAuthCredentialsForProvider(provider, deduped); this.setStoredCredentials( provider, - stored.map((record) => ({ id: record.id, credential: record.credential })), + stored.map(record => ({ id: record.id, credential: record.credential })), ); this.resetProviderAssignments(provider); } @@ -704,7 +703,7 @@ export class AuthStorage { * Check if OAuth credentials are configured for a provider. */ hasOAuth(provider: string): boolean { - return this.getCredentialsForProvider(provider).some((credential) => credential.type === "oauth"); + return this.getCredentialsForProvider(provider).some(credential => credential.type === "oauth"); } /** @@ -722,7 +721,7 @@ export class AuthStorage { getAll(): AuthStorageData { const result: AuthStorageData = {}; for (const [provider, entries] of this.data.entries()) { - const credentials = entries.map((entry) => entry.credential); + const credentials = entries.map(entry => entry.credential); if (credentials.length === 1) { result[provider] = credentials[0]; } else if (credentials.length > 1) { @@ -773,7 +772,7 @@ export class AuthStorage { break; case "cursor": credentials = await loginCursor( - (url) => ctrl.onAuth({ url }), + url => ctrl.onAuth({ url }), ctrl.onProgress ? () => ctrl.onProgress?.("Waiting for browser authentication...") : undefined, ); break; @@ -847,7 +846,7 @@ export class AuthStorage { if (username) identifiers.push(`account:${username}`); const scopeAccountId = this.getUsageReportScopeAccountId(report); if (scopeAccountId) identifiers.push(`account:${scopeAccountId}`); - return identifiers.map((identifier) => `${report.provider}:${identifier.toLowerCase()}`); + return identifiers.map(identifier => `${report.provider}:${identifier.toLowerCase()}`); } private mergeUsageReportGroup(reports: UsageReport[]): UsageReport { @@ -859,7 +858,7 @@ export class AuthStorage { }); const base = sorted[0]; const mergedLimits = [...base.limits]; - const limitIds = new Set(mergedLimits.map((limit) => limit.id)); + const limitIds = new Set(mergedLimits.map(limit => limit.id)); const mergedMetadata: Record = { ...(base.metadata ?? {}) }; let fetchedAt = base.fetchedAt; @@ -912,7 +911,7 @@ export class AuthStorage { } } - const deduped = groups.map((group) => this.mergeUsageReportGroup(group)); + const deduped = groups.map(group => this.mergeUsageReportGroup(group)); if (deduped.length !== reports.length) { this.usageLogger?.debug("Usage reports deduped", { before: reports.length, @@ -935,7 +934,7 @@ export class AuthStorage { /** Returns true if usage indicates rate limit has been reached. */ private isUsageLimitReached(report: UsageReport): boolean { - return report.limits.some((limit) => this.isUsageLimitExhausted(limit)); + return report.limits.some(limit => this.isUsageLimitExhausted(limit)); } /** Extracts the earliest reset timestamp from exhausted windows (in ms). */ @@ -1000,10 +999,7 @@ export class AuthStorage { if (!resolver || !cache) return null; const tasks: Array> = []; - const providers = new Set([ - ...this.data.keys(), - ...DEFAULT_USAGE_PROVIDERS.map((provider) => provider.id), - ]); + const providers = new Set([...this.data.keys(), ...DEFAULT_USAGE_PROVIDERS.map(provider => provider.id)]); this.usageLogger?.debug("Usage fetch requested", { providers: Array.from(providers).sort(), }); @@ -1048,7 +1044,7 @@ export class AuthStorage { now: this.usageNow, logger: this.usageLogger, }) - .catch((error) => { + .catch(error => { logger.debug("AuthStorage usage fetch failed", { provider, error: String(error), @@ -1091,7 +1087,7 @@ export class AuthStorage { now: this.usageNow, logger: this.usageLogger, }) - .catch((error) => { + .catch(error => { logger.debug("AuthStorage usage fetch failed", { provider, error: String(error), @@ -1107,7 +1103,7 @@ export class AuthStorage { const reports = results.filter((report): report is UsageReport => report !== null); const deduped = this.dedupeUsageReports(reports); this.usageLogger?.debug("Usage fetch resolved", { - reports: deduped.map((report) => { + reports: deduped.map(report => { const accountLabel = this.getUsageReportMetadataValue(report, "email") ?? this.getUsageReportMetadataValue(report, "accountId") ?? @@ -1164,7 +1160,7 @@ export class AuthStorage { entry.credential.type === sessionCredential.type && entry.index !== sessionCredential.index, ); - return remainingCredentials.some((candidate) => !this.isCredentialBlocked(providerKey, candidate.index)); + return remainingCredentials.some(candidate => !this.isCredentialBlocked(providerKey, candidate.index)); } /** @@ -1285,7 +1281,7 @@ export class AuthStorage { error: String(error), }); this.removeCredentialAt(provider, selection.index); - if (this.getCredentialsForProvider(provider).some((credential) => credential.type === "oauth")) { + if (this.getCredentialsForProvider(provider).some(credential => credential.type === "oauth")) { return this.getApiKey(provider, sessionId, options); } } diff --git a/packages/coding-agent/src/session/compaction/branch-summarization.ts b/packages/coding-agent/src/session/compaction/branch-summarization.ts index 9a0e00b16..4d3ccf05b 100644 --- a/packages/coding-agent/src/session/compaction/branch-summarization.ts +++ b/packages/coding-agent/src/session/compaction/branch-summarization.ts @@ -4,7 +4,6 @@ * When navigating to a different point in the session tree, this generates * a summary of the branch being left so context isn't lost. */ - import type { AgentMessage } from "@oh-my-pi/pi-agent-core"; import type { Model } from "@oh-my-pi/pi-ai"; import { completeSimple } from "@oh-my-pi/pi-ai"; @@ -105,7 +104,7 @@ export function collectEntriesForBranchSummary( } // Find common ancestor (deepest node that's on both paths) - const oldPath = new Set(session.getBranch(oldLeafId).map((e) => e.id)); + const oldPath = new Set(session.getBranch(oldLeafId).map(e => e.id)); const targetPath = session.getBranch(targetId); // targetPath is root-first, so iterate backwards to find deepest common ancestor @@ -298,7 +297,7 @@ export async function generateBranchSummary( let summary = response.content .filter((c): c is { type: "text"; text: string } => c.type === "text") - .map((c) => c.text) + .map(c => c.text) .join("\n"); // Prepend preamble to provide context about the branch summary diff --git a/packages/coding-agent/src/session/compaction/compaction.ts b/packages/coding-agent/src/session/compaction/compaction.ts index f8accaefd..8c6ab8e06 100644 --- a/packages/coding-agent/src/session/compaction/compaction.ts +++ b/packages/coding-agent/src/session/compaction/compaction.ts @@ -4,7 +4,6 @@ * Pure functions for compaction logic. The session manager handles I/O, * and after compaction the session is reloaded. */ - import type { AgentMessage } from "@oh-my-pi/pi-agent-core"; import type { AssistantMessage, Model, Usage } from "@oh-my-pi/pi-ai"; import { completeSimple } from "@oh-my-pi/pi-ai"; @@ -444,7 +443,7 @@ export async function generateSummary( const textContent = response.content .filter((c): c is { type: "text"; text: string } => c.type === "text") - .map((c) => c.text) + .map(c => c.text) .join("\n"); return textContent; @@ -665,6 +664,6 @@ async function generateTurnPrefixSummary( return response.content .filter((c): c is { type: "text"; text: string } => c.type === "text") - .map((c) => c.text) + .map(c => c.text) .join("\n"); } diff --git a/packages/coding-agent/src/session/compaction/utils.ts b/packages/coding-agent/src/session/compaction/utils.ts index 5be94dd08..580a7ccb1 100644 --- a/packages/coding-agent/src/session/compaction/utils.ts +++ b/packages/coding-agent/src/session/compaction/utils.ts @@ -1,7 +1,6 @@ /** * Shared utilities for compaction and branch summarization. */ - import type { AgentMessage } from "@oh-my-pi/pi-agent-core"; import type { Message } from "@oh-my-pi/pi-ai"; import { renderPromptTemplate } from "../../config/prompt-templates"; @@ -64,7 +63,7 @@ export function extractFileOpsFromMessage(message: AgentMessage, fileOps: FileOp */ export function computeFileLists(fileOps: FileOperations): { readFiles: string[]; modifiedFiles: string[] } { const modified = new Set([...fileOps.edited, ...fileOps.written]); - const readOnly = [...fileOps.read].filter((f) => !modified.has(f)).sort(); + const readOnly = [...fileOps.read].filter(f => !modified.has(f)).sort(); const modifiedFiles = [...modified].sort(); return { readFiles: readOnly, modifiedFiles }; } @@ -96,7 +95,7 @@ export function serializeConversation(messages: Message[]): string { ? msg.content : msg.content .filter((c): c is { type: "text"; text: string } => c.type === "text") - .map((c) => c.text) + .map(c => c.text) .join(""); if (content) parts.push(`[User]: ${content}`); } else if (msg.role === "assistant") { @@ -130,7 +129,7 @@ export function serializeConversation(messages: Message[]): string { } else if (msg.role === "toolResult") { const content = msg.content .filter((c): c is { type: "text"; text: string } => c.type === "text") - .map((c) => c.text) + .map(c => c.text) .join(""); if (content) { parts.push(`[Tool result]: ${content}`); diff --git a/packages/coding-agent/src/session/history-storage.ts b/packages/coding-agent/src/session/history-storage.ts index c4626de16..f0f98985a 100644 --- a/packages/coding-agent/src/session/history-storage.ts +++ b/packages/coding-agent/src/session/history-storage.ts @@ -110,7 +110,7 @@ END; try { const rows = this.recentStmt.all(safeLimit) as HistoryRow[]; - return rows.map((row) => this.toEntry(row)); + return rows.map(row => this.toEntry(row)); } catch (error) { logger.error("HistoryStorage getRecent failed", { error: String(error) }); return []; @@ -126,7 +126,7 @@ END; try { const rows = this.searchStmt.all(ftsQuery, safeLimit) as HistoryRow[]; - return rows.map((row) => this.toEntry(row)); + return rows.map(row => this.toEntry(row)); } catch (error) { logger.error("HistoryStorage search failed", { error: String(error) }); return []; @@ -148,13 +148,13 @@ END; const tokens = query .trim() .split(/\s+/) - .map((token) => token.trim()) + .map(token => token.trim()) .filter(Boolean); if (tokens.length === 0) return null; return tokens - .map((token) => { + .map(token => { const escaped = token.replace(/"/g, '""'); return `"${escaped}"*`; }) diff --git a/packages/coding-agent/src/session/messages.ts b/packages/coding-agent/src/session/messages.ts index 76fbcb442..aaa8a7f47 100644 --- a/packages/coding-agent/src/session/messages.ts +++ b/packages/coding-agent/src/session/messages.ts @@ -4,7 +4,6 @@ * Extends the base AgentMessage type with coding-agent specific message types, * and provides a transformer to convert them to LLM-compatible messages. */ - import type { AgentMessage } from "@oh-my-pi/pi-agent-core"; import type { ImageContent, Message, TextContent } from "@oh-my-pi/pi-ai"; import type { OutputMeta } from "../tools/output-meta"; @@ -255,7 +254,7 @@ export function convertToLlm(messages: AgentMessage[]): Message[] { timestamp: m.timestamp, }; case "fileMention": { - const fileContents = m.files.map((f) => `\n${f.content}\n`).join("\n\n"); + const fileContents = m.files.map(f => `\n${f.content}\n`).join("\n\n"); return { role: "user", content: [{ type: "text" as const, text: `\n${fileContents}\n` }], @@ -272,5 +271,5 @@ export function convertToLlm(messages: AgentMessage[]): Message[] { return undefined; } }) - .filter((m) => m !== undefined); + .filter(m => m !== undefined); } diff --git a/packages/coding-agent/src/session/session-manager.ts b/packages/coding-agent/src/session/session-manager.ts index b3f96c263..0b23078d7 100644 --- a/packages/coding-agent/src/session/session-manager.ts +++ b/packages/coding-agent/src/session/session-manager.ts @@ -252,7 +252,7 @@ function migrateV2ToV3(entries: FileEntry[]): void { * Mutates entries in place. Returns true if any migration was applied. */ function migrateToCurrentVersion(entries: FileEntry[]): boolean { - const header = entries.find((e) => e.type === "session") as SessionHeader | undefined; + const header = entries.find(e => e.type === "session") as SessionHeader | undefined; const version = header?.version ?? 1; if (version >= CURRENT_SESSION_VERSION) return false; @@ -397,7 +397,7 @@ export function buildSessionContext( messages.push(createCompactionSummaryMessage(compaction.summary, compaction.tokensBefore, compaction.timestamp)); // Find compaction index in path - const compactionIdx = path.findIndex((e) => e.type === "compaction" && e.id === compaction.id); + const compactionIdx = path.findIndex(e => e.type === "compaction" && e.id === compaction.id); // Emit kept messages (before compaction, starting from firstKeptEntryId) let foundFirstKept = false; @@ -682,7 +682,7 @@ async function truncateForPersistence(obj: T, key?: string): Promise { if (Array.isArray(obj)) { let changed = false; const result = await Promise.all( - obj.map(async (item) => { + obj.map(async item => { // Special handling: compress oversized images while preserving shape if (key === TEXT_CONTENT_KEY && isImageBlock(item)) { if (item.data.length > MAX_PERSIST_CHARS) { @@ -889,7 +889,7 @@ function extractTextFromContent(content: Message["content"]): string { if (typeof content === "string") return content; return content .filter((block): block is TextContent => block.type === "text") - .map((block) => block.text) + .map(block => block.text) .join(" "); } @@ -1013,7 +1013,7 @@ export class SessionManager { this.sessionFile = path.resolve(sessionFile); this.fileEntries = await loadEntriesFromFile(this.sessionFile, this.storage); if (this.fileEntries.length > 0) { - const header = this.fileEntries.find((e) => e.type === "session") as SessionHeader | undefined; + const header = this.fileEntries.find(e => e.type === "session") as SessionHeader | undefined; this.sessionId = header?.id ?? nanoid(); this.sessionTitle = header?.title; @@ -1122,7 +1122,7 @@ export class SessionManager { if (this.persistError && !options?.ignoreError) throw this.persistError; await task(); }); - this.persistChain = next.catch((err) => { + this.persistChain = next.catch(err => { this._recordPersistError(err); }); return next; @@ -1134,7 +1134,7 @@ export class SessionManager { if (this.persistWriter && this.persistWriterPath === this.sessionFile) return this.persistWriter; // Note: caller must await _closePersistWriter() before calling this if switching files this.persistWriter = new NdjsonFileWriter(this.storage, this.sessionFile, { - onError: (err) => { + onError: err => { this._recordPersistError(err); }, }); @@ -1192,7 +1192,7 @@ export class SessionManager { if (!this.persist || !this.sessionFile) return; await this._queuePersistTask(async () => { await this._closePersistWriterInternal(); - const entries = await Promise.all(this.fileEntries.map((entry) => prepareEntryForPersistence(entry))); + const entries = await Promise.all(this.fileEntries.map(entry => prepareEntryForPersistence(entry))); await this._writeEntriesAtomically(entries); this.flushed = true; }); @@ -1243,7 +1243,7 @@ export class SessionManager { this.sessionTitle = title; // Update the in-memory header (so first flush includes title) - const header = this.fileEntries.find((e) => e.type === "session") as SessionHeader | undefined; + const header = this.fileEntries.find(e => e.type === "session") as SessionHeader | undefined; if (header) { header.title = title; } @@ -1259,7 +1259,7 @@ export class SessionManager { if (!this.persist || !this.sessionFile) return; if (this.persistError) throw this.persistError; - const hasAssistant = this.fileEntries.some((e) => e.type === "message" && e.message.role === "assistant"); + const hasAssistant = this.fileEntries.some(e => e.type === "message" && e.message.role === "assistant"); if (!hasAssistant) return; if (!this.flushed) { @@ -1267,7 +1267,7 @@ export class SessionManager { void this._queuePersistTask(async () => { const writer = this._ensurePersistWriter(); if (!writer) return; - const entries = await Promise.all(this.fileEntries.map((e) => prepareEntryForPersistence(e))); + const entries = await Promise.all(this.fileEntries.map(e => prepareEntryForPersistence(e))); for (const persistedEntry of entries) { await writer.write(persistedEntry); } @@ -1603,7 +1603,7 @@ export class SessionManager { * Get session header. */ getHeader(): SessionHeader | null { - const h = this.fileEntries.find((e) => e.type === "session"); + const h = this.fileEntries.find(e => e.type === "session"); return h ? (h as SessionHeader) : null; } @@ -1722,7 +1722,7 @@ export class SessionManager { } // Filter out LabelEntry from path - we'll recreate them from the resolved map - const pathWithoutLabels = branchPath.filter((e) => e.type !== "label"); + const pathWithoutLabels = branchPath.filter(e => e.type !== "label"); const newSessionId = nanoid(); const timestamp = new Date().toISOString(); @@ -1739,7 +1739,7 @@ export class SessionManager { }; // Collect labels for entries in the path - const pathEntryIds = new Set(pathWithoutLabels.map((e) => e.id)); + const pathEntryIds = new Set(pathWithoutLabels.map(e => e.id)); const labelsToWrite: Array<{ targetId: string; label: string }> = []; for (const [targetId, label] of this.labelsById) { if (pathEntryIds.has(targetId)) { @@ -1784,7 +1784,7 @@ export class SessionManager { for (const { targetId, label } of labelsToWrite) { const labelEntry: LabelEntry = { type: "label", - id: generateId(new Set([...pathEntryIds, ...labelEntries.map((e) => e.id)])), + id: generateId(new Set([...pathEntryIds, ...labelEntries.map(e => e.id)])), parentId, timestamp: new Date().toISOString(), targetId, @@ -1825,8 +1825,8 @@ export class SessionManager { const manager = new SessionManager(cwd, dir, true, storage); const forkEntries = structuredClone(await loadEntriesFromFile(sourcePath, storage)) as FileEntry[]; migrateToCurrentVersion(forkEntries); - const sourceHeader = forkEntries.find((e) => e.type === "session") as SessionHeader | undefined; - const historyEntries = forkEntries.filter((entry) => entry.type !== "session") as SessionEntry[]; + const sourceHeader = forkEntries.find(e => e.type === "session") as SessionHeader | undefined; + const historyEntries = forkEntries.filter(entry => entry.type !== "session") as SessionEntry[]; manager._newSessionSync({ parentSession: sourceHeader?.id }); const newHeader = manager.fileEntries[0] as SessionHeader; newHeader.title = sourceHeader?.title; @@ -1849,7 +1849,7 @@ export class SessionManager { ): Promise { // Extract cwd from session header if possible, otherwise use process.cwd() const entries = await loadEntriesFromFile(filePath, storage); - const header = entries.find((e) => e.type === "session") as SessionHeader | undefined; + const header = entries.find(e => e.type === "session") as SessionHeader | undefined; const cwd = header?.cwd ?? process.cwd(); // If no sessionDir provided, derive from file's parent directory const dir = sessionDir ?? path.resolve(filePath, ".."); @@ -1911,7 +1911,7 @@ export class SessionManager { static async listAll(storage: SessionStorage = new FileSessionStorage()): Promise { const sessionsRoot = path.join(getDefaultAgentDir(), "sessions"); try { - const files = Array.from(new Bun.Glob("**/*.jsonl").scanSync(sessionsRoot)).map((name) => + const files = Array.from(new Bun.Glob("**/*.jsonl").scanSync(sessionsRoot)).map(name => path.join(sessionsRoot, name), ); return await collectSessionsFromFiles(files, storage); diff --git a/packages/coding-agent/src/session/session-storage.ts b/packages/coding-agent/src/session/session-storage.ts index f94c69e12..31f8bf390 100644 --- a/packages/coding-agent/src/session/session-storage.ts +++ b/packages/coding-agent/src/session/session-storage.ts @@ -1,6 +1,5 @@ import * as fs from "node:fs"; import * as path from "node:path"; - import { isEnoent } from "@oh-my-pi/pi-utils"; export interface SessionStorageStat { @@ -39,7 +38,7 @@ function toError(value: unknown): Error { } // FinalizationRegistry to clean up leaked file descriptors -const writerRegistry = new FinalizationRegistry((fd) => { +const writerRegistry = new FinalizationRegistry(fd => { try { fs.closeSync(fd); } catch { @@ -147,7 +146,7 @@ export class FileSessionStorage implements SessionStorage { listFilesSync(dir: string, pattern: string): string[] { try { - return Array.from(new Bun.Glob(pattern).scanSync(dir)).map((name) => path.join(dir, name)); + return Array.from(new Bun.Glob(pattern).scanSync(dir)).map(name => path.join(dir, name)); } catch { return []; } diff --git a/packages/coding-agent/src/session/storage-migration.ts b/packages/coding-agent/src/session/storage-migration.ts index d9e8100f1..f6ac4f82b 100644 --- a/packages/coding-agent/src/session/storage-migration.ts +++ b/packages/coding-agent/src/session/storage-migration.ts @@ -6,7 +6,6 @@ * NOTE: Settings migration is now handled by SettingsManager.migrateToYaml(), * which migrates from both settings.json and agent.db to config.yaml. */ - import { logger } from "@oh-my-pi/pi-utils"; import { getAgentDbPath } from "../config"; import { AgentStorage } from "./agent-storage"; @@ -128,7 +127,7 @@ async function migrateAuth(storage: AgentStorage, authPaths: string[], warnings: for (const [provider, entry] of Object.entries(authJson.data)) { const credentials = normalizeCredentialEntry(entry) .filter(isValidCredential) - .map((credential) => credential); + .map(credential => credential); if (credentials.length === 0) continue; sawValid = true; diff --git a/packages/coding-agent/src/session/streaming-output.ts b/packages/coding-agent/src/session/streaming-output.ts index f1c1d40e5..6ca9330bc 100644 --- a/packages/coding-agent/src/session/streaming-output.ts +++ b/packages/coding-agent/src/session/streaming-output.ts @@ -141,7 +141,7 @@ export class OutputSink { }; return new WritableStream({ - write: async (chunk) => { + write: async chunk => { if (typeof chunk === "string") { await this.push(chunk); } else { diff --git a/packages/coding-agent/src/ssh/connection-manager.ts b/packages/coding-agent/src/ssh/connection-manager.ts index 5d34da04c..ef254f0ae 100644 --- a/packages/coding-agent/src/ssh/connection-manager.ts +++ b/packages/coding-agent/src/ssh/connection-manager.ts @@ -3,7 +3,6 @@ import * as os from "node:os"; import * as path from "node:path"; import { isEnoent, logger } from "@oh-my-pi/pi-utils"; import { $ } from "bun"; - import { CONFIG_DIR_NAME } from "../config"; export interface SSHConnectionTarget { diff --git a/packages/coding-agent/src/ssh/sshfs-mount.ts b/packages/coding-agent/src/ssh/sshfs-mount.ts index d01899a57..bade95fe4 100644 --- a/packages/coding-agent/src/ssh/sshfs-mount.ts +++ b/packages/coding-agent/src/ssh/sshfs-mount.ts @@ -15,7 +15,7 @@ async function ensureDir(path: string, mode = 0o700): Promise { try { await fs.promises.mkdir(path, { recursive: true, mode }); } catch { - await fs.promises.chmod(path, mode).catch((e) => void e); + await fs.promises.chmod(path, mode).catch(e => void e); } } diff --git a/packages/coding-agent/src/system-prompt.ts b/packages/coding-agent/src/system-prompt.ts index 4288b5391..0c1e26717 100644 --- a/packages/coding-agent/src/system-prompt.ts +++ b/packages/coding-agent/src/system-prompt.ts @@ -1,7 +1,6 @@ /** * System prompt construction and project context loading */ - import * as os from "node:os"; import * as path from "node:path"; import { $ } from "bun"; @@ -35,7 +34,7 @@ export async function loadGitContext(cwd: string): Promise { .quiet() .text() .catch(() => null) - .then((text) => text?.trim() ?? null); + .then(text => text?.trim() ?? null); // Check if inside a git repo const isGitRepo = await git("rev-parse", "--is-inside-work-tree"); @@ -79,7 +78,7 @@ function firstNonEmptyLine(value: string | null): string | null { if (!value) return null; const line = value .split("\n") - .map((entry) => entry.trim()) + .map(entry => entry.trim()) .filter(Boolean)[0]; return line ?? null; } @@ -87,9 +86,9 @@ function firstNonEmptyLine(value: string | null): string | null { function parseWmicTable(output: string, header: string): string | null { const lines = output .split("\n") - .map((line) => line.trim()) + .map(line => line.trim()) .filter(Boolean); - const filtered = lines.filter((line) => line.toLowerCase() !== header.toLowerCase()); + const filtered = lines.filter(line => line.toLowerCase() !== header.toLowerCase()); return filtered[0] ?? null; } @@ -130,8 +129,8 @@ function listAgentsMdFiles(root: string, limit: number): string[] { new Bun.Glob(AGENTS_MD_PATTERN).scanSync({ cwd: root, onlyFiles: true, dot: false, absolute: false }), ); const normalized = entries - .map((entry) => normalizePath(entry)) - .filter((entry) => entry.length > 0 && !entry.includes("node_modules")) + .map(entry => normalizePath(entry)) + .filter(entry => entry.length > 0 && !entry.includes("node_modules")) .sort(); return normalized.length > limit ? normalized.slice(0, limit) : normalized; } catch { @@ -267,8 +266,8 @@ async function getCpuModel(): Promise { if (lscpu) { const match = lscpu .split("\n") - .map((line) => line.trim()) - .find((line) => line.toLowerCase().startsWith("model name:")); + .map(line => line.trim()) + .find(line => line.toLowerCase().startsWith("model name:")); if (match) return match.split(":").slice(1).join(":").trim(); } const cpuInfo = await Bun.file("/proc/cpuinfo") @@ -363,7 +362,7 @@ function normalizeDesktopValue(value: string): string { if (!trimmed) return "unknown"; const parts = trimmed .split(":") - .map((part) => part.trim()) + .map(part => part.trim()) .filter(Boolean); return parts[0] ?? trimmed; } @@ -488,7 +487,7 @@ async function getDiskInfo(): Promise { .text() .catch(() => null); if (!output) return null; - const lines = output.split("\n").filter((l) => l.trim() && !l.startsWith("Node")); + const lines = output.split("\n").filter(l => l.trim() && !l.startsWith("Node")); const disks: string[] = []; for (const line of lines) { const parts = line.split(","); @@ -584,7 +583,7 @@ export async function loadProjectContextFiles( const result = await loadCapability(contextFileCapability.id, { cwd: resolvedCwd }); // Convert ContextFile items and preserve depth info - const files = result.items.map((item) => { + const files = result.items.map(item => { const contextFile = item as ContextFile; return { path: contextFile.path, @@ -616,8 +615,8 @@ export async function loadSystemPromptFiles(options: LoadContextFilesOptions = { if (result.items.length === 0) return null; // Combine all SYSTEM.md contents (user-level first, then project-level) - const userLevel = result.items.filter((item) => item.level === "user"); - const projectLevel = result.items.filter((item) => item.level === "project"); + const userLevel = result.items.filter(item => item.level === "user"); + const projectLevel = result.items.filter(item => item.level === "project"); const parts: string[] = []; for (const item of [...userLevel, ...projectLevel]) { diff --git a/packages/coding-agent/src/task/agents.ts b/packages/coding-agent/src/task/agents.ts index 71fe5748c..f0e44dd05 100644 --- a/packages/coding-agent/src/task/agents.ts +++ b/packages/coding-agent/src/task/agents.ts @@ -3,7 +3,6 @@ * * Agents are embedded at build time via Bun's import with { type: "text" }. */ - import { renderPromptTemplate } from "../config/prompt-templates"; import { parseAgentFields } from "../discovery/helpers"; import exploreMd from "../prompts/agents/explore.md" with { type: "text" }; @@ -69,7 +68,7 @@ const EMBEDDED_AGENT_DEFS: EmbeddedAgentDef[] = [ }, ]; -const EMBEDDED_AGENTS: { name: string; content: string }[] = EMBEDDED_AGENT_DEFS.map((def) => ({ +const EMBEDDED_AGENTS: { name: string; content: string }[] = EMBEDDED_AGENT_DEFS.map(def => ({ name: def.fileName, content: buildAgentContent(def), })); @@ -141,7 +140,7 @@ export function loadBundledAgents(): AgentDefinition[] { * Get a bundled agent by name. */ export function getBundledAgent(name: string): AgentDefinition | undefined { - return loadBundledAgents().find((a) => a.name === name); + return loadBundledAgents().find(a => a.name === name); } /** diff --git a/packages/coding-agent/src/task/commands.ts b/packages/coding-agent/src/task/commands.ts index 0422a9157..0a23bfff7 100644 --- a/packages/coding-agent/src/task/commands.ts +++ b/packages/coding-agent/src/task/commands.ts @@ -3,7 +3,6 @@ * * Commands are embedded at build time via Bun's import with { type: "text" }. */ - import * as path from "node:path"; import { type SlashCommand, slashCommandCapability } from "../capability/slash-command"; import { renderPromptTemplate } from "../config/prompt-templates"; @@ -116,7 +115,7 @@ export async function discoverCommands(cwd: string): Promise * Get a command by name. */ export function getCommand(commands: WorkflowCommand[], name: string): WorkflowCommand | undefined { - return commands.find((c) => c.name === name); + return commands.find(c => c.name === name); } /** diff --git a/packages/coding-agent/src/task/discovery.ts b/packages/coding-agent/src/task/discovery.ts index b942bde2f..86e67056f 100644 --- a/packages/coding-agent/src/task/discovery.ts +++ b/packages/coding-agent/src/task/discovery.ts @@ -11,7 +11,6 @@ * * Agent files use markdown with YAML frontmatter. */ - import * as fs from "node:fs/promises"; import * as path from "node:path"; import { logger } from "@oh-my-pi/pi-utils"; @@ -31,14 +30,14 @@ export interface DiscoveryResult { async function loadAgentsFromDir(dir: string, source: AgentSource): Promise { const entries = await fs.readdir(dir, { withFileTypes: true }).catch(() => []); const files = entries - .filter((entry) => (entry.isFile() || entry.isSymbolicLink()) && entry.name.endsWith(".md")) + .filter(entry => (entry.isFile() || entry.isSymbolicLink()) && entry.name.endsWith(".md")) .sort((a, b) => a.name.localeCompare(b.name)) - .map((file) => { + .map(file => { const filePath = path.join(dir, file.name); return fs .readFile(filePath, "utf-8") - .then((content) => parseAgent(filePath, content, source, "warn")) - .catch((error) => { + .then(content => parseAgent(filePath, content, source, "warn")) + .catch(error => { logger.warn("Failed to read agent file", { filePath, error }); return null; }); @@ -56,47 +55,46 @@ async function loadAgentsFromDir(dir: string, source: AgentSource): Promise { const resolvedCwd = path.resolve(cwd); - const agentSources = Array.from(new Set(getConfigDirs("", { project: false }).map((entry) => entry.source))); + const agentSources = Array.from(new Set(getConfigDirs("", { project: false }).map(entry => entry.source))); // Get user directories (priority order: .omp, .pi, .claude, ...) const userDirs = getConfigDirs("agents", { project: false }) - .filter((entry) => agentSources.includes(entry.source)) - .map((entry) => ({ + .filter(entry => agentSources.includes(entry.source)) + .map(entry => ({ ...entry, path: path.resolve(entry.path), })); // Get project directories by walking up from cwd (priority order) const projectDirs = (await findAllNearestProjectConfigDirs("agents", resolvedCwd)) - .filter((entry) => agentSources.includes(entry.source)) - .map((entry) => ({ + .filter(entry => agentSources.includes(entry.source)) + .map(entry => ({ ...entry, path: path.resolve(entry.path), })); const orderedSources = agentSources.filter( - (source) => - userDirs.some((entry) => entry.source === source) || projectDirs.some((entry) => entry.source === source), + source => userDirs.some(entry => entry.source === source) || projectDirs.some(entry => entry.source === source), ); const orderedDirs: Array<{ dir: string; source: AgentSource }> = []; for (const source of orderedSources) { - const project = projectDirs.find((entry) => entry.source === source); + const project = projectDirs.find(entry => entry.source === source); if (project) orderedDirs.push({ dir: project.path, source: "project" }); - const user = userDirs.find((entry) => entry.source === source); + const user = userDirs.find(entry => entry.source === source); if (user) orderedDirs.push({ dir: user.path, source: "user" }); } const seen = new Set(); const loadedAgents = (await Promise.all(orderedDirs.map(({ dir, source }) => loadAgentsFromDir(dir, source)))) .flat() - .filter((agent) => { + .filter(agent => { if (seen.has(agent.name)) return false; seen.add(agent.name); return true; }); - const bundledAgents = loadBundledAgents().filter((agent) => { + const bundledAgents = loadBundledAgents().filter(agent => { if (seen.has(agent.name)) return false; seen.add(agent.name); return true; @@ -111,5 +109,5 @@ export async function discoverAgents(cwd: string): Promise { * Get an agent by name from discovered agents. */ export function getAgent(agents: AgentDefinition[], name: string): AgentDefinition | undefined { - return agents.find((a) => a.name === name); + return agents.find(a => a.name === name); } diff --git a/packages/coding-agent/src/task/executor.ts b/packages/coding-agent/src/task/executor.ts index be332db8a..0be740813 100644 --- a/packages/coding-agent/src/task/executor.ts +++ b/packages/coding-agent/src/task/executor.ts @@ -3,7 +3,6 @@ * * Runs each subagent in a Bun Worker and forwards AgentEvents for progress tracking. */ - import path from "node:path"; import type { AgentEvent, ThinkingLevel } from "@oh-my-pi/pi-agent-core"; import type { ToolSession } from ".."; @@ -171,7 +170,7 @@ function getUsageTokens(usage: unknown): number { * falling back to empty strings if not. */ function extractMCPToolMetadata(mcpManager: MCPManager): MCPToolMetadata[] { - return mcpManager.getTools().map((tool) => { + return mcpManager.getTools().map(tool => { // MCPTool and DeferredMCPTool have these properties const mcpTool = tool as { mcpToolName?: string; mcpServerName?: string }; return { @@ -264,7 +263,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise name !== "exec"); + const expanded = toolNames.filter(name => name !== "exec"); if (pythonToolMode === "bash-only") { expanded.push("bash"); } else if (pythonToolMode === "ipy-only") { @@ -706,11 +705,11 @@ export async function runSubprocess(options: ExecutorOptions): Promise>((resolve) => { + const done = await new Promise>(resolve => { const cleanup = () => { listenerController.abort(); }; - finalize = (message) => { + finalize = message => { if (resolved) return; resolved = true; cleanup(); diff --git a/packages/coding-agent/src/task/index.ts b/packages/coding-agent/src/task/index.ts index 23bb7ac82..24f7c59f9 100644 --- a/packages/coding-agent/src/task/index.ts +++ b/packages/coding-agent/src/task/index.ts @@ -12,7 +12,6 @@ * - Progress tracking via JSON events * - Session artifacts for debugging */ - import * as fs from "node:fs/promises"; import * as os from "node:os"; import path from "node:path"; @@ -25,6 +24,8 @@ import { renderPromptTemplate } from "../config/prompt-templates"; import type { Theme } from "../modes/theme/theme"; import taskDescriptionTemplate from "../prompts/tools/task.md" with { type: "text" }; import { formatDuration } from "../tools/render-utils"; +// Import review tools for side effects (registers subagent tool handlers) +import "../tools/review"; import { discoverAgents, getAgent } from "./discovery"; import { runSubprocess } from "./executor"; import { AgentOutputManager } from "./output-manager"; @@ -49,9 +50,6 @@ import { getRepoRoot, } from "./worktree"; -// Import review tools for side effects (registers subagent tool handlers) -import "../tools/review"; - /** Format byte count for display */ function formatBytes(bytes: number): string { if (bytes < 1024) return `${bytes}B`; @@ -174,7 +172,7 @@ export class TaskTool implements AgentTool a.name).join(", ") || "none"; + const available = agents.map(a => a.name).join(", ") || "none"; return { content: [ { @@ -268,7 +266,7 @@ export class TaskTool implements AgentTool 0) { - const details = duplicateIds.map((entry) => `${entry.id} (indexes ${entry.indexes.join(", ")})`).join("; "); + const details = duplicateIds.map(entry => `${entry.id} (indexes ${entry.indexes.join(", ")})`).join("; "); problems.push(`Duplicate task ids detected (case-insensitive): ${details}`); } return { @@ -348,7 +346,7 @@ export class TaskTool implements AgentTool s.trim()); + const allowedSpawns = parentSpawns.split(",").map(s => s.trim()); const isSpawnAllowed = (): boolean => { if (parentSpawns === "") return false; // Empty = deny all if (parentSpawns === "*") return true; // Wildcard = allow all @@ -371,11 +369,11 @@ export class TaskTool implements AgentTool null)); - const uniqueIds = await outputManager.allocateBatch(tasks.map((t) => t.id)); + const uniqueIds = await outputManager.allocateBatch(tasks.map(t => t.id)); const tasksWithUniqueIds = tasks.map((t, i) => ({ ...t, id: uniqueIds[i] })); // Build full prompts with context prepended - const tasksWithContext = tasksWithUniqueIds.map((t) => renderTemplate(context, t)); + const tasksWithContext = tasksWithUniqueIds.map(t => renderTemplate(context, t)); // Initialize progress for all tasks for (let i = 0; i < tasksWithContext.length; i++) { @@ -418,7 +416,7 @@ export class TaskTool implements AgentTool { + onProgress: progress => { progressMap.set(index, { ...structuredClone(progress), args: tasksWithContext[index]?.args, @@ -458,7 +456,7 @@ export class TaskTool implements AgentTool { + onProgress: progress => { progressMap.set(index, { ...structuredClone(progress), args: tasksWithContext[index]?.args, @@ -564,25 +562,25 @@ export class TaskTool implements AgentTool result.patchPath).filter(Boolean) as string[]; - const missingPatch = results.some((result) => !result.patchPath); + const patchesInOrder = results.map(result => result.patchPath).filter(Boolean) as string[]; + const missingPatch = results.some(result => !result.patchPath); if (!repoRoot || missingPatch) { patchesApplied = false; } else { const patchStats = await Promise.all( - patchesInOrder.map(async (patchPath) => ({ + patchesInOrder.map(async patchPath => ({ patchPath, size: (await fs.stat(patchPath)).size, })), ); - const nonEmptyPatches = patchStats.filter((patch) => patch.size > 0).map((patch) => patch.patchPath); + const nonEmptyPatches = patchStats.filter(patch => patch.size > 0).map(patch => patch.patchPath); if (nonEmptyPatches.length === 0) { patchesApplied = true; } else { const patchTexts = await Promise.all( - nonEmptyPatches.map(async (patchPath) => Bun.file(patchPath).text()), + nonEmptyPatches.map(async patchPath => Bun.file(patchPath).text()), ); - const combinedPatch = patchTexts.map((text) => (text.endsWith("\n") ? text : `${text}\n`)).join(""); + const combinedPatch = patchTexts.map(text => (text.endsWith("\n") ? text : `${text}\n`)).join(""); if (!combinedPatch.trim()) { patchesApplied = true; } else { @@ -616,18 +614,18 @@ export class TaskTool implements AgentToolPatches were not applied and must be handled manually."; const patchList = patchPaths.length > 0 - ? `\n\nPatch artifacts:\n${patchPaths.map((patch) => `- ${patch}`).join("\n")}` + ? `\n\nPatch artifacts:\n${patchPaths.map(patch => `- ${patch}`).join("\n")}` : ""; patchApplySummary = `\n\n${notification}${patchList}`; } } // Build final output - match plugin format - const successCount = results.filter((r) => r.exitCode === 0).length; - const cancelledCount = results.filter((r) => r.aborted).length; + const successCount = results.filter(r => r.exitCode === 0).length; + const cancelledCount = results.filter(r => r.aborted).length; const totalDuration = Date.now() - startTime; - const summaries = results.map((r) => { + const summaries = results.map(r => { const status = r.aborted ? "cancelled" : r.exitCode === 0 ? "completed" : `failed (exit ${r.exitCode})`; const output = r.output.trim() || r.stderr.trim() || "(no output)"; const preview = output.split("\n").slice(0, 5).join("\n"); @@ -637,10 +635,10 @@ export class TaskTool implements AgentTool !r.aborted || r.output.trim()).map((r) => r.id); + const outputIds = results.filter(r => !r.aborted || r.output.trim()).map(r => r.id); const outputHint = outputIds.length > 0 - ? `\n\nUse read with agent:// for full logs: ${outputIds.map((id) => `agent://${id}`).join(", ")}` + ? `\n\nUse read with agent:// for full logs: ${outputIds.map(id => `agent://${id}`).join(", ")}` : ""; const schemaNote = schemaOverridden ? `\n\nNote: Agent '${agentName}' has a fixed output schema; your 'output' parameter was ignored.\nRequired schema: ${JSON.stringify(agent.output)}` diff --git a/packages/coding-agent/src/task/output-manager.ts b/packages/coding-agent/src/task/output-manager.ts index a1893d837..e9b9a5ea1 100644 --- a/packages/coding-agent/src/task/output-manager.ts +++ b/packages/coding-agent/src/task/output-manager.ts @@ -6,7 +6,6 @@ * * This enables reliable agent:// URL resolution and prevents artifact collisions. */ - import * as fs from "node:fs/promises"; /** @@ -73,7 +72,7 @@ export class AgentOutputManager { */ async allocateBatch(ids: string[]): Promise { await this.#ensureInitialized(); - return ids.map((id) => `${this.#nextId++}-${id}`); + return ids.map(id => `${this.#nextId++}-${id}`); } /** diff --git a/packages/coding-agent/src/task/parallel.ts b/packages/coding-agent/src/task/parallel.ts index c80501a26..c51e16daa 100644 --- a/packages/coding-agent/src/task/parallel.ts +++ b/packages/coding-agent/src/task/parallel.ts @@ -1,7 +1,6 @@ /** * Parallel execution with concurrency control. */ - import { MAX_CONCURRENCY } from "./types"; /** Result of parallel execution */ diff --git a/packages/coding-agent/src/task/render.ts b/packages/coding-agent/src/task/render.ts index 3e942f1fb..d10186c1e 100644 --- a/packages/coding-agent/src/task/render.ts +++ b/packages/coding-agent/src/task/render.ts @@ -4,7 +4,6 @@ * Provides renderCall and renderResult functions for displaying * task execution in the terminal UI. */ - import path from "node:path"; import type { Component } from "@oh-my-pi/pi-tui"; import { Container, Text } from "@oh-my-pi/pi-tui"; @@ -79,7 +78,7 @@ function formatJsonScalar(value: unknown, theme: Theme): string { } function buildTreePrefix(ancestors: boolean[], theme: Theme): string { - return ancestors.map((hasNext) => (hasNext ? `${theme.tree.vertical} ` : " ")).join(""); + return ancestors.map(hasNext => (hasNext ? `${theme.tree.vertical} ` : " ")).join(""); } function renderJsonTreeLines( @@ -295,7 +294,7 @@ function formatArgsInline(args: Record, theme: Theme): string { /** Convert snake_case or kebab-case to Title Case */ function humanizeKey(key: string): string { - return key.replace(/[-_]/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()); + return key.replace(/[-_]/g, " ").replace(/\b\w/g, c => c.toUpperCase()); } function formatScalarInline(value: unknown, maxLen: number, theme: Theme): string { @@ -512,8 +511,8 @@ function renderAgentProgress( const completeData = progress.extractedToolData.complete as Array<{ data: unknown }> | undefined; const reportFindingData = progress.extractedToolData.report_finding as ReportFindingDetails[] | undefined; const reviewData = completeData - ?.map((c) => c.data as SubmitReviewDetails) - .filter((d) => d && typeof d === "object" && "overall_correctness" in d); + ?.map(c => c.data as SubmitReviewDetails) + .filter(d => d && typeof d === "object" && "overall_correctness" in d); if (reviewData && reviewData.length > 0) { const summary = reviewData[reviewData.length - 1]; const findings = reportFindingData ?? []; @@ -697,8 +696,8 @@ function renderAgentResult(result: SingleResult, isLast: boolean, expanded: bool // Extract review verdict from complete tool's data field if it matches SubmitReviewDetails const reviewData = completeData - ?.map((c) => c.data as SubmitReviewDetails) - .filter((d) => d && typeof d === "object" && "overall_correctness" in d); + ?.map(c => c.data as SubmitReviewDetails) + .filter(d => d && typeof d === "object" && "overall_correctness" in d); const submitReviewData = reviewData && reviewData.length > 0 ? reviewData : undefined; if (submitReviewData && submitReviewData.length > 0) { @@ -775,12 +774,12 @@ export function renderResult( theme: Theme, ): Component { const { expanded, isPartial, spinnerFrame } = options; - const fallbackText = result.content.find((c) => c.type === "text")?.text ?? ""; + const fallbackText = result.content.find(c => c.type === "text")?.text ?? ""; const details = result.details; if (!details) { // Fallback to simple text - const text = result.content.find((c) => c.type === "text")?.text || ""; + const text = result.content.find(c => c.type === "text")?.text || ""; return new Text(theme.fg("dim", truncate(text, 100, theme.format.ellipsis)), 0, 0); } @@ -800,8 +799,8 @@ export function renderResult( }); // Summary line - const abortedCount = details.results.filter((r) => r.aborted).length; - const successCount = details.results.filter((r) => !r.aborted && r.exitCode === 0).length; + const abortedCount = details.results.filter(r => r.aborted).length; + const successCount = details.results.filter(r => !r.aborted && r.exitCode === 0).length; const failCount = details.results.length - successCount - abortedCount; let summary = `${theme.fg("dim", "Total:")} `; if (abortedCount > 0) { @@ -829,7 +828,7 @@ export function renderResult( if (fallbackText.trim()) { const summaryLines = fallbackText.split("\n"); const markerIndex = summaryLines.findIndex( - (line) => line.includes("") || line.startsWith("Applied patches:"), + line => line.includes("") || line.startsWith("Applied patches:"), ); if (markerIndex >= 0) { const extra = summaryLines.slice(markerIndex); @@ -840,7 +839,7 @@ export function renderResult( } } - const indented = lines.map((line) => (line.length > 0 ? ` ${line}` : "")); + const indented = lines.map(line => (line.length > 0 ? ` ${line}` : "")); return new Text(indented.join("\n"), 0, 0); } diff --git a/packages/coding-agent/src/task/subprocess-tool-registry.ts b/packages/coding-agent/src/task/subprocess-tool-registry.ts index 13462fa5e..5ca232a96 100644 --- a/packages/coding-agent/src/task/subprocess-tool-registry.ts +++ b/packages/coding-agent/src/task/subprocess-tool-registry.ts @@ -6,7 +6,6 @@ * - Trigger subprocess termination on completion * - Provide custom rendering for realtime/final display */ - import type { Component } from "@oh-my-pi/pi-tui"; import type { Theme } from "../modes/theme/theme"; diff --git a/packages/coding-agent/src/task/worker.ts b/packages/coding-agent/src/task/worker.ts index 82380e1ce..1193c94a0 100644 --- a/packages/coding-agent/src/task/worker.ts +++ b/packages/coding-agent/src/task/worker.ts @@ -12,7 +12,6 @@ * 4. Worker sends { type: "done", exitCode, ... } on completion * 5. Parent can send { type: "abort" } to request cancellation */ - import type { AgentEvent, ThinkingLevel } from "@oh-my-pi/pi-agent-core"; import type { Api, Model } from "@oh-my-pi/pi-ai"; import { logger, postmortem, untilAborted } from "@oh-my-pi/pi-utils"; @@ -42,7 +41,7 @@ import type { type PostMessageFn = (message: SubagentWorkerResponse) => void; -const postMessageSafe: PostMessageFn = (message) => { +const postMessageSafe: PostMessageFn = message => { try { (globalThis as typeof globalThis & { postMessage: PostMessageFn }).postMessage(message); } catch { @@ -127,11 +126,11 @@ function callMCPToolViaParent( } pendingMCPCalls.set(callId, { - resolve: (result) => { + resolve: result => { cleanup(); resolve(result ?? { content: [] }); }, - reject: (error) => { + reject: error => { cleanup(); reject(error); }, @@ -195,11 +194,11 @@ function callPythonToolViaParent( } pendingPythonCalls.set(callId, { - resolve: (result) => { + resolve: result => { cleanup(); resolve(result ?? { content: [] }); }, - reject: (error) => { + reject: error => { cleanup(); reject(error); }, @@ -255,11 +254,11 @@ function callLspToolViaParent( } pendingLspCalls.set(callId, { - resolve: (result) => { + resolve: result => { cleanup(); resolve(result ?? { content: [] }); }, - reject: (error) => { + reject: error => { cleanup(); reject(error); }, @@ -344,7 +343,7 @@ function createMCPProxyTool(metadata: MCPToolMetadata): CustomTool { metadata.timeoutMs, ); return { - content: result.content.map((c) => + content: result.content.map(c => c.type === "text" ? { type: "text" as const, text: c.text ?? "" } : { type: "text" as const, text: JSON.stringify(c) }, @@ -386,7 +385,7 @@ function createPythonProxyTool(): CustomTool { const result = await callPythonToolViaParent(params as PythonToolParams, signal, timeoutMs); return { content: - result?.content?.map((c) => + result?.content?.map(c => c.type === "text" ? { type: "text" as const, text: c.text ?? "" } : { type: "text" as const, text: JSON.stringify(c) }, @@ -419,7 +418,7 @@ function createLspProxyTool(): CustomTool { const result = await callLspToolViaParent(params as Record, signal); return { content: - result?.content?.map((c) => + result?.content?.map(c => c.type === "text" ? { type: "text" as const, text: c.text ?? "" } : { type: "text" as const, text: JSON.stringify(c) }, @@ -601,7 +600,7 @@ async function runTask(runState: RunState, payload: SubagentWorkerStartPayload): outputSchema: payload.outputSchema, requireCompleteTool: true, // Append system prompt (equivalent to CLI's --append-system-prompt) - systemPrompt: (defaultPrompt) => + systemPrompt: defaultPrompt => `${defaultPrompt}\n\n${payload.systemPrompt}\n\n${worktreeNotice}\n\n${completionInstruction}`, sessionManager, hasUI: false, @@ -633,14 +632,14 @@ async function runTask(runState: RunState, payload: SubagentWorkerStartPayload): // ExtensionActions { sendMessage: (message, options) => { - session.sendCustomMessage(message, options).catch((e) => { + session.sendCustomMessage(message, options).catch(e => { logger.error("Extension sendMessage failed", { error: e instanceof Error ? e.message : String(e), }); }); }, sendUserMessage: (content, options) => { - session.sendUserMessage(content, options).catch((e) => { + session.sendUserMessage(content, options).catch(e => { logger.error("Extension sendUserMessage failed", { error: e instanceof Error ? e.message : String(e), }); @@ -655,14 +654,14 @@ async function runTask(runState: RunState, payload: SubagentWorkerStartPayload): getActiveTools: () => session.getActiveToolNames(), getAllTools: () => session.getAllToolNames(), setActiveTools: (toolNames: string[]) => session.setActiveToolsByName(toolNames), - setModel: async (model) => { + setModel: async model => { const key = await session.modelRegistry.getApiKey(model); if (!key) return false; await session.setModel(model); return true; }, getThinkingLevel: () => session.thinkingLevel, - setThinkingLevel: (level) => session.setThinkingLevel(level), + setThinkingLevel: level => session.setThinkingLevel(level), }, // ExtensionContextActions { @@ -672,7 +671,7 @@ async function runTask(runState: RunState, payload: SubagentWorkerStartPayload): hasPendingMessages: () => session.queuedMessageCount > 0, shutdown: () => {}, getContextUsage: () => session.getContextUsage(), - compact: async (instructionsOrOptions) => { + compact: async instructionsOrOptions => { const instructions = typeof instructionsOrOptions === "string" ? instructionsOrOptions : undefined; const options = instructionsOrOptions && typeof instructionsOrOptions === "object" @@ -682,7 +681,7 @@ async function runTask(runState: RunState, payload: SubagentWorkerStartPayload): }, }, ); - extensionRunner.onError((err) => { + extensionRunner.onError(err => { logger.error("Extension error", { path: err.extensionPath, error: err.error }); }); await extensionRunner.emit({ type: "session_start" }); @@ -839,11 +838,11 @@ declare const self: { addEventListener(type: "messageerror", listener: (event: MessageEvent) => void): void; }; -self.addEventListener("error", (event) => { +self.addEventListener("error", event => { reportFatal(`Uncaught error: ${event.message || "Unknown error"}`); }); -self.addEventListener("unhandledrejection", (event) => { +self.addEventListener("unhandledrejection", event => { const reason = event.reason; const message = reason instanceof Error ? reason.stack || reason.message : String(reason); diff --git a/packages/coding-agent/src/task/worktree.ts b/packages/coding-agent/src/task/worktree.ts index 7b76d8794..8f98aee7a 100644 --- a/packages/coding-agent/src/task/worktree.ts +++ b/packages/coding-agent/src/task/worktree.ts @@ -44,8 +44,8 @@ export async function captureBaseline(repoRoot: string): Promise line.trim()) - .filter((line) => line.length > 0); + .map(line => line.trim()) + .filter(line => line.length > 0); return { repoRoot, @@ -114,8 +114,8 @@ async function listUntracked(cwd: string): Promise { const raw = await $`git ls-files --others --exclude-standard`.cwd(cwd).quiet().text(); return raw .split("\n") - .map((line) => line.trim()) - .filter((line) => line.length > 0); + .map(line => line.trim()) + .filter(line => line.length > 0); } export async function captureDeltaPatch(worktreeDir: string, baseline: WorktreeBaseline): Promise { @@ -136,12 +136,12 @@ export async function captureDeltaPatch(worktreeDir: string, baseline: WorktreeB const currentUntracked = await listUntracked(worktreeDir); const baselineUntracked = new Set(baseline.untracked); - const newUntracked = currentUntracked.filter((entry) => !baselineUntracked.has(entry)); + const newUntracked = currentUntracked.filter(entry => !baselineUntracked.has(entry)); if (newUntracked.length === 0) return diff; const untrackedDiffs = await Promise.all( - newUntracked.map((entry) => + newUntracked.map(entry => $`git diff --binary --no-index /dev/null ${entry}`.cwd(worktreeDir).quiet().nothrow().text(), ), ); diff --git a/packages/coding-agent/src/tools/ask.ts b/packages/coding-agent/src/tools/ask.ts index 71fa048d6..7c1668e33 100644 --- a/packages/coding-agent/src/tools/ask.ts +++ b/packages/coding-agent/src/tools/ask.ts @@ -14,7 +14,6 @@ * - If you recommend a specific option, make that the first option in the list * and add "(Recommended)" at the end of the label */ - import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core"; import type { Component } from "@oh-my-pi/pi-tui"; import { Text } from "@oh-my-pi/pi-tui"; @@ -238,7 +237,7 @@ export class AskTool implements AgentTool { const results: QuestionResult[] = []; for (const q of params.questions) { - const optionLabels = q.options.map((o) => o.label); + const optionLabels = q.options.map(o => o.label); const { selectedOptions, customInput } = await askSingleQuestion( ui, q.question, @@ -267,7 +266,7 @@ export class AskTool implements AgentTool { const question = params.question ?? ""; const options = params.options ?? []; const multi = params.multi ?? false; - const optionLabels = options.map((o) => o.label); + const optionLabels = options.map(o => o.label); if (!question || optionLabels.length === 0) { return { @@ -391,7 +390,7 @@ export const askToolRenderer = { if (details.results && details.results.length > 0) { const lines: string[] = []; const hasAnySelection = details.results.some( - (r) => r.customInput || (r.selectedOptions && r.selectedOptions.length > 0), + r => r.customInput || (r.selectedOptions && r.selectedOptions.length > 0), ); const header = renderStatusLine( { diff --git a/packages/coding-agent/src/tools/bash-interceptor.ts b/packages/coding-agent/src/tools/bash-interceptor.ts index e7f0735fe..c7330085b 100644 --- a/packages/coding-agent/src/tools/bash-interceptor.ts +++ b/packages/coding-agent/src/tools/bash-interceptor.ts @@ -5,7 +5,6 @@ * this interceptor provides helpful error messages directing them to use * the specialized tools instead. */ - import { type BashInterceptorRule, DEFAULT_BASH_INTERCEPTOR_RULES } from "../config/settings-manager"; export interface InterceptionResult { diff --git a/packages/coding-agent/src/tools/bash.ts b/packages/coding-agent/src/tools/bash.ts index 806c40a26..753606e53 100644 --- a/packages/coding-agent/src/tools/bash.ts +++ b/packages/coding-agent/src/tools/bash.ts @@ -106,7 +106,7 @@ export class BashTool implements AgentTool { env: extraEnv, artifactPath, artifactId, - onChunk: (chunk) => { + onChunk: chunk => { tailBuffer.append(chunk); if (onUpdate) { onUpdate({ @@ -211,7 +211,7 @@ export const bashToolRenderer = { const previewLines = renderContext?.previewLines ?? BASH_DEFAULT_PREVIEW_LINES; // Get output from context (preferred) or fall back to result content - const output = renderContext?.output ?? result.content?.find((c) => c.type === "text")?.text ?? ""; + const output = renderContext?.output ?? result.content?.find(c => c.type === "text")?.text ?? ""; const displayOutput = output.trimEnd(); const showingFullOutput = expanded && renderContext?.isFullOutput === true; @@ -249,11 +249,11 @@ export const bashToolRenderer = { const hasOutput = displayOutput.trim().length > 0; if (hasOutput) { if (expanded) { - outputLines.push(...displayOutput.split("\n").map((line) => uiTheme.fg("toolOutput", line))); + outputLines.push(...displayOutput.split("\n").map(line => uiTheme.fg("toolOutput", line))); } else { const styledOutput = displayOutput .split("\n") - .map((line) => uiTheme.fg("toolOutput", line)) + .map(line => uiTheme.fg("toolOutput", line)) .join("\n"); const textContent = styledOutput; const result = truncateToVisualLines(textContent, previewLines, width); diff --git a/packages/coding-agent/src/tools/calculator.ts b/packages/coding-agent/src/tools/calculator.ts index 8b62a7dba..7086b7252 100644 --- a/packages/coding-agent/src/tools/calculator.ts +++ b/packages/coding-agent/src/tools/calculator.ts @@ -417,13 +417,13 @@ export class CalculatorTool implements AgentTool> { return untilAborted(signal, async () => { - const results = calculations.map((calc) => { + const results = calculations.map(calc => { const value = evaluateExpression(calc.expression); const output = `${calc.prefix}${formatResult(value)}${calc.suffix}`; return { expression: calc.expression, value, output }; }); - const outputText = results.map((result) => result.output).join("\n"); + const outputText = results.map(result => result.output).join("\n"); return { content: [{ type: "text", text: outputText }], details: { results }, @@ -471,17 +471,17 @@ export const calculatorToolRenderer = { args?: CalculatorRenderArgs, ): Component { const details = result.details; - const textContent = result.content?.find((c) => c.type === "text")?.text ?? ""; + const textContent = result.content?.find(c => c.type === "text")?.text ?? ""; if (result.isError) { const header = renderStatusLine({ icon: "error", title: "Calc" }, uiTheme); return new Text([header, formatErrorMessage(textContent, uiTheme)].join("\n"), 0, 0); } // Prefer structured details; fall back to parsing text content - let outputs = details?.results?.map((entry) => `${entry.expression} = ${entry.output}`) ?? []; + let outputs = details?.results?.map(entry => `${entry.expression} = ${entry.output}`) ?? []; if (outputs.length === 0 && textContent.trim()) { - const rawOutputs = textContent.split("\n").filter((line) => line.trim().length > 0); - const expressions = args?.calculations?.map((calc) => calc.expression) ?? []; + const rawOutputs = textContent.split("\n").filter(line => line.trim().length > 0); + const expressions = args?.calculations?.map(calc => calc.expression) ?? []; if (expressions.length === rawOutputs.length && expressions.length > 0) { outputs = rawOutputs.map((output, index) => `${expressions[index]} = ${output}`); } else { @@ -507,7 +507,7 @@ export const calculatorToolRenderer = { expanded, maxCollapsed: COLLAPSED_LIST_LIMIT, itemType: "result", - renderItem: (output) => uiTheme.fg("toolOutput", output), + renderItem: output => uiTheme.fg("toolOutput", output), }, uiTheme, ); diff --git a/packages/coding-agent/src/tools/complete.ts b/packages/coding-agent/src/tools/complete.ts index 6ff957320..fae66c477 100644 --- a/packages/coding-agent/src/tools/complete.ts +++ b/packages/coding-agent/src/tools/complete.ts @@ -3,7 +3,6 @@ * * Subagents must call this tool to finish and return structured JSON output. */ - import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core"; import { StringEnum } from "@oh-my-pi/pi-ai"; import type { Static, TObject } from "@sinclair/typebox"; @@ -46,7 +45,7 @@ function formatSchema(schema: unknown): string { function formatAjvErrors(errors: ErrorObject[] | null | undefined): string { if (!errors || errors.length === 0) return "Unknown schema validation error."; return errors - .map((err) => { + .map(err => { const path = err.instancePath ? `${err.instancePath}: ` : ""; return `${path}${err.message ?? "invalid"}`; }) @@ -137,6 +136,6 @@ export class CompleteTool implements AgentTool { // Register subprocess tool handler for extraction + termination. subprocessToolRegistry.register("complete", { - extractData: (event) => event.result?.details as CompleteDetails | undefined, + extractData: event => event.result?.details as CompleteDetails | undefined, shouldTerminate: () => true, }); diff --git a/packages/coding-agent/src/tools/fetch.ts b/packages/coding-agent/src/tools/fetch.ts index c19b8a6c6..6091a0548 100644 --- a/packages/coding-agent/src/tools/fetch.ts +++ b/packages/coding-agent/src/tools/fetch.ts @@ -497,13 +497,13 @@ function isLowQualityOutput(content: string): boolean { "please enable javascript", "browser not supported", ]; - if (content.length < 1024 && jsGated.some((t) => lower.includes(t))) { + if (content.length < 1024 && jsGated.some(t => lower.includes(t))) { return true; } // Mostly navigation (high link/menu density) - const lines = content.split("\n").filter((l) => l.trim()); - const shortLines = lines.filter((l) => l.trim().length < 40); + const lines = content.split("\n").filter(l => l.trim()); + const shortLines = lines.filter(l => l.trim().length < 40); if (lines.length > 10 && shortLines.length / lines.length > 0.7) { return true; } @@ -694,7 +694,7 @@ async function renderUrl( if (isHtml && !raw) { // 5A: Check for page-specific markdown alternate const alternates = parseAlternateLinks(rawContent, finalUrl); - const markdownAlt = alternates.find((alt) => alt.endsWith(".md") || alt.includes("markdown")); + const markdownAlt = alternates.find(alt => alt.endsWith(".md") || alt.includes("markdown")); if (markdownAlt) { const resolved = markdownAlt.startsWith("http") ? markdownAlt : new URL(markdownAlt, finalUrl).href; const altResult = await loadPage(resolved, { timeout, signal }); @@ -766,7 +766,7 @@ async function renderUrl( } // 5E: Check for feed alternates - const feedAlternates = alternates.filter((alt) => !alt.endsWith(".md") && !alt.includes("markdown")); + const feedAlternates = alternates.filter(alt => !alt.endsWith(".md") && !alt.includes("markdown")); for (const altUrl of feedAlternates.slice(0, 2)) { const resolved = altUrl.startsWith("http") ? altUrl : new URL(altUrl, finalUrl).href; const altResult = await loadPage(resolved, { timeout, signal }); @@ -983,7 +983,7 @@ function getDomain(url: string): string { /** Count non-empty lines */ function countNonEmptyLines(text: string): number { - return text.split("\n").filter((l) => l.trim()).length; + return text.split("\n").filter(l => l.trim()).length; } /** Render fetch call (URL preview) */ @@ -1036,7 +1036,7 @@ export function renderFetchResult( : contentText; const lineCount = countNonEmptyLines(contentBody); const charCount = contentBody.trim().length; - const contentLines = contentBody.split("\n").filter((l) => l.trim()); + const contentLines = contentBody.split("\n").filter(l => l.trim()); const metadataLines: string[] = [ `${uiTheme.fg("muted", "Content-Type:")} ${details.contentType || "unknown"}`, @@ -1060,12 +1060,10 @@ export function renderFetchResult( const previewLimit = expanded ? 12 : 3; const previewList = applyListLimit(contentLines, { headLimit: previewLimit }); - const previewLines = previewList.items.map((line) => truncate(line.trimEnd(), 120, uiTheme.format.ellipsis)); + const previewLines = previewList.items.map(line => truncate(line.trimEnd(), 120, uiTheme.format.ellipsis)); const remaining = Math.max(0, contentLines.length - previewLines.length); const contentPreviewLines = - previewLines.length > 0 - ? previewLines.map((line) => uiTheme.fg("dim", line)) - : [uiTheme.fg("dim", "(no content)")]; + previewLines.length > 0 ? previewLines.map(line => uiTheme.fg("dim", line)) : [uiTheme.fg("dim", "(no content)")]; if (remaining > 0) { const hint = formatExpandHint(uiTheme, expanded, true); contentPreviewLines.push( diff --git a/packages/coding-agent/src/tools/find.ts b/packages/coding-agent/src/tools/find.ts index 934a20bf7..ebdefe3df 100644 --- a/packages/coding-agent/src/tools/find.ts +++ b/packages/coding-agent/src/tools/find.ts @@ -163,7 +163,7 @@ export class FindTool implements AgentTool { } // Relativize paths - const relativized = results.map((p) => { + const relativized = results.map(p => { if (p.startsWith(searchPath)) { return p.slice(searchPath.length + 1); } @@ -330,7 +330,7 @@ export class FindTool implements AgentTool { const indexed = relativized.map((path, idx) => ({ path, mtime: mtimes[idx] })); indexed.sort((a, b) => b.mtime - a.mtime); relativized.length = 0; - relativized.push(...indexed.map((item) => item.path)); + relativized.push(...indexed.map(item => item.path)); } const listLimit = applyListLimit(relativized, { limit: effectiveLimit }); @@ -404,12 +404,12 @@ export const findToolRenderer = { const details = result.details; if (result.isError || details?.error) { - const errorText = details?.error || result.content?.find((c) => c.type === "text")?.text || "Unknown error"; + const errorText = details?.error || result.content?.find(c => c.type === "text")?.text || "Unknown error"; return new Text(formatErrorMessage(errorText, uiTheme), 0, 0); } const hasDetailedData = details?.fileCount !== undefined; - const textContent = result.content?.find((c) => c.type === "text")?.text; + const textContent = result.content?.find(c => c.type === "text")?.text; if (!hasDetailedData) { if ( @@ -421,7 +421,7 @@ export const findToolRenderer = { return new Text(formatEmptyMessage("No files found", uiTheme), 0, 0); } - const lines = textContent.split("\n").filter((l) => l.trim()); + const lines = textContent.split("\n").filter(l => l.trim()); const header = renderStatusLine( { icon: "success", @@ -437,7 +437,7 @@ export const findToolRenderer = { expanded, maxCollapsed: COLLAPSED_LIST_LIMIT, itemType: "file", - renderItem: (line) => uiTheme.fg("accent", line), + renderItem: line => uiTheme.fg("accent", line), }, uiTheme, ); @@ -467,7 +467,7 @@ export const findToolRenderer = { const fileLines = renderFileList( { - files: files.map((entry) => ({ path: entry, isDirectory: entry.endsWith("/") })), + files: files.map(entry => ({ path: entry, isDirectory: entry.endsWith("/") })), expanded, maxCollapsed: COLLAPSED_LIST_LIMIT, }, diff --git a/packages/coding-agent/src/tools/gemini-image.ts b/packages/coding-agent/src/tools/gemini-image.ts index ed26c2fbf..58fb46be5 100644 --- a/packages/coding-agent/src/tools/gemini-image.ts +++ b/packages/coding-agent/src/tools/gemini-image.ts @@ -151,7 +151,7 @@ function assemblePrompt(params: GeminiImageParams): string { if (params.style) parts.push(params.style); // Join with periods for sentence structure - let prompt = `${parts.map((p) => p.replace(/[.!,;:]+$/, "")).join(". ")}.`; + let prompt = `${parts.map(p => p.replace(/[.!,;:]+$/, "")).join(". ")}.`; // Text rendering specs if (params.text) { @@ -160,7 +160,7 @@ function assemblePrompt(params: GeminiImageParams): string { // Edit mode: changes and preserve directives if (params.changes?.length) { - prompt += `\n\nChanges:\n${params.changes.map((c) => `- ${c}`).join("\n")}`; + prompt += `\n\nChanges:\n${params.changes.map(c => `- ${c}`).join("\n")}`; if (params.preserve) { prompt += `\n\nPreserve: ${params.preserve}`; } @@ -330,8 +330,8 @@ function collectOpenRouterResponseText(message: OpenRouterMessage | undefined): } if (Array.isArray(message.content)) { const texts = message.content - .filter((part) => part.type === "text") - .map((part) => part.text) + .filter(part => part.type === "text") + .map(part => part.text) .filter((text): text is string => Boolean(text)); const combined = texts.join("\n").trim(); return combined.length > 0 ? combined : undefined; @@ -515,7 +515,7 @@ function buildResponseSummary( } function collectResponseText(parts: GeminiPart[]): string | undefined { - const texts = parts.map((part) => part.text).filter((text): text is string => Boolean(text)); + const texts = parts.map(part => part.text).filter((text): text is string => Boolean(text)); const combined = texts.join("\n").trim(); return combined.length > 0 ? combined : undefined; } diff --git a/packages/coding-agent/src/tools/grep.ts b/packages/coding-agent/src/tools/grep.ts index b34c7b12c..da1302a7a 100644 --- a/packages/coding-agent/src/tools/grep.ts +++ b/packages/coding-agent/src/tools/grep.ts @@ -73,8 +73,8 @@ export interface GrepOperations { } const defaultGrepOperations: GrepOperations = { - isDirectory: async (p) => (await Bun.file(p).stat()).isDirectory(), - readFile: (p) => Bun.file(p).text(), + isDirectory: async p => (await Bun.file(p).stat()).isDirectory(), + readFile: p => Bun.file(p).text(), }; export interface GrepToolOptions { @@ -287,7 +287,7 @@ export class GrepTool implements AgentTool { // For simple output modes (files_with_matches, count), process text directly if (effectiveOutputMode === "files_with_matches" || effectiveOutputMode === "count") { - const stdout = await child.text().catch((x) => { + const stdout = await child.text().catch(x => { if (x instanceof ptree.Exception && x.exitCode === 1) { return ""; } @@ -303,7 +303,7 @@ export class GrepTool implements AgentTool { const lines = stdout .trim() .split("\n") - .filter((line) => line.length > 0); + .filter(line => line.length > 0); if (lines.length === 0) { const details: GrepToolDetails = { @@ -371,7 +371,7 @@ export class GrepTool implements AgentTool { // For count mode, format as "path:count" if (effectiveOutputMode === "count") { - const formatted = processedLines.map((line) => { + const formatted = processedLines.map(line => { const separatorIndex = line.lastIndexOf(":"); const relative = formatPath(separatorIndex === -1 ? line : line.slice(0, separatorIndex)); const count = separatorIndex === -1 ? "0" : line.slice(separatorIndex + 1); @@ -383,7 +383,7 @@ export class GrepTool implements AgentTool { matchCount: simpleMatchCount, fileCount, files: simpleFileList, - fileMatches: simpleFileList.map((path) => ({ + fileMatches: simpleFileList.map(path => ({ path, count: simpleFileMatchCounts.get(path) ?? 0, })), @@ -401,7 +401,7 @@ export class GrepTool implements AgentTool { } // For files_with_matches, format paths - const formatted = processedLines.map((line) => formatPath(line)); + const formatted = processedLines.map(line => formatPath(line)); const output = formatted.join("\n"); const details: GrepToolDetails = { scopePath, @@ -547,7 +547,7 @@ export class GrepTool implements AgentTool { matchCount, fileCount: files.size, files: fileList, - fileMatches: fileList.map((path) => ({ + fileMatches: fileList.map(path => ({ path, count: fileMatchCounts.get(path) ?? 0, })), @@ -632,18 +632,18 @@ export const grepToolRenderer = { const details = result.details; if (result.isError || details?.error) { - const errorText = details?.error || result.content?.find((c) => c.type === "text")?.text || "Unknown error"; + const errorText = details?.error || result.content?.find(c => c.type === "text")?.text || "Unknown error"; return new Text(formatErrorMessage(errorText, uiTheme), 0, 0); } const hasDetailedData = details?.matchCount !== undefined || details?.fileCount !== undefined; if (!hasDetailedData) { - const textContent = result.content?.find((c) => c.type === "text")?.text; + const textContent = result.content?.find(c => c.type === "text")?.text; if (!textContent || textContent === "No matches found") { return new Text(formatEmptyMessage("No matches found", uiTheme), 0, 0); } - const lines = textContent.split("\n").filter((line) => line.trim() !== ""); + const lines = textContent.split("\n").filter(line => line.trim() !== ""); const description = args?.pattern ?? undefined; const header = renderStatusLine( { icon: "success", title: "Grep", description, meta: [formatCount("item", lines.length)] }, @@ -655,7 +655,7 @@ export const grepToolRenderer = { expanded, maxCollapsed: COLLAPSED_TEXT_LIMIT, itemType: "item", - renderItem: (line) => uiTheme.fg("toolOutput", line), + renderItem: line => uiTheme.fg("toolOutput", line), }, uiTheme, ); @@ -699,15 +699,15 @@ export const grepToolRenderer = { ); if (mode === "content") { - const textContent = result.content?.find((c) => c.type === "text")?.text ?? ""; - const contentLines = textContent.split("\n").filter((line) => line.trim().length > 0); + const textContent = result.content?.find(c => c.type === "text")?.text ?? ""; + const contentLines = textContent.split("\n").filter(line => line.trim().length > 0); const matchLines = renderTreeList( { items: contentLines, expanded, maxCollapsed: COLLAPSED_TEXT_LIMIT, itemType: "match", - renderItem: (line) => uiTheme.fg("toolOutput", line), + renderItem: line => uiTheme.fg("toolOutput", line), }, uiTheme, ); @@ -715,11 +715,11 @@ export const grepToolRenderer = { } const fileEntries: Array<{ path: string; count?: number }> = details?.fileMatches?.length - ? details.fileMatches.map((entry) => ({ path: entry.path, count: entry.count })) - : files.map((path) => ({ path })); + ? details.fileMatches.map(entry => ({ path: entry.path, count: entry.count })) + : files.map(path => ({ path })); const fileLines = renderFileList( { - files: fileEntries.map((entry) => ({ + files: fileEntries.map(entry => ({ path: entry.path, isDirectory: entry.path.endsWith("/"), meta: entry.count !== undefined ? `(${entry.count} match${entry.count !== 1 ? "es" : ""})` : undefined, diff --git a/packages/coding-agent/src/tools/index.ts b/packages/coding-agent/src/tools/index.ts index 77837003f..55d77a991 100644 --- a/packages/coding-agent/src/tools/index.ts +++ b/packages/coding-agent/src/tools/index.ts @@ -1,3 +1,34 @@ +import type { AgentTool } from "@oh-my-pi/pi-agent-core"; +import { logger } from "@oh-my-pi/pi-utils"; +import type { BashInterceptorRule } from "../config/settings-manager"; +import type { InternalUrlRouter } from "../internal-urls"; +import { getPreludeDocs, warmPythonEnvironment } from "../ipy/executor"; +import { checkPythonKernelAvailability } from "../ipy/kernel"; +import { LspTool } from "../lsp"; +import { EditTool } from "../patch"; +import type { ArtifactManager } from "../session/artifacts"; +import { TaskTool } from "../task"; +import type { AgentOutputManager } from "../task/output-manager"; +import type { EventBus } from "../utils/event-bus"; +import { time } from "../utils/timings"; +import { WebSearchTool } from "../web/search"; +import { AskTool } from "./ask"; +import { BashTool } from "./bash"; +import { CalculatorTool } from "./calculator"; +import { CompleteTool } from "./complete"; +import { FetchTool } from "./fetch"; +import { FindTool } from "./find"; +import { GrepTool } from "./grep"; +import { LsTool } from "./ls"; +import { NotebookTool } from "./notebook"; +import { wrapToolsWithMetaNotice } from "./output-meta"; +import { PythonTool } from "./python"; +import { ReadTool } from "./read"; +import { reportFindingTool } from "./review"; +import { loadSshTool } from "./ssh"; +import { TodoWriteTool } from "./todo-write"; +import { WriteTool } from "./write"; + // Exa MCP tools (22 tools) export { exaTools } from "../exa"; @@ -60,37 +91,6 @@ export { } from "./truncate"; export { WriteTool, type WriteToolDetails } from "./write"; -import type { AgentTool } from "@oh-my-pi/pi-agent-core"; -import { logger } from "@oh-my-pi/pi-utils"; -import type { BashInterceptorRule } from "../config/settings-manager"; -import type { InternalUrlRouter } from "../internal-urls"; -import { getPreludeDocs, warmPythonEnvironment } from "../ipy/executor"; -import { checkPythonKernelAvailability } from "../ipy/kernel"; -import { LspTool } from "../lsp"; -import { EditTool } from "../patch"; -import type { ArtifactManager } from "../session/artifacts"; -import { TaskTool } from "../task"; -import type { AgentOutputManager } from "../task/output-manager"; -import type { EventBus } from "../utils/event-bus"; -import { time } from "../utils/timings"; -import { WebSearchTool } from "../web/search"; -import { AskTool } from "./ask"; -import { BashTool } from "./bash"; -import { CalculatorTool } from "./calculator"; -import { CompleteTool } from "./complete"; -import { FetchTool } from "./fetch"; -import { FindTool } from "./find"; -import { GrepTool } from "./grep"; -import { LsTool } from "./ls"; -import { NotebookTool } from "./notebook"; -import { wrapToolsWithMetaNotice } from "./output-meta"; -import { PythonTool } from "./python"; -import { ReadTool } from "./read"; -import { reportFindingTool } from "./review"; -import { loadSshTool } from "./ssh"; -import { TodoWriteTool } from "./todo-write"; -import { WriteTool } from "./write"; - /** Tool type (AgentTool from pi-ai) */ export type Tool = AgentTool; @@ -155,26 +155,26 @@ type ToolFactory = (session: ToolSession) => Tool | null | Promise; export const BUILTIN_TOOLS: Record = { ask: AskTool.createIf, - bash: (s) => new BashTool(s), - python: (s) => new PythonTool(s), - calc: (s) => new CalculatorTool(s), + bash: s => new BashTool(s), + python: s => new PythonTool(s), + calc: s => new CalculatorTool(s), ssh: loadSshTool, - edit: (s) => new EditTool(s), - find: (s) => new FindTool(s), - grep: (s) => new GrepTool(s), - ls: (s) => new LsTool(s), + edit: s => new EditTool(s), + find: s => new FindTool(s), + grep: s => new GrepTool(s), + ls: s => new LsTool(s), lsp: LspTool.createIf, - notebook: (s) => new NotebookTool(s), - read: (s) => new ReadTool(s), + notebook: s => new NotebookTool(s), + read: s => new ReadTool(s), task: TaskTool.create, - todo_write: (s) => new TodoWriteTool(s), - fetch: (s) => new FetchTool(s), - web_search: (s) => new WebSearchTool(s), - write: (s) => new WriteTool(s), + todo_write: s => new TodoWriteTool(s), + fetch: s => new FetchTool(s), + web_search: s => new WebSearchTool(s), + write: s => new WriteTool(s), }; export const HIDDEN_TOOLS: Record = { - complete: (s) => new CompleteTool(s), + complete: s => new CompleteTool(s), report_finding: () => reportFindingTool, }; @@ -270,11 +270,11 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P requestedTools.push("complete"); } - const filteredRequestedTools = requestedTools?.filter((name) => name in allTools && isToolAllowed(name)); + const filteredRequestedTools = requestedTools?.filter(name => name in allTools && isToolAllowed(name)); const entries = filteredRequestedTools !== undefined - ? filteredRequestedTools.map((name) => [name, allTools[name]] as const) + ? filteredRequestedTools.map(name => [name, allTools[name]] as const) : [ ...Object.entries(BUILTIN_TOOLS).filter(([name]) => isToolAllowed(name)), ...(includeComplete ? ([["complete", HIDDEN_TOOLS.complete]] as const) : []), @@ -296,12 +296,12 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P if (slowTools.length > 0 && process.env.OMP_TIMING === "1") { logger.debug("Tool factory timings", { slowTools }); } - const tools = results.filter((r) => r.tool !== null).map((r) => r.tool as Tool); + const tools = results.filter(r => r.tool !== null).map(r => r.tool as Tool); const wrappedTools = wrapToolsWithMetaNotice(tools); if (filteredRequestedTools !== undefined) { const allowed = new Set(filteredRequestedTools); - return wrappedTools.filter((tool) => allowed.has(tool.name)); + return wrappedTools.filter(tool => allowed.has(tool.name)); } return wrappedTools; diff --git a/packages/coding-agent/src/tools/ls.ts b/packages/coding-agent/src/tools/ls.ts index a7cad1d3d..2734daee7 100644 --- a/packages/coding-agent/src/tools/ls.ts +++ b/packages/coding-agent/src/tools/ls.ts @@ -224,7 +224,7 @@ export const lsToolRenderer = { args?: LsRenderArgs, ): Component { const details = result.details; - const textContent = result.content?.find((c) => c.type === "text")?.text ?? ""; + const textContent = result.content?.find(c => c.type === "text")?.text ?? ""; if (result.isError) { const header = renderStatusLine({ icon: "error", title: "Ls", description: args?.path || "." }, uiTheme); @@ -243,7 +243,7 @@ export const lsToolRenderer = { let rawEntries: string[] | undefined = details?.rawEntries; if (entries.length === 0) { const rawLines = textContent.split("\n").filter((l: string) => l.trim()); - entries = rawLines.filter((line) => !/^\[.*\]$/.test(line.trim())); + entries = rawLines.filter(line => !/^\[.*\]$/.test(line.trim())); rawEntries = undefined; // Can't reliably extract raw paths from text } diff --git a/packages/coding-agent/src/tools/notebook.ts b/packages/coding-agent/src/tools/notebook.ts index bcd457c36..43b4a7cb9 100644 --- a/packages/coding-agent/src/tools/notebook.ts +++ b/packages/coding-agent/src/tools/notebook.ts @@ -249,7 +249,7 @@ export const notebookToolRenderer = { if (lineCount > 0) summaryParts.push(formatCount("line", lineCount)); if (totalCells !== undefined) summaryParts.push(`${totalCells} total`); - const outputLines = summaryParts.map((part) => uiTheme.fg("dim", part)); + const outputLines = summaryParts.map(part => uiTheme.fg("dim", part)); const codeText = cellSource.join(""); const language = cellType === "markdown" ? "markdown" : undefined; diff --git a/packages/coding-agent/src/tools/output-meta.ts b/packages/coding-agent/src/tools/output-meta.ts index 3a5040051..69fcb5cb1 100644 --- a/packages/coding-agent/src/tools/output-meta.ts +++ b/packages/coding-agent/src/tools/output-meta.ts @@ -4,7 +4,6 @@ * Tools populate details.meta using the fluent OutputMetaBuilder. * The tool wrapper automatically formats and appends notices at message boundary. */ - import type { AgentTool, AgentToolResult } from "@oh-my-pi/pi-agent-core"; import type { ImageContent, TextContent } from "@oh-my-pi/pi-ai"; import type { OutputSummary } from "../session/streaming-output"; diff --git a/packages/coding-agent/src/tools/python.ts b/packages/coding-agent/src/tools/python.ts index 2dc01af73..348727721 100644 --- a/packages/coding-agent/src/tools/python.ts +++ b/packages/coding-agent/src/tools/python.ts @@ -225,7 +225,7 @@ export class PythonTool implements AgentTool { const buildUpdateDetails = (): PythonToolDetails => { const details: PythonToolDetails = { - cells: cellResults.map((cell) => ({ + cells: cellResults.map(cell => ({ ...cell, statusEvents: cell.statusEvents ? [...cell.statusEvents] : undefined, })), @@ -257,7 +257,7 @@ export class PythonTool implements AgentTool { outputSink = new OutputSink({ artifactPath, artifactId, - onChunk: (chunk) => { + onChunk: chunk => { appendTail(chunk); pushUpdate(); }, @@ -288,7 +288,7 @@ export class PythonTool implements AgentTool { const executorOptions: PythonExecutorOptions = { ...baseExecutorOptions, reset: isFirstCell ? reset : false, - onChunk: async (chunk) => { + onChunk: async chunk => { await outputSink!.push(chunk); }, }; @@ -638,14 +638,14 @@ function formatStatusEventExpanded(event: PythonStatusEvent, theme: Theme): stri switch (op) { case "find": case "glob": - if (data.matches) addItems(data.matches as unknown[], (m) => String(m)); + if (data.matches) addItems(data.matches as unknown[], m => String(m)); break; case "ls": - if (data.items) addItems(data.items as unknown[], (m) => String(m)); + if (data.items) addItems(data.items as unknown[], m => String(m)); break; case "grep": if (data.hits) { - addItems(data.hits as unknown[], (h) => { + addItems(data.hits as unknown[], h => { const hit = h as { line: number; text: string }; return `${hit.line}: ${truncate(hit.text, 60, theme.format.ellipsis)}`; }); @@ -653,7 +653,7 @@ function formatStatusEventExpanded(event: PythonStatusEvent, theme: Theme): stri break; case "rgrep": if (data.hits) { - addItems(data.hits as unknown[], (h) => { + addItems(data.hits as unknown[], h => { const hit = h as { file: string; line: number; text: string }; return `${shortenPath(hit.file)}:${hit.line}: ${truncate(hit.text, 50, theme.format.ellipsis)}`; }); @@ -661,28 +661,28 @@ function formatStatusEventExpanded(event: PythonStatusEvent, theme: Theme): stri break; case "rsed": if (data.changed) { - addItems(data.changed as unknown[], (c) => { + addItems(data.changed as unknown[], c => { const change = c as { file: string; count: number }; return `${shortenPath(change.file)}: ${change.count} replacement${change.count !== 1 ? "s" : ""}`; }); } break; case "env": - if (data.keys) addItems(data.keys as unknown[], (k) => String(k), 10); + if (data.keys) addItems(data.keys as unknown[], k => String(k), 10); break; case "git_log": if (data.entries) { - addItems(data.entries as unknown[], (e) => { + addItems(data.entries as unknown[], e => { const entry = e as { sha: string; subject: string }; return `${entry.sha} ${truncate(entry.subject, 50, theme.format.ellipsis)}`; }); } break; case "git_status": - if (data.files) addItems(data.files as unknown[], (f) => String(f)); + if (data.files) addItems(data.files as unknown[], f => String(f)); break; case "git_branch": - if (data.branches) addItems(data.branches as unknown[], (b) => String(b)); + if (data.branches) addItems(data.branches as unknown[], b => String(b)); break; case "read": case "cat": @@ -748,7 +748,7 @@ function formatCellOutputLines( const rawLines = cell.output ? cell.output.split("\n") : []; const displayLines = expanded ? rawLines : rawLines.slice(-previewLines); const hiddenCount = rawLines.length - displayLines.length; - const outputLines = displayLines.map((line) => theme.fg("toolOutput", line)); + const outputLines = displayLines.map(line => theme.fg("toolOutput", line)); if (outputLines.length === 0) { return { lines: [], hiddenCount: 0 }; @@ -831,7 +831,7 @@ export const pythonToolRenderer = { const expanded = renderContext?.expanded ?? options.expanded; const previewLines = renderContext?.previewLines ?? PYTHON_DEFAULT_PREVIEW_LINES; - const output = renderContext?.output ?? (result.content?.find((c) => c.type === "text")?.text ?? "").trimEnd(); + const output = renderContext?.output ?? (result.content?.find(c => c.type === "text")?.text ?? "").trimEnd(); const jsonOutputs = details?.jsonOutputs ?? []; const jsonLines = jsonOutputs.flatMap((value, index) => { @@ -950,7 +950,7 @@ export const pythonToolRenderer = { if (expanded) { const styledOutput = combinedOutput .split("\n") - .map((line) => uiTheme.fg("toolOutput", line)) + .map(line => uiTheme.fg("toolOutput", line)) .join("\n"); const lines = [ styledOutput, @@ -963,7 +963,7 @@ export const pythonToolRenderer = { const styledOutput = combinedOutput .split("\n") - .map((line) => uiTheme.fg("toolOutput", line)) + .map(line => uiTheme.fg("toolOutput", line)) .join("\n"); const textContent = `\n${styledOutput}`; diff --git a/packages/coding-agent/src/tools/read.ts b/packages/coding-agent/src/tools/read.ts index 4677f23d5..c8907a1fc 100644 --- a/packages/coding-agent/src/tools/read.ts +++ b/packages/coding-agent/src/tools/read.ts @@ -202,8 +202,8 @@ async function listCandidateFiles( if (output) { const nestedGitignores = output .split("\n") - .map((line) => line.replace(/\r$/, "").trim()) - .filter((line) => line.length > 0); + .map(line => line.replace(/\r$/, "").trim()) + .filter(line => line.length > 0); for (const file of nestedGitignores) { const normalized = file.replace(/\\/g, "/"); if (normalized.includes("/node_modules/") || normalized.includes("/.git/")) { @@ -238,8 +238,8 @@ async function listCandidateFiles( const files = output .split("\n") - .map((line) => line.replace(/\r$/, "").trim()) - .filter((line) => line.length > 0); + .map(line => line.replace(/\r$/, "").trim()) + .filter(line => line.length > 0); return { files, truncated: files.length >= MAX_FUZZY_CANDIDATES }; } @@ -337,7 +337,7 @@ async function findReadPathSuggestions( }); const listLimit = applyListLimit(matches, { limit: MAX_FUZZY_RESULTS }); - const suggestions = listLimit.items.map((match) => match.path); + const suggestions = listLimit.items.map(match => match.path); return { suggestions, scopeLabel, truncated }; } @@ -446,7 +446,7 @@ export class ReadTool implements AgentTool { if (suggestions?.suggestions.length) { const scopeLabel = suggestions.scopeLabel ? ` in ${suggestions.scopeLabel}` : ""; - message += `\n\nClosest matches${scopeLabel}:\n${suggestions.suggestions.map((match) => `- ${match}`).join("\n")}`; + message += `\n\nClosest matches${scopeLabel}:\n${suggestions.suggestions.map(match => `- ${match}`).join("\n")}`; if (suggestions.truncated) { message += `\n[Search truncated to first ${MAX_FUZZY_CANDIDATES} paths. Refine the path if the match isn't listed.]`; } @@ -856,8 +856,8 @@ export const readToolRenderer = { args?: ReadRenderArgs, ): Component { const details = result.details; - const contentText = result.content?.find((c) => c.type === "text")?.text ?? ""; - const imageContent = result.content?.find((c) => c.type === "image"); + const contentText = result.content?.find(c => c.type === "text")?.text ?? ""; + const imageContent = result.content?.find(c => c.type === "image"); const rawPath = args?.file_path || args?.path || ""; const filePath = shortenPath(rawPath); const lang = getLanguageFromPath(rawPath); @@ -892,7 +892,7 @@ export const readToolRenderer = { { icon: "success", title: "Read", description: filePath || rawPath || "image" }, uiTheme, ); - const detailLines = contentText ? contentText.split("\n").map((line) => uiTheme.fg("toolOutput", line)) : []; + const detailLines = contentText ? contentText.split("\n").map(line => uiTheme.fg("toolOutput", line)) : []; const lines = [...detailLines, ...warningLines]; return { render: (width: number) => diff --git a/packages/coding-agent/src/tools/render-utils.ts b/packages/coding-agent/src/tools/render-utils.ts index d7243f0a1..42cb76066 100644 --- a/packages/coding-agent/src/tools/render-utils.ts +++ b/packages/coding-agent/src/tools/render-utils.ts @@ -4,7 +4,6 @@ * Provides consistent formatting, truncation, and display patterns across all * tool renderers to ensure a unified TUI experience. */ - import * as os from "node:os"; import type { Theme } from "../modes/theme/theme"; import { getTreeBranch } from "../tui/utils"; @@ -62,8 +61,8 @@ export function truncate(text: string, maxLen: number, ellipsis: string): string * Get first N lines of text as preview, with each line truncated. */ export function getPreviewLines(text: string, maxLines: number, maxLineLen: number, ellipsis: string): string[] { - const lines = text.split("\n").filter((l) => l.trim()); - return lines.slice(0, maxLines).map((l) => truncate(l.trim(), maxLineLen, ellipsis)); + const lines = text.split("\n").filter(l => l.trim()); + return lines.slice(0, maxLines).map(l => truncate(l.trim(), maxLineLen, ellipsis)); } // ============================================================================= @@ -554,7 +553,7 @@ export function truncateDiffByHunk( const segments = parseDiffSegments(lines); - const changeSegments = segments.filter((s) => s.isChange); + const changeSegments = segments.filter(s => s.isChange); const changeLineCount = changeSegments.reduce((sum, s) => sum + s.lines.length, 0); if (changeLineCount > maxLines) { @@ -579,7 +578,7 @@ export function truncateDiffByHunk( } const contextBudget = maxLines - changeLineCount; - const contextSegments = segments.filter((s) => !s.isChange && !s.isEllipsis); + const contextSegments = segments.filter(s => !s.isChange && !s.isEllipsis); const totalContextLines = contextSegments.reduce((sum, s) => sum + s.lines.length, 0); const kept: string[] = []; diff --git a/packages/coding-agent/src/tools/renderers.ts b/packages/coding-agent/src/tools/renderers.ts index cd6f2540b..e63e8f304 100644 --- a/packages/coding-agent/src/tools/renderers.ts +++ b/packages/coding-agent/src/tools/renderers.ts @@ -3,7 +3,6 @@ * * These provide rich visualization for tool calls and results in the TUI. */ - import type { Component } from "@oh-my-pi/pi-tui"; import type { RenderResultOptions } from "../extensibility/custom-tools/types"; import { lspToolRenderer } from "../lsp/render"; diff --git a/packages/coding-agent/src/tools/review.ts b/packages/coding-agent/src/tools/review.ts index c0db13d28..f0f6068cc 100644 --- a/packages/coding-agent/src/tools/review.ts +++ b/packages/coding-agent/src/tools/review.ts @@ -5,12 +5,17 @@ * Hidden by default - only enabled when explicitly listed in agent's tools. * Reviewers finish via `complete` tool with SubmitReviewDetails schema. */ - +// ───────────────────────────────────────────────────────────────────────────── +// Subprocess tool handlers - registered for extraction/rendering in task tool +// ───────────────────────────────────────────────────────────────────────────── +import path from "node:path"; import type { AgentTool } from "@oh-my-pi/pi-agent-core"; +import { StringEnum } from "@oh-my-pi/pi-ai"; import type { Component } from "@oh-my-pi/pi-tui"; import { Container, Text } from "@oh-my-pi/pi-tui"; import { Type } from "@sinclair/typebox"; import type { Theme, ThemeColor } from "../modes/theme/theme"; +import { subprocessToolRegistry } from "../task/subprocess-tool-registry"; export type FindingPriority = "P0" | "P1" | "P2" | "P3"; @@ -145,17 +150,9 @@ export interface SubmitReviewDetails { // Re-export types for external use export type { ReportFindingDetails }; -// ───────────────────────────────────────────────────────────────────────────── -// Subprocess tool handlers - registered for extraction/rendering in task tool -// ───────────────────────────────────────────────────────────────────────────── - -import path from "node:path"; -import { StringEnum } from "@oh-my-pi/pi-ai"; -import { subprocessToolRegistry } from "../task/subprocess-tool-registry"; - // Register report_finding handler subprocessToolRegistry.register("report_finding", { - extractData: (event) => event.result?.details as ReportFindingDetails | undefined, + extractData: event => event.result?.details as ReportFindingDetails | undefined, renderInline: (data, theme) => { const { label, icon, color } = getPriorityDisplay(data.priority, theme); diff --git a/packages/coding-agent/src/tools/ssh.ts b/packages/coding-agent/src/tools/ssh.ts index 7e943649d..6922bfc8f 100644 --- a/packages/coding-agent/src/tools/ssh.ts +++ b/packages/coding-agent/src/tools/ssh.ts @@ -177,7 +177,7 @@ export class SshTool implements AgentTool { compatEnabled: hostInfo.compatEnabled, artifactPath, artifactId, - onChunk: (chunk) => { + onChunk: chunk => { tailBuffer.append(chunk); if (onUpdate) { onUpdate({ @@ -211,7 +211,7 @@ export async function loadSshTool(session: ToolSession): Promise } const descriptionHosts = hostNames - .map((name) => hostsByName.get(name)) + .map(name => hostsByName.get(name)) .filter((host): host is SSHHost => host !== undefined); const description = await formatDescription(descriptionHosts); @@ -264,12 +264,12 @@ export const sshToolRenderer = { ); const outputLines: string[] = []; - const textContent = result.content?.find((c) => c.type === "text")?.text ?? ""; + const textContent = result.content?.find(c => c.type === "text")?.text ?? ""; const output = textContent.trimEnd(); if (output) { if (expanded) { - outputLines.push(...output.split("\n").map((line) => uiTheme.fg("toolOutput", line))); + outputLines.push(...output.split("\n").map(line => uiTheme.fg("toolOutput", line))); } else if (renderContext?.visualLines) { const { visualLines, skippedCount = 0, totalVisualLines = visualLines.length } = renderContext; if (skippedCount > 0) { @@ -280,7 +280,7 @@ export const sshToolRenderer = { ), ); } - const styledVisual = visualLines.map((line) => + const styledVisual = visualLines.map(line => line.includes("\x1b[") ? line : uiTheme.fg("toolOutput", line), ); outputLines.push(...styledVisual); @@ -289,7 +289,7 @@ export const sshToolRenderer = { const maxLines = 5; const displayLines = outputLinesRaw.slice(0, maxLines); const remaining = outputLinesRaw.length - maxLines; - outputLines.push(...displayLines.map((line) => uiTheme.fg("toolOutput", line))); + outputLines.push(...displayLines.map(line => uiTheme.fg("toolOutput", line))); if (remaining > 0) { outputLines.push( uiTheme.fg("dim", `${uiTheme.format.ellipsis} (${remaining} more lines) (ctrl+o to expand)`), diff --git a/packages/coding-agent/src/tools/todo-write.ts b/packages/coding-agent/src/tools/todo-write.ts index 64f2aae7b..e27f9c287 100644 --- a/packages/coding-agent/src/tools/todo-write.ts +++ b/packages/coding-agent/src/tools/todo-write.ts @@ -63,7 +63,7 @@ function normalizeTodoStatus(status?: string): TodoStatus { } function normalizeTodos(items: Array<{ id?: string; content?: string; status?: string }>): TodoItem[] { - return items.map((item) => { + return items.map(item => { if (!item.content) { throw new Error("Todo content is required."); } @@ -82,7 +82,7 @@ function normalizeTodos(items: Array<{ id?: string; content?: string; status?: s function validateSequentialTodos(todos: TodoItem[]): { valid: boolean; error?: string } { if (todos.length === 0) return { valid: true }; - const firstIncompleteIndex = todos.findIndex((todo) => todo.status !== "completed"); + const firstIncompleteIndex = todos.findIndex(todo => todo.status !== "completed"); if (firstIncompleteIndex >= 0) { for (let i = firstIncompleteIndex + 1; i < todos.length; i++) { if (todos[i].status === "completed") { @@ -128,9 +128,9 @@ async function loadTodoFile(filePath: string): Promise { function formatTodoSummary(todos: TodoItem[]): string { if (todos.length === 0) return "Todo list cleared."; - const completed = todos.filter((t) => t.status === "completed").length; - const inProgress = todos.filter((t) => t.status === "in_progress").length; - const pending = todos.filter((t) => t.status === "pending").length; + const completed = todos.filter(t => t.status === "completed").length; + const inProgress = todos.filter(t => t.status === "in_progress").length; + const pending = todos.filter(t => t.status === "pending").length; return `Saved ${todos.length} todos (${pending} pending, ${inProgress} in progress, ${completed} completed).`; } @@ -237,7 +237,7 @@ export const todoWriteToolRenderer = { uiTheme, ); if (todos.length === 0) { - const fallback = result.content?.find((c) => c.type === "text")?.text ?? "No todos"; + const fallback = result.content?.find(c => c.type === "text")?.text ?? "No todos"; return new Text([header, uiTheme.fg("dim", fallback)].join("\n"), 0, 0); } const lines = renderTreeList( @@ -246,7 +246,7 @@ export const todoWriteToolRenderer = { expanded, maxCollapsed: PREVIEW_LIMITS.COLLAPSED_ITEMS, itemType: "todo", - renderItem: (todo) => formatTodoLine(todo, uiTheme, ""), + renderItem: todo => formatTodoLine(todo, uiTheme, ""), }, uiTheme, ); diff --git a/packages/coding-agent/src/tools/tool-errors.ts b/packages/coding-agent/src/tools/tool-errors.ts index c81c50acc..631898267 100644 --- a/packages/coding-agent/src/tools/tool-errors.ts +++ b/packages/coding-agent/src/tools/tool-errors.ts @@ -39,7 +39,7 @@ export class MultiError extends ToolError { readonly errors: ErrorEntry[]; constructor(errors: ErrorEntry[]) { - super(errors.map((e) => e.message).join("; ")); + super(errors.map(e => e.message).join("; ")); this.name = "MultiError"; this.errors = errors; } @@ -49,11 +49,11 @@ export class MultiError extends ToolError { const e = this.errors[0]; return e.context ? `${e.context}: ${e.message}` : e.message; } - return this.errors.map((e) => (e.context ? `${e.context}: ${e.message}` : e.message)).join("\n"); + return this.errors.map(e => (e.context ? `${e.context}: ${e.message}` : e.message)).join("\n"); } static from(errors: Array): MultiError { - return new MultiError(errors.map((e) => (typeof e === "string" ? { message: e } : e))); + return new MultiError(errors.map(e => (typeof e === "string" ? { message: e } : e))); } } diff --git a/packages/coding-agent/src/tools/write.ts b/packages/coding-agent/src/tools/write.ts index 94e3269dd..19f365714 100644 --- a/packages/coding-agent/src/tools/write.ts +++ b/packages/coding-agent/src/tools/write.ts @@ -44,7 +44,7 @@ function getLspBatchRequest(toolCall: ToolCallContext | undefined): { id: string if (!hasOtherWrites) { return undefined; } - const hasLaterWrites = toolCall.toolCalls.slice(toolCall.index + 1).some((call) => LSP_BATCH_TOOLS.has(call.name)); + const hasLaterWrites = toolCall.toolCalls.slice(toolCall.index + 1).some(call => LSP_BATCH_TOOLS.has(call.name)); return { id: toolCall.batchId, flush: !hasLaterWrites }; } @@ -226,12 +226,12 @@ export const writeToolRenderer = { } if (result.details?.diagnostics) { - const diagText = formatDiagnostics(result.details.diagnostics, expanded, uiTheme, (fp) => + const diagText = formatDiagnostics(result.details.diagnostics, expanded, uiTheme, fp => uiTheme.getLangIcon(getLanguageFromPath(fp)), ); if (diagText.trim()) { const diagLines = diagText.split("\n"); - const firstNonEmpty = diagLines.findIndex((line) => line.trim()); + const firstNonEmpty = diagLines.findIndex(line => line.trim()); outputLines.push(...(firstNonEmpty >= 0 ? diagLines.slice(firstNonEmpty) : [])); } } diff --git a/packages/coding-agent/src/tui/code-cell.ts b/packages/coding-agent/src/tui/code-cell.ts index e62102bcf..e6d0a0cf3 100644 --- a/packages/coding-agent/src/tui/code-cell.ts +++ b/packages/coding-agent/src/tui/code-cell.ts @@ -1,7 +1,6 @@ /** * Render a code cell with optional output section. */ - import { highlightCode, type Theme } from "../modes/theme/theme"; import { formatDuration, formatExpandHint, formatMoreItems, replaceTabs } from "../tools/render-utils"; import { renderOutputBlock } from "./output-block"; @@ -91,7 +90,7 @@ export function renderCodeCell(options: CodeCellOptions, theme: Theme): string[] const maxLines = expanded ? rawLines.length : Math.min(rawLines.length, outputMaxLines); const displayLines = rawLines .slice(0, maxLines) - .map((line) => (line.includes("\x1b[") ? line : theme.fg("toolOutput", line))); + .map(line => (line.includes("\x1b[") ? line : theme.fg("toolOutput", line))); outputLines.push(...displayLines); const remaining = rawLines.length - maxLines; if (remaining > 0) { diff --git a/packages/coding-agent/src/tui/file-list.ts b/packages/coding-agent/src/tui/file-list.ts index 8364a8771..830295af0 100644 --- a/packages/coding-agent/src/tui/file-list.ts +++ b/packages/coding-agent/src/tui/file-list.ts @@ -1,7 +1,6 @@ /** * Render file listings with optional icons and metadata. */ - import type { Theme } from "../modes/theme/theme"; import { getLanguageFromPath } from "../modes/theme/theme"; import { renderTreeList } from "./tree-list"; @@ -28,7 +27,7 @@ export function renderFileList(options: FileListOptions, theme: Theme): string[] expanded, maxCollapsed, itemType: "file", - renderItem: (entry) => { + renderItem: entry => { const isDirectory = entry.isDirectory ?? entry.path.endsWith("/"); const displayPath = isDirectory && entry.path.endsWith("/") ? entry.path : entry.path; const lang = isDirectory ? undefined : getLanguageFromPath(displayPath); diff --git a/packages/coding-agent/src/tui/output-block.ts b/packages/coding-agent/src/tui/output-block.ts index 49b2c7ea2..cebe9ef3e 100644 --- a/packages/coding-agent/src/tui/output-block.ts +++ b/packages/coding-agent/src/tui/output-block.ts @@ -1,7 +1,6 @@ /** * Bordered output container with optional header and sections. */ - import { visibleWidth } from "@oh-my-pi/pi-tui"; import type { Theme } from "../modes/theme/theme"; import type { State } from "./types"; diff --git a/packages/coding-agent/src/tui/status-line.ts b/packages/coding-agent/src/tui/status-line.ts index cfeeba14f..40978b0aa 100644 --- a/packages/coding-agent/src/tui/status-line.ts +++ b/packages/coding-agent/src/tui/status-line.ts @@ -1,7 +1,6 @@ /** * Standardized status header rendering for tool output. */ - import type { Theme, ThemeColor } from "../modes/theme/theme"; import type { IconType } from "./types"; import { getStateIcon } from "./utils"; @@ -31,7 +30,7 @@ export function renderStatusLine(options: StatusLineOptions, theme: Theme): stri line += ` ${theme.fg(color, `${theme.format.bracketLeft}${label}${theme.format.bracketRight}`)}`; } - const meta = options.meta?.filter((value) => value.trim().length > 0) ?? []; + const meta = options.meta?.filter(value => value.trim().length > 0) ?? []; if (meta.length > 0) { line += ` ${theme.fg("dim", meta.join(theme.sep.dot))}`; } diff --git a/packages/coding-agent/src/tui/tree-list.ts b/packages/coding-agent/src/tui/tree-list.ts index a4f2242af..8b714f7ab 100644 --- a/packages/coding-agent/src/tui/tree-list.ts +++ b/packages/coding-agent/src/tui/tree-list.ts @@ -1,7 +1,6 @@ /** * Hierarchical tree list rendering helper. */ - import type { Theme } from "../modes/theme/theme"; import { formatMoreItems } from "../tools/render-utils"; import type { TreeContext } from "./types"; diff --git a/packages/coding-agent/src/tui/types.ts b/packages/coding-agent/src/tui/types.ts index b71c560a2..ceed30aef 100644 --- a/packages/coding-agent/src/tui/types.ts +++ b/packages/coding-agent/src/tui/types.ts @@ -1,7 +1,6 @@ /** * Shared types for TUI rendering components. */ - import type { Theme } from "../modes/theme/theme"; export type State = "pending" | "running" | "success" | "error" | "warning"; diff --git a/packages/coding-agent/src/tui/utils.ts b/packages/coding-agent/src/tui/utils.ts index 455c42233..b467429e1 100644 --- a/packages/coding-agent/src/tui/utils.ts +++ b/packages/coding-agent/src/tui/utils.ts @@ -1,13 +1,12 @@ /** * Shared helpers for tool-rendered UI components. */ - import { truncateToWidth as truncateToWidthBase, visibleWidth } from "@oh-my-pi/pi-tui"; import type { Theme, ThemeBg } from "../modes/theme/theme"; import type { IconType, State } from "./types"; export function buildTreePrefix(ancestors: boolean[], theme: Theme): string { - return ancestors.map((hasNext) => (hasNext ? `${theme.tree.vertical} ` : " ")).join(""); + return ancestors.map(hasNext => (hasNext ? `${theme.tree.vertical} ` : " ")).join(""); } export function getTreeBranch(isLast: boolean, theme: Theme): string { diff --git a/packages/coding-agent/src/utils/changelog.ts b/packages/coding-agent/src/utils/changelog.ts index 6a0ed71c0..c50eb3fac 100644 --- a/packages/coding-agent/src/utils/changelog.ts +++ b/packages/coding-agent/src/utils/changelog.ts @@ -91,7 +91,7 @@ export function getNewEntries(entries: ChangelogEntry[], lastVersion: string): C content: "", }; - return entries.filter((entry) => compareVersions(entry, last) > 0); + return entries.filter(entry => compareVersions(entry, last) > 0); } // Re-export getChangelogPath from paths.ts for convenience diff --git a/packages/coding-agent/src/utils/clipboard.ts b/packages/coding-agent/src/utils/clipboard.ts index 6bd5ec654..87ebb0930 100644 --- a/packages/coding-agent/src/utils/clipboard.ts +++ b/packages/coding-agent/src/utils/clipboard.ts @@ -16,18 +16,18 @@ function baseMimeType(mimeType: string): string { function selectPreferredImageMimeType(mimeTypes: string[]): string | null { const normalized = mimeTypes - .map((t) => t.trim()) + .map(t => t.trim()) .filter(Boolean) - .map((t) => ({ raw: t, base: baseMimeType(t) })); + .map(t => ({ raw: t, base: baseMimeType(t) })); for (const preferred of PREFERRED_IMAGE_MIME_TYPES) { - const match = normalized.find((t) => t.base === preferred); + const match = normalized.find(t => t.base === preferred); if (match) { return match.raw; } } - const anyImage = normalized.find((t) => t.base.startsWith("image/")); + const anyImage = normalized.find(t => t.base.startsWith("image/")); return anyImage?.raw ?? null; } @@ -125,7 +125,7 @@ async function readImageWayland(): Promise { const typeList = types .split(/\r?\n/) - .map((t) => t.trim()) + .map(t => t.trim()) .filter(Boolean); const selectedType = selectPreferredImageMimeType(typeList); @@ -149,7 +149,7 @@ async function readImageX11(): Promise { const candidateTypes = targets .split(/\r?\n/) - .map((t) => t.trim()) + .map(t => t.trim()) .filter(Boolean); const selectedType = selectPreferredImageMimeType(candidateTypes); diff --git a/packages/coding-agent/src/utils/file-mentions.ts b/packages/coding-agent/src/utils/file-mentions.ts index 5fc3f954e..42cfc1b43 100644 --- a/packages/coding-agent/src/utils/file-mentions.ts +++ b/packages/coding-agent/src/utils/file-mentions.ts @@ -5,7 +5,6 @@ * we automatically inject the file contents as a FileMentionMessage * so the agent doesn't need to read them manually. */ - import path from "node:path"; import type { AgentMessage } from "@oh-my-pi/pi-agent-core"; import type { FileMentionMessage } from "../session/messages"; diff --git a/packages/coding-agent/src/utils/fuzzy.ts b/packages/coding-agent/src/utils/fuzzy.ts index f37c25e12..4903b4790 100644 --- a/packages/coding-agent/src/utils/fuzzy.ts +++ b/packages/coding-agent/src/utils/fuzzy.ts @@ -71,7 +71,7 @@ export function fuzzyFilter(items: T[], query: string, getText: (item: T) => const tokens = query .trim() .split(/\s+/) - .filter((t) => t.length > 0); + .filter(t => t.length > 0); if (tokens.length === 0) { return items; @@ -104,5 +104,5 @@ export function fuzzyFilter(items: T[], query: string, getText: (item: T) => // Sort by score (asc, lower is better) results.sort((a, b) => a.totalScore - b.totalScore); - return results.map((r) => r.item); + return results.map(r => r.item); } diff --git a/packages/coding-agent/src/utils/shell-snapshot.ts b/packages/coding-agent/src/utils/shell-snapshot.ts index 691f9b50c..97cffa0dc 100644 --- a/packages/coding-agent/src/utils/shell-snapshot.ts +++ b/packages/coding-agent/src/utils/shell-snapshot.ts @@ -5,7 +5,6 @@ * .bashrc/.zshrc, which can be sourced before each command to provide a familiar * shell experience. */ - import * as fs from "node:fs"; import * as os from "node:os"; import * as path from "node:path"; diff --git a/packages/coding-agent/src/utils/shell.ts b/packages/coding-agent/src/utils/shell.ts index c912ae4ef..cff262bf2 100644 --- a/packages/coding-agent/src/utils/shell.ts +++ b/packages/coding-agent/src/utils/shell.ts @@ -137,7 +137,7 @@ export async function getShellConfig(): Promise { ` 1. Install Git for Windows: https://git-scm.com/download/win\n` + ` 2. Add your bash to PATH (Cygwin, MSYS2, etc.)\n` + ` 3. Set shellPath in ~/.omp/agent/settings.json\n\n` + - `Searched Git Bash in:\n${paths.map((p) => ` ${p}`).join("\n")}`, + `Searched Git Bash in:\n${paths.map(p => ` ${p}`).join("\n")}`, ); } diff --git a/packages/coding-agent/src/utils/title-generator.ts b/packages/coding-agent/src/utils/title-generator.ts index 11843cc67..69f7d2316 100644 --- a/packages/coding-agent/src/utils/title-generator.ts +++ b/packages/coding-agent/src/utils/title-generator.ts @@ -1,7 +1,6 @@ /** * Generate session titles using a smol, fast model. */ - import type { Api, Model } from "@oh-my-pi/pi-ai"; import { completeSimple } from "@oh-my-pi/pi-ai"; import { logger } from "@oh-my-pi/pi-utils"; @@ -21,7 +20,7 @@ function getTitleModelCandidates(registry: ModelRegistry, savedSmolModel?: strin const candidates: Model[] = []; const addCandidate = (model?: Model): void => { if (!model) return; - const exists = candidates.some((candidate) => candidate.provider === model.provider && candidate.id === model.id); + const exists = candidates.some(candidate => candidate.provider === model.provider && candidate.id === model.id); if (!exists) { candidates.push(model); } @@ -30,17 +29,17 @@ function getTitleModelCandidates(registry: ModelRegistry, savedSmolModel?: strin if (savedSmolModel) { const parsed = parseModelString(savedSmolModel); if (parsed) { - const match = availableModels.find((model) => model.provider === parsed.provider && model.id === parsed.id); + const match = availableModels.find(model => model.provider === parsed.provider && model.id === parsed.id); addCandidate(match); } } for (const pattern of SMOL_MODEL_PRIORITY) { const needle = pattern.toLowerCase(); - const exactMatch = availableModels.find((model) => model.id.toLowerCase() === needle); + const exactMatch = availableModels.find(model => model.id.toLowerCase() === needle); addCandidate(exactMatch); - const fuzzyMatch = availableModels.find((model) => model.id.toLowerCase().includes(needle)); + const fuzzyMatch = availableModels.find(model => model.id.toLowerCase().includes(needle)); addCandidate(fuzzyMatch); } diff --git a/packages/coding-agent/src/web/scrapers/artifacthub.ts b/packages/coding-agent/src/web/scrapers/artifacthub.ts index 67dccdd41..50fa76f21 100644 --- a/packages/coding-agent/src/web/scrapers/artifacthub.ts +++ b/packages/coding-agent/src/web/scrapers/artifacthub.ts @@ -119,7 +119,7 @@ export const handleArtifactHub: SpecialHandler = async ( // Maintainers if (pkg.maintainers?.length) { - const maintainerNames = pkg.maintainers.map((m) => m.name).join(", "); + const maintainerNames = pkg.maintainers.map(m => m.name).join(", "); md += `**Maintainers:** ${maintainerNames}\n`; } diff --git a/packages/coding-agent/src/web/scrapers/arxiv.ts b/packages/coding-agent/src/web/scrapers/arxiv.ts index 342442877..754dabef9 100644 --- a/packages/coding-agent/src/web/scrapers/arxiv.ts +++ b/packages/coding-agent/src/web/scrapers/arxiv.ts @@ -40,12 +40,12 @@ export const handleArxiv: SpecialHandler = async ( const summary = entry.querySelector("summary")?.text?.trim(); const authors = entry .querySelectorAll("author name") - .map((n) => n.text?.trim()) + .map(n => n.text?.trim()) .filter(Boolean); const published = entry.querySelector("published")?.text?.trim()?.split("T")[0]; const categories = entry .querySelectorAll("category") - .map((c) => c.getAttribute("term")) + .map(c => c.getAttribute("term")) .filter(Boolean); const pdfLink = entry.querySelector('link[title="pdf"]')?.getAttribute("href"); diff --git a/packages/coding-agent/src/web/scrapers/bluesky.ts b/packages/coding-agent/src/web/scrapers/bluesky.ts index e7a74f5ce..466d37195 100644 --- a/packages/coding-agent/src/web/scrapers/bluesky.ts +++ b/packages/coding-agent/src/web/scrapers/bluesky.ts @@ -93,7 +93,7 @@ function formatPost(post: BlueskyPost, isQuote = false): string { md += `> **${name}** (${handle}) - ${date}\n>\n`; md += post.record.text .split("\n") - .map((line) => `> ${line}`) + .map(line => `> ${line}`) .join("\n"); md += "\n"; } else { @@ -126,7 +126,7 @@ function formatPost(post: BlueskyPost, isQuote = false): string { md += `> **${rec.author.displayName || rec.author.handle}** (@${rec.author.handle})\n`; md += rec.value.text .split("\n") - .map((line) => `> ${line}`) + .map(line => `> ${line}`) .join("\n"); md += "\n"; } diff --git a/packages/coding-agent/src/web/scrapers/cheatsh.ts b/packages/coding-agent/src/web/scrapers/cheatsh.ts index e86884b10..85484da38 100644 --- a/packages/coding-agent/src/web/scrapers/cheatsh.ts +++ b/packages/coding-agent/src/web/scrapers/cheatsh.ts @@ -43,7 +43,7 @@ export const handleCheatSh: SpecialHandler = async ( const content = result.content.trim(); const lines = content.split("\n"); const hasCodeIndicators = lines.some( - (line) => + line => line.startsWith("$") || line.startsWith("#") || line.includes("()") || diff --git a/packages/coding-agent/src/web/scrapers/chocolatey.ts b/packages/coding-agent/src/web/scrapers/chocolatey.ts index 31376960a..1e62a7dd7 100644 --- a/packages/coding-agent/src/web/scrapers/chocolatey.ts +++ b/packages/coding-agent/src/web/scrapers/chocolatey.ts @@ -110,7 +110,7 @@ export const handleChocolatey: SpecialHandler = async ( if (pkg.LicenseUrl) md += `**License:** ${pkg.LicenseUrl}\n`; if (pkg.Tags) { - const tags = pkg.Tags.split(/\s+/).filter((t) => t.length > 0); + const tags = pkg.Tags.split(/\s+/).filter(t => t.length > 0); if (tags.length > 0) { md += `**Tags:** ${tags.join(", ")}\n`; } @@ -127,7 +127,7 @@ export const handleChocolatey: SpecialHandler = async ( if (pkg.Dependencies) { // Dependencies format: "id:version|id:version" - const deps = pkg.Dependencies.split("|").filter((d) => d.trim().length > 0); + const deps = pkg.Dependencies.split("|").filter(d => d.trim().length > 0); if (deps.length > 0) { md += `\n## Dependencies\n\n`; for (const dep of deps) { diff --git a/packages/coding-agent/src/web/scrapers/choosealicense.ts b/packages/coding-agent/src/web/scrapers/choosealicense.ts index edd310506..75727b625 100644 --- a/packages/coding-agent/src/web/scrapers/choosealicense.ts +++ b/packages/coding-agent/src/web/scrapers/choosealicense.ts @@ -16,14 +16,14 @@ function normalizeList(value: unknown): string[] { if (Array.isArray(value)) { return value .filter((item): item is string => typeof item === "string") - .map((item) => item.trim()) - .filter((item) => item.length > 0); + .map(item => item.trim()) + .filter(item => item.length > 0); } if (typeof value === "string") { return value .split(",") - .map((item) => item.trim()) - .filter((item) => item.length > 0); + .map(item => item.trim()) + .filter(item => item.length > 0); } return []; } diff --git a/packages/coding-agent/src/web/scrapers/cisa-kev.ts b/packages/coding-agent/src/web/scrapers/cisa-kev.ts index 95d5ede71..daf444b0b 100644 --- a/packages/coding-agent/src/web/scrapers/cisa-kev.ts +++ b/packages/coding-agent/src/web/scrapers/cisa-kev.ts @@ -60,7 +60,7 @@ export const handleCisaKev: SpecialHandler = async ( return null; } - const entry = data.vulnerabilities?.find((item) => item.cveID?.toUpperCase() === cveId); + const entry = data.vulnerabilities?.find(item => item.cveID?.toUpperCase() === cveId); if (!entry) return null; let md = `# ${entry.cveID}\n\n`; diff --git a/packages/coding-agent/src/web/scrapers/crossref.ts b/packages/coding-agent/src/web/scrapers/crossref.ts index 009740909..0dae1326f 100644 --- a/packages/coding-agent/src/web/scrapers/crossref.ts +++ b/packages/coding-agent/src/web/scrapers/crossref.ts @@ -42,7 +42,7 @@ function extractDoi(pathname: string): string | null { function formatAuthors(authors?: CrossrefAuthor[]): string | null { if (!authors || authors.length === 0) return null; const names = authors - .map((author) => { + .map(author => { if (author.name) return author.name; const parts = [author.given, author.family].filter(Boolean); return parts.length > 0 ? parts.join(" ") : null; @@ -67,7 +67,7 @@ function formatDate(date?: CrossrefDate): string | null { function formatAbstract(abstract?: string): string | null { if (!abstract) return null; - const normalized = abstract.replace(/<\/?jats:p[^>]*>/g, (match) => (match.startsWith("" : "

")); + const normalized = abstract.replace(/<\/?jats:p[^>]*>/g, match => (match.startsWith("" : "

")); const markdown = htmlToBasicMarkdown(normalized); return markdown.trim().length > 0 ? markdown : null; } diff --git a/packages/coding-agent/src/web/scrapers/devto.ts b/packages/coding-agent/src/web/scrapers/devto.ts index 60e81abf8..240ec89f0 100644 --- a/packages/coding-agent/src/web/scrapers/devto.ts +++ b/packages/coding-agent/src/web/scrapers/devto.ts @@ -62,7 +62,7 @@ export const handleDevTo: SpecialHandler = async ( md += `by **${article.user?.name || "Unknown"}** (@${article.user?.username || "unknown"})`; md += `${readTime}${reactStr}\n`; md += `*${new Date(article.published_at || article.published_timestamp || "").toISOString().split("T")[0]}*\n`; - if (tags.length > 0) md += `Tags: ${tags.map((t) => `#${t}`).join(", ")}\n`; + if (tags.length > 0) md += `Tags: ${tags.map(t => `#${t}`).join(", ")}\n`; if (article.description) md += `\n${article.description}\n`; md += `\n---\n\n`; } @@ -104,7 +104,7 @@ export const handleDevTo: SpecialHandler = async ( md += `### ${article.title}\n\n`; md += `${readTime.substring(3)}${reactStr}\n`; md += `*${new Date(article.published_at || article.published_timestamp || "").toISOString().split("T")[0]}*\n`; - if (tags.length > 0) md += `Tags: ${tags.map((t) => `#${t}`).join(", ")}\n`; + if (tags.length > 0) md += `Tags: ${tags.map(t => `#${t}`).join(", ")}\n`; if (article.description) md += `\n${article.description}\n`; md += `\n---\n\n`; } @@ -146,7 +146,7 @@ export const handleDevTo: SpecialHandler = async ( if (readTime > 0) md += `**Reading time:** ${readTime} min\n`; if (reactions > 0) md += `**Reactions:** ${formatCount(reactions)}\n`; if (comments > 0) md += `**Comments:** ${formatCount(comments)}\n`; - if (tags.length > 0) md += `**Tags:** ${tags.map((t) => `#${t}`).join(", ")}\n`; + if (tags.length > 0) md += `**Tags:** ${tags.map(t => `#${t}`).join(", ")}\n`; md += `\n---\n\n`; // Prefer body_markdown over body_html diff --git a/packages/coding-agent/src/web/scrapers/discogs.ts b/packages/coding-agent/src/web/scrapers/discogs.ts index 0f4c43def..df2c08977 100644 --- a/packages/coding-agent/src/web/scrapers/discogs.ts +++ b/packages/coding-agent/src/web/scrapers/discogs.ts @@ -4,7 +4,6 @@ * Uses the Discogs API to extract structured metadata about releases. * API docs: https://www.discogs.com/developers */ - import type { RenderResult, SpecialHandler } from "./types"; import { finalizeOutput, loadPage } from "./types"; @@ -76,7 +75,7 @@ interface DiscogsMaster { function formatArtists(artists: DiscogsArtist[] | undefined): string { if (!artists?.length) return "Unknown Artist"; return artists - .map((a) => { + .map(a => { const name = a.anv || a.name; const join = a.join || ", "; return name + (a.join ? ` ${join} ` : ""); @@ -126,7 +125,7 @@ function formatFormats(formats: DiscogsFormat[] | undefined): string { if (!formats?.length) return ""; return formats - .map((f) => { + .map(f => { const parts: string[] = []; if (f.qty && parseInt(f.qty, 10) > 1) parts.push(`${f.qty}×`); parts.push(f.name); @@ -142,7 +141,7 @@ function formatFormats(formats: DiscogsFormat[] | undefined): string { function formatLabels(labels: DiscogsLabel[] | undefined): string { if (!labels?.length) return ""; return labels - .map((l) => { + .map(l => { if (l.catno && l.catno !== "none") return `${l.name} (${l.catno})`; return l.name; }) diff --git a/packages/coding-agent/src/web/scrapers/discourse.ts b/packages/coding-agent/src/web/scrapers/discourse.ts index 71fd0745f..eed6cd4fe 100644 --- a/packages/coding-agent/src/web/scrapers/discourse.ts +++ b/packages/coding-agent/src/web/scrapers/discourse.ts @@ -157,7 +157,7 @@ export const handleDiscourse: SpecialHandler = async ( const fetchedAt = new Date().toISOString(); const posts: DiscoursePost[] = [...(topic.post_stream?.posts ?? [])]; - if (requestedPost && !posts.some((post) => post.id === requestedPost?.id)) { + if (requestedPost && !posts.some(post => post.id === requestedPost?.id)) { posts.unshift(requestedPost); } diff --git a/packages/coding-agent/src/web/scrapers/dockerhub.ts b/packages/coding-agent/src/web/scrapers/dockerhub.ts index 8cd0bc4cc..774709d85 100644 --- a/packages/coding-agent/src/web/scrapers/dockerhub.ts +++ b/packages/coding-agent/src/web/scrapers/dockerhub.ts @@ -134,7 +134,7 @@ export const handleDockerHub: SpecialHandler = async ( const size = tag.full_size ? formatSize(tag.full_size) : "-"; const archs = tag.images - ?.map((img) => img.architecture) + ?.map(img => img.architecture) .filter(Boolean) .join(", ") || "-"; const updated = tag.last_updated ? new Date(tag.last_updated).toISOString().split("T")[0] : "-"; diff --git a/packages/coding-agent/src/web/scrapers/fdroid.ts b/packages/coding-agent/src/web/scrapers/fdroid.ts index 14b65d674..dcc0e0805 100644 --- a/packages/coding-agent/src/web/scrapers/fdroid.ts +++ b/packages/coding-agent/src/web/scrapers/fdroid.ts @@ -30,7 +30,7 @@ function pickLocalizedText(value?: LocalizedText): string | undefined { if (typeof value === "string") return value; const preferred = value["en-US"] ?? value.en_US ?? value.en; if (preferred) return preferred; - const first = Object.values(value).find((entry) => typeof entry === "string"); + const first = Object.values(value).find(entry => typeof entry === "string"); return first; } @@ -61,7 +61,7 @@ function collectAntiFeatures(data: FdroidPackage): string[] { function resolveSuggestedVersion(data: FdroidPackage): string | undefined { if (data.suggestedVersionName) return data.suggestedVersionName; if (data.suggestedVersionCode) { - const match = data.packages?.find((pkg) => pkg.versionCode === data.suggestedVersionCode); + const match = data.packages?.find(pkg => pkg.versionCode === data.suggestedVersionCode); if (match?.versionName) return match.versionName; } return data.packages?.[0]?.versionName; diff --git a/packages/coding-agent/src/web/scrapers/firefox-addons.ts b/packages/coding-agent/src/web/scrapers/firefox-addons.ts index 393e5ca75..e73d969f7 100644 --- a/packages/coding-agent/src/web/scrapers/firefox-addons.ts +++ b/packages/coding-agent/src/web/scrapers/firefox-addons.ts @@ -71,7 +71,7 @@ function normalizeCategories(categories?: string[] | Record): } const seen = new Set(); - return values.filter((item) => { + return values.filter(item => { if (seen.has(item)) return false; seen.add(item); return true; @@ -135,8 +135,8 @@ export const handleFirefoxAddons: SpecialHandler = async ( const description = descriptionRaw ? htmlToBasicMarkdown(descriptionRaw) : undefined; const authors = (data.authors ?? []) - .map((author) => author.name ?? "") - .map((author) => author.trim()) + .map(author => author.name ?? "") + .map(author => author.trim()) .filter(Boolean); const ratingAverage = data.ratings?.average; diff --git a/packages/coding-agent/src/web/scrapers/flathub.ts b/packages/coding-agent/src/web/scrapers/flathub.ts index 6e94c82a9..017935b81 100644 --- a/packages/coding-agent/src/web/scrapers/flathub.ts +++ b/packages/coding-agent/src/web/scrapers/flathub.ts @@ -63,7 +63,7 @@ function normalizeStringList(value: unknown): string[] { if (typeof value === "string") { return value .split(/[,;\n]+/) - .map((item) => item.trim()) + .map(item => item.trim()) .filter(Boolean); } return []; diff --git a/packages/coding-agent/src/web/scrapers/github.ts b/packages/coding-agent/src/web/scrapers/github.ts index 302229965..3de286087 100644 --- a/packages/coding-agent/src/web/scrapers/github.ts +++ b/packages/coding-agent/src/web/scrapers/github.ts @@ -141,7 +141,7 @@ async function renderGitHubIssue( md += `**#${issue.number}** · ${issue.state} · opened by @${issue.user.login}\n`; md += `Created: ${issue.created_at} · Updated: ${issue.updated_at}\n`; if (issue.labels.length > 0) { - md += `Labels: ${issue.labels.map((l) => l.name).join(", ")}\n`; + md += `Labels: ${issue.labels.map(l => l.name).join(", ")}\n`; } md += `\n---\n\n`; md += issue.body || "*No description provided.*"; @@ -196,7 +196,7 @@ async function renderGitHubIssuesList( for (const issue of issues) { if (issue.pull_request) continue; // Skip PRs in issues list - const labels = issue.labels.length > 0 ? ` [${issue.labels.map((l) => l.name).join(", ")}]` : ""; + const labels = issue.labels.length > 0 ? ` [${issue.labels.map(l => l.name).join(", ")}]` : ""; md += `- **#${issue.number}** ${issue.title}${labels}\n`; md += ` by @${issue.user.login} · ${issue.comments} comments · ${issue.created_at}\n\n`; } @@ -259,7 +259,7 @@ async function renderGitHubTree( md += "```\n\n"; // Look for README in this directory - const readmeFile = items.find((item) => item.type === "file" && /^readme\.md$/i.test(item.name)); + const readmeFile = items.find(item => item.type === "file" && /^readme\.md$/i.test(item.name)); if (readmeFile) { const readmePath = dirPath ? `${dirPath}/${readmeFile.name}` : readmeFile.name; const rawUrl = `https://raw.githubusercontent.com/${gh.owner}/${gh.repo}/${ref}/${readmePath}`; diff --git a/packages/coding-agent/src/web/scrapers/gitlab.ts b/packages/coding-agent/src/web/scrapers/gitlab.ts index 64b811c79..afdc68ebc 100644 --- a/packages/coding-agent/src/web/scrapers/gitlab.ts +++ b/packages/coding-agent/src/web/scrapers/gitlab.ts @@ -197,8 +197,8 @@ async function renderGitLabTree( md += `**Ref:** ${gl.ref}\n\n`; // Separate directories and files - const dirs = tree.filter((item) => item.type === "tree"); - const files = tree.filter((item) => item.type === "blob"); + const dirs = tree.filter(item => item.type === "tree"); + const files = tree.filter(item => item.type === "blob"); if (dirs.length > 0) { md += `## Directories (${dirs.length})\n\n`; @@ -260,7 +260,7 @@ async function renderGitLabIssue( } if (issue.assignees && issue.assignees.length > 0) { - md += `**Assignees:** ${issue.assignees.map((a) => a.name).join(", ")}\n`; + md += `**Assignees:** ${issue.assignees.map(a => a.name).join(", ")}\n`; } md += `\n---\n\n## Description\n\n`; @@ -317,7 +317,7 @@ async function renderGitLabMR( } if (mr.assignees && mr.assignees.length > 0) { - md += `**Assignees:** ${mr.assignees.map((a) => a.name).join(", ")}\n`; + md += `**Assignees:** ${mr.assignees.map(a => a.name).join(", ")}\n`; } md += `\n---\n\n## Description\n\n`; diff --git a/packages/coding-agent/src/web/scrapers/hackernews.ts b/packages/coding-agent/src/web/scrapers/hackernews.ts index c79ee7d21..e82e2a040 100644 --- a/packages/coding-agent/src/web/scrapers/hackernews.ts +++ b/packages/coding-agent/src/web/scrapers/hackernews.ts @@ -29,7 +29,7 @@ async function fetchItem(id: number, timeout: number, signal?: AbortSignal): Pro } async function fetchItems(ids: number[], timeout: number, limit = 20, signal?: AbortSignal): Promise { - const promises = ids.slice(0, limit).map((id) => fetchItem(id, timeout, signal)); + const promises = ids.slice(0, limit).map(id => fetchItem(id, timeout, signal)); const results = await Promise.all(promises); return results.filter((item): item is HNItem => item !== null && !item.deleted && !item.dead); } @@ -103,7 +103,7 @@ async function renderStory(item: HNItem, timeout: number, depth = 0, signal?: Ab if (comment.text) { const text = decodeHNText(comment.text); const lines = text.split("\n"); - output += `${lines.map((line) => `${indent}${line}`).join("\n")}\n\n`; + output += `${lines.map(line => `${indent}${line}`).join("\n")}\n\n`; } if (comment.kids && comment.kids.length > 0 && depth < 1) { diff --git a/packages/coding-agent/src/web/scrapers/huggingface.ts b/packages/coding-agent/src/web/scrapers/huggingface.ts index 2ccf5db1f..9d5e9fd6e 100644 --- a/packages/coding-agent/src/web/scrapers/huggingface.ts +++ b/packages/coding-agent/src/web/scrapers/huggingface.ts @@ -360,7 +360,7 @@ export const handleHuggingFace: SpecialHandler = async (url: string, timeout: nu if (user.numSpaces !== undefined) md += `**Spaces:** ${formatCount(user.numSpaces)}\n`; if (user.orgs?.length) { - md += `**Organizations:** ${user.orgs.map((o) => o.name).join(", ")}\n`; + md += `**Organizations:** ${user.orgs.map(o => o.name).join(", ")}\n`; } const { content, truncated } = finalizeOutput(md); diff --git a/packages/coding-agent/src/web/scrapers/iacr.ts b/packages/coding-agent/src/web/scrapers/iacr.ts index 67a12baee..9c144e5d3 100644 --- a/packages/coding-agent/src/web/scrapers/iacr.ts +++ b/packages/coding-agent/src/web/scrapers/iacr.ts @@ -38,10 +38,10 @@ export const handleIacr: SpecialHandler = async ( doc.querySelector('meta[name="citation_title"]')?.getAttribute("content"); const authors = doc .querySelectorAll('meta[name="citation_author"]') - .map((m) => m.getAttribute("content")) + .map(m => m.getAttribute("content")) .filter(Boolean); // Abstract is in

after

Abstract
- const abstractHeading = doc.querySelectorAll("h5").find((h) => h.text?.includes("Abstract")); + const abstractHeading = doc.querySelectorAll("h5").find(h => h.text?.includes("Abstract")); const abstract = abstractHeading?.parentNode?.querySelector("p")?.text?.trim() || doc.querySelector('meta[name="description"]')?.getAttribute("content"); diff --git a/packages/coding-agent/src/web/scrapers/index.ts b/packages/coding-agent/src/web/scrapers/index.ts index 79a1ec976..a3b1afb43 100644 --- a/packages/coding-agent/src/web/scrapers/index.ts +++ b/packages/coding-agent/src/web/scrapers/index.ts @@ -3,7 +3,6 @@ * * Exports all special handlers for site-specific content extraction. */ - import { handleArtifactHub } from "./artifacthub"; import { handleArxiv } from "./arxiv"; import { handleAur } from "./aur"; diff --git a/packages/coding-agent/src/web/scrapers/jetbrains-marketplace.ts b/packages/coding-agent/src/web/scrapers/jetbrains-marketplace.ts index 88da5b605..930b80158 100644 --- a/packages/coding-agent/src/web/scrapers/jetbrains-marketplace.ts +++ b/packages/coding-agent/src/web/scrapers/jetbrains-marketplace.ts @@ -114,7 +114,7 @@ export const handleJetBrainsMarketplace: SpecialHandler = async ( const vendorName = plugin.vendor?.name ?? plugin.vendor?.publicName; const descriptionSource = plugin.description ?? plugin.preview ?? ""; const description = descriptionSource ? htmlToBasicMarkdown(descriptionSource) : ""; - const tags = (plugin.tags ?? []).map((tag) => tag.name).filter(Boolean) as string[]; + const tags = (plugin.tags ?? []).map(tag => tag.name).filter(Boolean) as string[]; const rating = extractRating(plugin); const buildCompatibility = update ? formatBuildCompatibility(update) : null; diff --git a/packages/coding-agent/src/web/scrapers/lemmy.ts b/packages/coding-agent/src/web/scrapers/lemmy.ts index 6d129c782..228f91df4 100644 --- a/packages/coding-agent/src/web/scrapers/lemmy.ts +++ b/packages/coding-agent/src/web/scrapers/lemmy.ts @@ -87,14 +87,14 @@ function formatAuthor(creator: LemmyCreator): string { function indentBlock(text: string, indent: string): string { return text .split("\n") - .map((line) => `${indent}${line}`) + .map(line => `${indent}${line}`) .join("\n"); } function renderComments(comments: LemmyCommentView[]): string { const childrenByParent = new Map(); - const commentIds = new Set(comments.map((view) => view.comment.id)); + const commentIds = new Set(comments.map(view => view.comment.id)); for (const commentView of comments) { const parentId = commentView.comment.parent_id; diff --git a/packages/coding-agent/src/web/scrapers/maven.ts b/packages/coding-agent/src/web/scrapers/maven.ts index 198c330c0..f10518846 100644 --- a/packages/coding-agent/src/web/scrapers/maven.ts +++ b/packages/coding-agent/src/web/scrapers/maven.ts @@ -124,10 +124,10 @@ export const handleMaven: SpecialHandler = async ( // Add available classifiers/extensions if present if (doc.ec && doc.ec.length > 0) { - const extensions = doc.ec.filter((e) => e && e !== "-"); + const extensions = doc.ec.filter(e => e && e !== "-"); if (extensions.length > 0) { md += `\n## Available Extensions\n\n`; - md += `${extensions.map((e) => `- ${e}`).join("\n")}\n`; + md += `${extensions.map(e => `- ${e}`).join("\n")}\n`; } } diff --git a/packages/coding-agent/src/web/scrapers/mdn.ts b/packages/coding-agent/src/web/scrapers/mdn.ts index 2e8cc4197..f5f7aad54 100644 --- a/packages/coding-agent/src/web/scrapers/mdn.ts +++ b/packages/coding-agent/src/web/scrapers/mdn.ts @@ -82,11 +82,9 @@ function convertMDNBody(sections: MDNSection[]): string { case "table": if (value.rows && value.rows.length > 0) { // Simple markdown table - const header = value.rows[0].map((cell) => htmlToBasicMarkdown(cell)).join(" | "); + const header = value.rows[0].map(cell => htmlToBasicMarkdown(cell)).join(" | "); const separator = value.rows[0].map(() => "---").join(" | "); - const bodyRows = value.rows - .slice(1) - .map((row) => row.map((cell) => htmlToBasicMarkdown(cell)).join(" | ")); + const bodyRows = value.rows.slice(1).map(row => row.map(cell => htmlToBasicMarkdown(cell)).join(" | ")); parts.push(`| ${header} |`); parts.push(`| ${separator} |`); diff --git a/packages/coding-agent/src/web/scrapers/metacpan.ts b/packages/coding-agent/src/web/scrapers/metacpan.ts index 5c2636c28..523ff1c08 100644 --- a/packages/coding-agent/src/web/scrapers/metacpan.ts +++ b/packages/coding-agent/src/web/scrapers/metacpan.ts @@ -183,7 +183,7 @@ function formatModuleMarkdown(module: ModuleResponse, release: ReleaseResponse | // Show runtime dependencies const runtimeDeps = release.dependency?.filter( - (d) => d.phase === "runtime" && d.relationship === "requires" && d.module !== "perl", + d => d.phase === "runtime" && d.relationship === "requires" && d.module !== "perl", ); if (runtimeDeps?.length) { md += `\n## Dependencies\n\n`; @@ -233,7 +233,7 @@ function formatReleaseMarkdown(release: ReleaseResponse): string { // Show runtime dependencies const runtimeDeps = release.dependency?.filter( - (d) => d.phase === "runtime" && d.relationship === "requires" && d.module !== "perl", + d => d.phase === "runtime" && d.relationship === "requires" && d.module !== "perl", ); if (runtimeDeps?.length) { md += `\n## Dependencies\n\n`; diff --git a/packages/coding-agent/src/web/scrapers/musicbrainz.ts b/packages/coding-agent/src/web/scrapers/musicbrainz.ts index 641feaf61..05f44d4ae 100644 --- a/packages/coding-agent/src/web/scrapers/musicbrainz.ts +++ b/packages/coding-agent/src/web/scrapers/musicbrainz.ts @@ -1,7 +1,6 @@ /** * MusicBrainz URL handler for artists, releases, and recordings */ - import type { RenderResult, SpecialHandler } from "./types"; import { finalizeOutput, loadPage } from "./types"; @@ -133,7 +132,7 @@ function formatArtistCredits(credits: MusicBrainzArtistCredit[] | undefined): st if (!credits?.length) return null; const names = credits - .map((credit) => credit.name || credit.artist?.name) + .map(credit => credit.name || credit.artist?.name) .filter((name): name is string => Boolean(name)); if (!names.length) return null; diff --git a/packages/coding-agent/src/web/scrapers/npm.ts b/packages/coding-agent/src/web/scrapers/npm.ts index 38e870313..35c894ba0 100644 --- a/packages/coding-agent/src/web/scrapers/npm.ts +++ b/packages/coding-agent/src/web/scrapers/npm.ts @@ -84,7 +84,7 @@ export const handleNpm: SpecialHandler = async ( const repoUrl = typeof pkg.repository === "string" ? pkg.repository : pkg.repository?.url; if (repoUrl) md += `**Repository:** ${repoUrl.replace(/^git\+/, "").replace(/\.git$/, "")}\n`; if (pkg.keywords?.length) md += `**Keywords:** ${pkg.keywords.join(", ")}\n`; - if (pkg.maintainers?.length) md += `**Maintainers:** ${pkg.maintainers.map((m) => m.name).join(", ")}\n`; + if (pkg.maintainers?.length) md += `**Maintainers:** ${pkg.maintainers.map(m => m.name).join(", ")}\n`; if (pkg.dependencies && Object.keys(pkg.dependencies).length > 0) { md += `\n## Dependencies\n\n`; diff --git a/packages/coding-agent/src/web/scrapers/nuget.ts b/packages/coding-agent/src/web/scrapers/nuget.ts index 25587aa08..15a21b653 100644 --- a/packages/coding-agent/src/web/scrapers/nuget.ts +++ b/packages/coding-agent/src/web/scrapers/nuget.ts @@ -106,7 +106,7 @@ export const handleNuGet: SpecialHandler = async ( if (pageItems) { const found = pageItems.find( - (item) => item.catalogEntry.version.toLowerCase() === requestedVersion.toLowerCase(), + item => item.catalogEntry.version.toLowerCase() === requestedVersion.toLowerCase(), ); if (found) { targetEntry = found.catalogEntry; @@ -162,7 +162,7 @@ export const handleNuGet: SpecialHandler = async ( // Show dependencies by target framework if (targetEntry.dependencyGroups?.length) { - const hasAnyDeps = targetEntry.dependencyGroups.some((g) => g.dependencies?.length); + const hasAnyDeps = targetEntry.dependencyGroups.some(g => g.dependencies?.length); if (hasAnyDeps) { md += `\n## Dependencies\n\n`; for (const group of targetEntry.dependencyGroups) { diff --git a/packages/coding-agent/src/web/scrapers/nvd.ts b/packages/coding-agent/src/web/scrapers/nvd.ts index 727e0d6f5..088e85d54 100644 --- a/packages/coding-agent/src/web/scrapers/nvd.ts +++ b/packages/coding-agent/src/web/scrapers/nvd.ts @@ -119,7 +119,7 @@ export const handleNvd: SpecialHandler = async ( md += ` · **Modified:** ${formatDate(vuln.lastModified)}\n\n`; // Description - const desc = vuln.descriptions.find((d) => d.lang === "en")?.value; + const desc = vuln.descriptions.find(d => d.lang === "en")?.value; if (desc) { md += `## Description\n\n${desc}\n\n`; } @@ -165,8 +165,8 @@ export const handleNvd: SpecialHandler = async ( // Weaknesses (CWE) const cwes = vuln.weaknesses - ?.flatMap((w) => w.description) - .filter((d) => d.lang === "en" && d.value !== "NVD-CWE-Other" && d.value !== "NVD-CWE-noinfo"); + ?.flatMap(w => w.description) + .filter(d => d.lang === "en" && d.value !== "NVD-CWE-Other" && d.value !== "NVD-CWE-noinfo"); if (cwes?.length) { md += `## Weaknesses\n\n`; diff --git a/packages/coding-agent/src/web/scrapers/ollama.ts b/packages/coding-agent/src/web/scrapers/ollama.ts index ff4cdb12e..64aa7585c 100644 --- a/packages/coding-agent/src/web/scrapers/ollama.ts +++ b/packages/coding-agent/src/web/scrapers/ollama.ts @@ -108,7 +108,7 @@ function formatSize(bytes: number): string { } function buildModelPath(parts: string[]): string { - return parts.map((part) => encodeURIComponent(part)).join("/"); + return parts.map(part => encodeURIComponent(part)).join("/"); } function parseOllamaUrl(url: string): { modelRef: string; baseRef: string; pageUrl: string } | null { @@ -152,7 +152,7 @@ function sortTags(tags: string[]): string[] { function formatTagList(tags: string[], maxItems: number): string { const limited = tags.slice(0, maxItems); - const formatted = limited.map((tag) => `\`${tag}\``).join(", "); + const formatted = limited.map(tag => `\`${tag}\``).join(", "); if (tags.length > maxItems) { return `${formatted} (and ${tags.length - maxItems} more)`; } @@ -205,17 +205,17 @@ export const handleOllama: SpecialHandler = async ( const baseLower = baseRef.toLowerCase(); const models = tagsData?.models ?? []; - const matchingModels = models.filter((model) => { + const matchingModels = models.filter(model => { const name = (model.model ?? model.name ?? "").toLowerCase(); return name === baseLower || name.startsWith(`${baseLower}:`); }); const tagRef = modelRef.includes(":") ? modelRef : null; - const selectedTag = tagRef ? matchingModels.find((model) => (model.model ?? model.name ?? "") === tagRef) : null; + const selectedTag = tagRef ? matchingModels.find(model => (model.model ?? model.name ?? "") === tagRef) : null; const availableTagsRaw = matchingModels - .map((model) => model.model ?? model.name ?? "") - .filter((tag) => tag.length > 0); + .map(model => model.model ?? model.name ?? "") + .filter(tag => tag.length > 0); const availableTags = sortTags(Array.from(new Set(availableTagsRaw))); const fallbackTags = sortTags(Array.from(new Set(htmlTags))); @@ -223,9 +223,7 @@ export const handleOllama: SpecialHandler = async ( const parameterSizes = collectParameterSizes(selectedTag ? [selectedTag] : matchingModels, htmlParameterSizes); - const sizes = matchingModels - .map((model) => model.size) - .filter((size): size is number => typeof size === "number"); + const sizes = matchingModels.map(model => model.size).filter((size): size is number => typeof size === "number"); let sizeLine: string | null = null; if (selectedTag?.size) { diff --git a/packages/coding-agent/src/web/scrapers/opencorporates.ts b/packages/coding-agent/src/web/scrapers/opencorporates.ts index 429d03fa9..cbf0709db 100644 --- a/packages/coding-agent/src/web/scrapers/opencorporates.ts +++ b/packages/coding-agent/src/web/scrapers/opencorporates.ts @@ -166,8 +166,8 @@ export const handleOpenCorporates: SpecialHandler = async ( // Officers/Directors if (company.officers && company.officers.length > 0) { - const activeOfficers = company.officers.filter((o) => !o.officer.inactive && !o.officer.end_date); - const inactiveOfficers = company.officers.filter((o) => o.officer.inactive || o.officer.end_date); + const activeOfficers = company.officers.filter(o => !o.officer.inactive && !o.officer.end_date); + const inactiveOfficers = company.officers.filter(o => o.officer.inactive || o.officer.end_date); if (activeOfficers.length > 0) { md += `## Current Officers (${activeOfficers.length})\n\n`; diff --git a/packages/coding-agent/src/web/scrapers/openlibrary.ts b/packages/coding-agent/src/web/scrapers/openlibrary.ts index 9c78c99c9..85d88a7f6 100644 --- a/packages/coding-agent/src/web/scrapers/openlibrary.ts +++ b/packages/coding-agent/src/web/scrapers/openlibrary.ts @@ -129,7 +129,7 @@ async function fetchWork(workId: string, timeout: number, signal?: AbortSignal): // Fetch author names if we have author keys if (work.authors?.length) { const authorNames = await fetchAuthorNames( - work.authors.map((a) => a.author.key), + work.authors.map(a => a.author.key), timeout, signal, ); @@ -179,7 +179,7 @@ async function fetchEdition(editionId: string, timeout: number, signal?: AbortSi // Fetch author names if (edition.authors?.length) { const authorNames = await fetchAuthorNames( - edition.authors.map((a) => a.key), + edition.authors.map(a => a.key), timeout, signal, ); @@ -250,11 +250,11 @@ async function fetchByIsbn(isbn: string, timeout: number, signal?: AbortSignal): let md = `# ${book.title}\n\n`; if (book.authors?.length) { - md += `**Authors:** ${book.authors.map((a) => a.name).join(", ")}\n`; + md += `**Authors:** ${book.authors.map(a => a.name).join(", ")}\n`; } if (book.publishers?.length) { - md += `**Publishers:** ${book.publishers.map((p) => p.name).join(", ")}\n`; + md += `**Publishers:** ${book.publishers.map(p => p.name).join(", ")}\n`; } if (book.publish_date) { @@ -280,7 +280,7 @@ async function fetchByIsbn(isbn: string, timeout: number, signal?: AbortSignal): if (book.subjects?.length) { md += `## Subjects\n\n${book.subjects .slice(0, 20) - .map((s) => s.name) + .map(s => s.name) .join(", ")}\n`; } @@ -291,7 +291,7 @@ async function fetchAuthorNames(authorKeys: string[], timeout: number, signal?: const names: string[] = []; // Fetch authors in parallel (limit to first 5) - const promises = authorKeys.slice(0, 5).map(async (key) => { + const promises = authorKeys.slice(0, 5).map(async key => { const authorKey = key.startsWith("/authors/") ? key : `/authors/${key}`; const apiUrl = `https://openlibrary.org${authorKey}.json`; try { diff --git a/packages/coding-agent/src/web/scrapers/orcid.ts b/packages/coding-agent/src/web/scrapers/orcid.ts index dfe26f532..e2dce8d24 100644 --- a/packages/coding-agent/src/web/scrapers/orcid.ts +++ b/packages/coding-agent/src/web/scrapers/orcid.ts @@ -1,7 +1,6 @@ /** * ORCID handler for web-fetch */ - import type { RenderResult, SpecialHandler } from "./types"; import { finalizeOutput, loadPage } from "./types"; diff --git a/packages/coding-agent/src/web/scrapers/osv.ts b/packages/coding-agent/src/web/scrapers/osv.ts index a2502d951..8b3c630d3 100644 --- a/packages/coding-agent/src/web/scrapers/osv.ts +++ b/packages/coding-agent/src/web/scrapers/osv.ts @@ -103,9 +103,9 @@ export const handleOsv: SpecialHandler = async ( } // Severity - const severities = vuln.severity || vuln.affected?.flatMap((a) => a.severity || []) || []; + const severities = vuln.severity || vuln.affected?.flatMap(a => a.severity || []) || []; if (severities.length) { - const formatted = severities.map((s) => `${s.type}: ${s.score}`).join(", "); + const formatted = severities.map(s => `${s.type}: ${s.score}`).join(", "); md += `**Severity:** ${formatted}\n`; } md += "\n"; diff --git a/packages/coding-agent/src/web/scrapers/packagist.ts b/packages/coding-agent/src/web/scrapers/packagist.ts index c454814ee..09f8938d1 100644 --- a/packages/coding-agent/src/web/scrapers/packagist.ts +++ b/packages/coding-agent/src/web/scrapers/packagist.ts @@ -121,7 +121,7 @@ export const handlePackagist: SpecialHandler = async ( // Maintainers if (pkg.maintainers?.length) { - md += `**Maintainers:** ${pkg.maintainers.map((m) => m.name).join(", ")}\n`; + md += `**Maintainers:** ${pkg.maintainers.map(m => m.name).join(", ")}\n`; } // Links diff --git a/packages/coding-agent/src/web/scrapers/pubmed.ts b/packages/coding-agent/src/web/scrapers/pubmed.ts index b9033bc8b..e35f2c390 100644 --- a/packages/coding-agent/src/web/scrapers/pubmed.ts +++ b/packages/coding-agent/src/web/scrapers/pubmed.ts @@ -1,7 +1,6 @@ /** * PubMed handler for web-fetch */ - import type { RenderResult, SpecialHandler } from "./types"; import { finalizeOutput, loadPage } from "./types"; @@ -100,7 +99,7 @@ export const handlePubMed: SpecialHandler = async ( // Authors if (article.authors && article.authors.length > 0) { - const authorNames = article.authors.map((a) => a.name).join(", "); + const authorNames = article.authors.map(a => a.name).join(", "); md += `**Authors:** ${authorNames}\n`; } diff --git a/packages/coding-agent/src/web/scrapers/rawg.ts b/packages/coding-agent/src/web/scrapers/rawg.ts index 5ff527c8e..f4f825776 100644 --- a/packages/coding-agent/src/web/scrapers/rawg.ts +++ b/packages/coding-agent/src/web/scrapers/rawg.ts @@ -58,10 +58,10 @@ export const handleRawg: SpecialHandler = async ( md += `**Rating:** ${game.rating.toFixed(2)} / 5\n`; } - const platforms = collectNames(game.platforms?.map((entry) => entry.platform?.name)); + const platforms = collectNames(game.platforms?.map(entry => entry.platform?.name)); if (platforms.length) md += `**Platforms:** ${platforms.join(", ")}\n`; - const genres = collectNames(game.genres?.map((entry) => entry.name)); + const genres = collectNames(game.genres?.map(entry => entry.name)); if (genres.length) md += `**Genres:** ${genres.join(", ")}\n`; md += `**RAWG:** https://rawg.io/games/${encodeURIComponent(slug)}\n`; diff --git a/packages/coding-agent/src/web/scrapers/readthedocs.ts b/packages/coding-agent/src/web/scrapers/readthedocs.ts index bbfc18225..fc623b347 100644 --- a/packages/coding-agent/src/web/scrapers/readthedocs.ts +++ b/packages/coding-agent/src/web/scrapers/readthedocs.ts @@ -1,7 +1,6 @@ /** * Read the Docs handler for web-fetch */ - import { parse as parseHtml } from "node-html-parser"; import type { RenderResult, SpecialHandler } from "./types"; import { finalizeOutput, htmlToBasicMarkdown, loadPage } from "./types"; @@ -62,7 +61,7 @@ export const handleReadTheDocs: SpecialHandler = async ( ?.querySelectorAll( ".headerlink, .viewcode-link, nav, .sidebar, footer, .related, .sphinxsidebar, .toctree-wrapper", ) - .forEach((el) => { + .forEach(el => { el.remove(); }); diff --git a/packages/coding-agent/src/web/scrapers/repology.ts b/packages/coding-agent/src/web/scrapers/repology.ts index 507aa0804..22efb2f49 100644 --- a/packages/coding-agent/src/web/scrapers/repology.ts +++ b/packages/coding-agent/src/web/scrapers/repology.ts @@ -95,7 +95,7 @@ function prettifyRepo(repo: string): string { // Fallback: titlecase with underscores replaced return repo .split("_") - .map((w) => w.charAt(0).toUpperCase() + w.slice(1)) + .map(w => w.charAt(0).toUpperCase() + w.slice(1)) .join(" "); } @@ -157,7 +157,7 @@ export const handleRepology: SpecialHandler = async ( // If no newest found, find the highest version if (newestVersions.size === 0) { - const versions = packages.map((p) => p.version); + const versions = packages.map(p => p.version); if (versions.length > 0) newestVersions.add(versions[0]); } diff --git a/packages/coding-agent/src/web/scrapers/rfc.ts b/packages/coding-agent/src/web/scrapers/rfc.ts index 6765583a8..c1ec3dea5 100644 --- a/packages/coding-agent/src/web/scrapers/rfc.ts +++ b/packages/coding-agent/src/web/scrapers/rfc.ts @@ -135,7 +135,7 @@ export const handleRfc: SpecialHandler = async ( // Authors if (metadata.authors?.length) { const authorList = metadata.authors - .map((a) => (a.affiliation ? `${a.name} (${a.affiliation})` : a.name)) + .map(a => (a.affiliation ? `${a.name} (${a.affiliation})` : a.name)) .join(", "); md += `**Authors:** ${authorList}\n`; } diff --git a/packages/coding-agent/src/web/scrapers/searchcode.ts b/packages/coding-agent/src/web/scrapers/searchcode.ts index c08fce95d..a361a725e 100644 --- a/packages/coding-agent/src/web/scrapers/searchcode.ts +++ b/packages/coding-agent/src/web/scrapers/searchcode.ts @@ -27,12 +27,12 @@ function parseLineNumbers(lines: SearchcodeResult["lines"]): number[] | null { if (typeof lines === "string") { const parts = lines.split(/[,\s]+/).filter(Boolean); - const parsed = parts.map((part) => Number.parseInt(part, 10)).filter((value) => Number.isFinite(value)); + const parsed = parts.map(part => Number.parseInt(part, 10)).filter(value => Number.isFinite(value)); return parsed.length ? parsed : null; } if (Array.isArray(lines)) { - const parsed = lines.map((part) => Number.parseInt(String(part), 10)).filter((value) => Number.isFinite(value)); + const parsed = lines.map(part => Number.parseInt(String(part), 10)).filter(value => Number.isFinite(value)); return parsed.length ? parsed : null; } diff --git a/packages/coding-agent/src/web/scrapers/semantic-scholar.ts b/packages/coding-agent/src/web/scrapers/semantic-scholar.ts index 9e188bc43..88f0f68a1 100644 --- a/packages/coding-agent/src/web/scrapers/semantic-scholar.ts +++ b/packages/coding-agent/src/web/scrapers/semantic-scholar.ts @@ -114,7 +114,7 @@ export const handleSemanticScholar: SpecialHandler = async (url: string, timeout sections.push(""); if (paper.authors && paper.authors.length > 0) { - const authorList = paper.authors.map((a) => a.name).join(", "); + const authorList = paper.authors.map(a => a.name).join(", "); sections.push(`**Authors:** ${authorList}`); sections.push(""); } diff --git a/packages/coding-agent/src/web/scrapers/snapcraft.ts b/packages/coding-agent/src/web/scrapers/snapcraft.ts index 41b265ac7..55de57270 100644 --- a/packages/coding-agent/src/web/scrapers/snapcraft.ts +++ b/packages/coding-agent/src/web/scrapers/snapcraft.ts @@ -81,9 +81,9 @@ function formatChannelName(channel?: SnapcraftChannel): string | null { } function pickVersionFromChannels(entries: SnapcraftChannelMapEntry[]): string | undefined { - const stable = entries.find((entry) => entry.channel?.risk === "stable" && entry.version); + const stable = entries.find(entry => entry.channel?.risk === "stable" && entry.version); if (stable?.version) return stable.version; - const first = entries.find((entry) => entry.version); + const first = entries.find(entry => entry.version); return first?.version; } diff --git a/packages/coding-agent/src/web/scrapers/sourcegraph.ts b/packages/coding-agent/src/web/scrapers/sourcegraph.ts index 55cc614da..a01e40b42 100644 --- a/packages/coding-agent/src/web/scrapers/sourcegraph.ts +++ b/packages/coding-agent/src/web/scrapers/sourcegraph.ts @@ -129,7 +129,7 @@ function parseSourcegraphUrl(url: string): SourcegraphTarget | null { const parts = parsed.pathname .split("/") .filter(Boolean) - .map((part) => decodeURIComponent(part)); + .map(part => decodeURIComponent(part)); if (parts.length < 3) return null; const hyphenIndex = parts.indexOf("-"); diff --git a/packages/coding-agent/src/web/scrapers/spdx.ts b/packages/coding-agent/src/web/scrapers/spdx.ts index 52853cc80..c41a0b7e0 100644 --- a/packages/coding-agent/src/web/scrapers/spdx.ts +++ b/packages/coding-agent/src/web/scrapers/spdx.ts @@ -30,11 +30,11 @@ function formatYesNo(value?: boolean): string { function collectCrossReferences(license: SpdxLicense): string[] { const ordered = (license.crossRef ?? []) - .filter((ref) => ref.url) + .filter(ref => ref.url) .sort((a, b) => (a.order ?? 0) - (b.order ?? 0)) - .map((ref) => ref.url as string); + .map(ref => ref.url as string); - const seeAlso = (license.seeAlso ?? []).filter((url) => url); + const seeAlso = (license.seeAlso ?? []).filter(url => url); const combined = [...ordered, ...seeAlso]; return combined.filter((url, index) => combined.indexOf(url) === index); } diff --git a/packages/coding-agent/src/web/scrapers/spotify.ts b/packages/coding-agent/src/web/scrapers/spotify.ts index bd037f0f4..185e18850 100644 --- a/packages/coding-agent/src/web/scrapers/spotify.ts +++ b/packages/coding-agent/src/web/scrapers/spotify.ts @@ -4,7 +4,6 @@ * Uses oEmbed API and Open Graph metadata to extract information * from Spotify URLs without requiring authentication. */ - import type { SpecialHandler } from "./types"; import { finalizeOutput, loadPage } from "./types"; diff --git a/packages/coding-agent/src/web/scrapers/types.ts b/packages/coding-agent/src/web/scrapers/types.ts index 89b3ea912..9ba123d00 100644 --- a/packages/coding-agent/src/web/scrapers/types.ts +++ b/packages/coding-agent/src/web/scrapers/types.ts @@ -1,7 +1,6 @@ /** * Shared types and utilities for web-fetch handlers */ - import { ToolAbortError } from "../../tools/tool-errors"; export interface RenderResult { diff --git a/packages/coding-agent/src/web/scrapers/wikidata.ts b/packages/coding-agent/src/web/scrapers/wikidata.ts index 0c080396c..52e3736d7 100644 --- a/packages/coding-agent/src/web/scrapers/wikidata.ts +++ b/packages/coding-agent/src/web/scrapers/wikidata.ts @@ -179,8 +179,8 @@ export const handleWikidata: SpecialHandler = async ( // Sort: known properties first, then by property ID processedProperties.sort((a, b) => { - const aKnown = Object.values(PROPERTY_LABELS).some((l) => a.includes(`**${l}:**`)); - const bKnown = Object.values(PROPERTY_LABELS).some((l) => b.includes(`**${l}:**`)); + const aKnown = Object.values(PROPERTY_LABELS).some(l => a.includes(`**${l}:**`)); + const bKnown = Object.values(PROPERTY_LABELS).some(l => b.includes(`**${l}:**`)); if (aKnown && !bKnown) return -1; if (!aKnown && bKnown) return 1; return a.localeCompare(b); @@ -254,7 +254,7 @@ function getLocalizedAliases( if (!aliases) return []; const langAliases = aliases[preferredLang]; if (!langAliases) return []; - return langAliases.map((a) => a.value); + return langAliases.map(a => a.value); } /** diff --git a/packages/coding-agent/src/web/scrapers/youtube.ts b/packages/coding-agent/src/web/scrapers/youtube.ts index 25fafc065..b32b73dbb 100644 --- a/packages/coding-agent/src/web/scrapers/youtube.ts +++ b/packages/coding-agent/src/web/scrapers/youtube.ts @@ -304,7 +304,7 @@ export const handleYouTube: SpecialHandler = async ( } finally { // Cleanup temp files (fire-and-forget with error suppression) Array.fromAsync(new Bun.Glob(`${tmpBase}*`).scan({ absolute: true })) - .then((tmpFiles) => Promise.all(tmpFiles.map((f) => fs.unlink(f).catch(() => {})))) + .then(tmpFiles => Promise.all(tmpFiles.map(f => fs.unlink(f).catch(() => {})))) .catch(() => {}); } diff --git a/packages/coding-agent/src/web/search/auth.ts b/packages/coding-agent/src/web/search/auth.ts index c5ca88288..e6faee6fd 100644 --- a/packages/coding-agent/src/web/search/auth.ts +++ b/packages/coding-agent/src/web/search/auth.ts @@ -7,7 +7,6 @@ * 3. OAuth credentials in ~/.omp/agent/agent.db (with expiry check) * 4. ANTHROPIC_API_KEY / ANTHROPIC_BASE_URL fallback */ - import * as os from "node:os"; import * as path from "node:path"; import { buildAnthropicHeaders as buildProviderAnthropicHeaders } from "@oh-my-pi/pi-ai"; diff --git a/packages/coding-agent/src/web/search/index.ts b/packages/coding-agent/src/web/search/index.ts index dd3b8289f..d56ad6fb4 100644 --- a/packages/coding-agent/src/web/search/index.ts +++ b/packages/coding-agent/src/web/search/index.ts @@ -11,7 +11,6 @@ * - web_search_linkedin: Search LinkedIn profiles and companies * - web_search_company: Comprehensive company research */ - import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@oh-my-pi/pi-agent-core"; import { StringEnum } from "@oh-my-pi/pi-ai"; import { Type } from "@sinclair/typebox"; @@ -94,7 +93,7 @@ function formatProviderLabel(provider: WebSearchProvider): string { } function formatProviderList(providers: WebSearchProvider[]): string { - return providers.map((provider) => formatProviderLabel(provider)).join(", "); + return providers.map(provider => formatProviderLabel(provider)).join(", "); } function buildNoProviderError(): string { diff --git a/packages/coding-agent/src/web/search/providers/anthropic.ts b/packages/coding-agent/src/web/search/providers/anthropic.ts index cb1362254..020b67835 100644 --- a/packages/coding-agent/src/web/search/providers/anthropic.ts +++ b/packages/coding-agent/src/web/search/providers/anthropic.ts @@ -4,7 +4,6 @@ * Uses Claude's built-in web_search_20250305 tool to search the web. * Returns synthesized answers with citations and source metadata. */ - import { applyClaudeToolPrefix, buildAnthropicSystemBlocks, stripClaudeToolPrefix } from "@oh-my-pi/pi-ai"; import { buildAnthropicHeaders, buildAnthropicUrl, findAnthropicAuth, getEnv } from "../../../web/search/auth"; import type { diff --git a/packages/coding-agent/src/web/search/providers/exa.ts b/packages/coding-agent/src/web/search/providers/exa.ts index acce55c95..67cededfe 100644 --- a/packages/coding-agent/src/web/search/providers/exa.ts +++ b/packages/coding-agent/src/web/search/providers/exa.ts @@ -4,7 +4,6 @@ * High-quality neural search via Exa Search API. * Returns structured search results with optional content extraction. */ - import * as os from "node:os"; import type { WebSearchResponse, WebSearchSource } from "../../../web/search/types"; import { WebSearchProviderError } from "../../../web/search/types"; diff --git a/packages/coding-agent/src/web/search/providers/perplexity.ts b/packages/coding-agent/src/web/search/providers/perplexity.ts index e9924d3cb..f49842c8d 100644 --- a/packages/coding-agent/src/web/search/providers/perplexity.ts +++ b/packages/coding-agent/src/web/search/providers/perplexity.ts @@ -4,7 +4,6 @@ * Supports both sonar (fast) and sonar-pro (comprehensive) models. * Returns synthesized answers with citations and related questions. */ - import * as os from "node:os"; import type { PerplexityRequest, @@ -123,7 +122,7 @@ function parseResponse(response: PerplexityResponse): WebSearchResponse { const searchResults = response.search_results ?? []; for (const url of citationUrls) { - const searchResult = searchResults.find((r) => r.url === url); + const searchResult = searchResults.find(r => r.url === url); sources.push({ title: searchResult?.title ?? url, url, diff --git a/packages/coding-agent/src/web/search/render.ts b/packages/coding-agent/src/web/search/render.ts index 288faf336..b4fce0c96 100644 --- a/packages/coding-agent/src/web/search/render.ts +++ b/packages/coding-agent/src/web/search/render.ts @@ -3,7 +3,6 @@ * * Tree-based rendering with collapsed/expanded states for web search results. */ - import type { Component } from "@oh-my-pi/pi-tui"; import { Text } from "@oh-my-pi/pi-tui"; import type { RenderResultOptions } from "../../extensibility/custom-tools/types"; @@ -34,9 +33,9 @@ const MAX_QUERY_LEN = 90; const MAX_REQUEST_ID_LEN = 36; function renderFallbackText(contentText: string, expanded: boolean, theme: Theme): Component { - const lines = contentText.split("\n").filter((line) => line.trim()); + const lines = contentText.split("\n").filter(line => line.trim()); const maxLines = expanded ? lines.length : 6; - const displayLines = lines.slice(0, maxLines).map((line) => truncate(line.trim(), 110, theme.format.ellipsis)); + const displayLines = lines.slice(0, maxLines).map(line => truncate(line.trim(), 110, theme.format.ellipsis)); const remaining = lines.length - displayLines.length; const headerIcon = formatStatusIcon("warning", theme); @@ -81,7 +80,7 @@ export function renderWebSearchResult( return new Text(theme.fg("error", `Error: ${details.error}`), 0, 0); } - const rawText = result.content?.find((block) => block.type === "text")?.text?.trim() ?? ""; + const rawText = result.content?.find(block => block.type === "text")?.text?.trim() ?? ""; const response = details?.response; if (!response) { return renderFallbackText(rawText, expanded, theme); @@ -92,18 +91,18 @@ export function renderWebSearchResult( const citations = Array.isArray(response.citations) ? response.citations : []; const citationCount = citations.length; const related = Array.isArray(response.relatedQuestions) - ? response.relatedQuestions.filter((item) => typeof item === "string") + ? response.relatedQuestions.filter(item => typeof item === "string") : []; const relatedCount = related.length; const searchQueries = Array.isArray(response.searchQueries) - ? response.searchQueries.filter((item) => typeof item === "string") + ? response.searchQueries.filter(item => typeof item === "string") : []; const provider = response.provider; // Get answer text const answerText = typeof response.answer === "string" ? response.answer.trim() : ""; const contentText = answerText || rawText; - const totalAnswerLines = contentText ? contentText.split("\n").filter((l) => l.trim()).length : 0; + const totalAnswerLines = contentText ? contentText.split("\n").filter(l => l.trim()).length : 0; const answerLimit = expanded ? MAX_EXPANDED_ANSWER_LINES : MAX_COLLAPSED_ANSWER_LINES; const answerPreview = contentText ? getPreviewLines(contentText, answerLimit, MAX_ANSWER_LINE_LEN, theme.format.ellipsis) @@ -140,7 +139,7 @@ export function renderWebSearchResult( expanded: true, maxCollapsed: answerLines.length, itemType: "line", - renderItem: (line) => (line === "No answer text returned" ? theme.fg("muted", line) : theme.fg("dim", line)), + renderItem: line => (line === "No answer text returned" ? theme.fg("muted", line) : theme.fg("dim", line)), }, theme, ); @@ -154,7 +153,7 @@ export function renderWebSearchResult( expanded, maxCollapsed: MAX_COLLAPSED_ITEMS, itemType: "source", - renderItem: (src) => { + renderItem: src => { const titleText = typeof src.title === "string" && src.title.trim() ? src.title @@ -198,8 +197,7 @@ export function renderWebSearchResult( expanded, maxCollapsed: MAX_COLLAPSED_ITEMS, itemType: "question", - renderItem: (line) => - theme.fg("muted", line === "No related questions" ? line : `${theme.format.dash} ${line}`), + renderItem: line => theme.fg("muted", line === "No related questions" ? line : `${theme.format.dash} ${line}`), }, theme, ); @@ -233,7 +231,7 @@ export function renderWebSearchResult( } if (searchQueries.length > 0) { const queriesPreview = searchQueries.slice(0, MAX_QUERY_PREVIEW); - const queryList = queriesPreview.map((q) => truncate(q, MAX_QUERY_LEN, theme.format.ellipsis)); + const queryList = queriesPreview.map(q => truncate(q, MAX_QUERY_LEN, theme.format.ellipsis)); const suffix = searchQueries.length > queriesPreview.length ? theme.format.ellipsis : ""; metaLines.push(`${theme.fg("muted", "Queries:")} ${theme.fg("text", queryList.join("; "))}${suffix}`); } diff --git a/packages/coding-agent/test/agent-session-compaction.test.ts b/packages/coding-agent/test/agent-session-compaction.test.ts index bd492c767..1602be2ad 100644 --- a/packages/coding-agent/test/agent-session-compaction.test.ts +++ b/packages/coding-agent/test/agent-session-compaction.test.ts @@ -80,7 +80,7 @@ describe.skipIf(!API_KEY)("AgentSession compaction e2e", () => { }); // Subscribe to track events - session.subscribe((event) => { + session.subscribe(event => { events.push(event); }); @@ -134,7 +134,7 @@ describe.skipIf(!API_KEY)("AgentSession compaction e2e", () => { expect(session.messages.length).toBeGreaterThan(0); // The agent should have responded - const assistantMessages = session.messages.filter((m) => m.role === "assistant"); + const assistantMessages = session.messages.filter(m => m.role === "assistant"); expect(assistantMessages.length).toBeGreaterThan(0); }, 180000); @@ -154,7 +154,7 @@ describe.skipIf(!API_KEY)("AgentSession compaction e2e", () => { const entries = sessionManager.getEntries(); // Should have a compaction entry - const compactionEntries = entries.filter((e) => e.type === "compaction"); + const compactionEntries = entries.filter(e => e.type === "compaction"); expect(compactionEntries.length).toBe(1); const compaction = compactionEntries[0]; @@ -184,7 +184,7 @@ describe.skipIf(!API_KEY)("AgentSession compaction e2e", () => { // In-memory entries should have the compaction const entries = sessionManager.getEntries(); - const compactionEntries = entries.filter((e) => e.type === "compaction"); + const compactionEntries = entries.filter(e => e.type === "compaction"); expect(compactionEntries.length).toBe(1); }, 120000); @@ -200,13 +200,13 @@ describe.skipIf(!API_KEY)("AgentSession compaction e2e", () => { // Check that no auto_compaction events were emitted for manual compaction const autoCompactionEvents = events.filter( - (e) => e.type === "auto_compaction_start" || e.type === "auto_compaction_end", + e => e.type === "auto_compaction_start" || e.type === "auto_compaction_end", ); // Manual compaction doesn't emit auto_compaction events expect(autoCompactionEvents.length).toBe(0); // Regular events should have been emitted - const messageEndEvents = events.filter((e) => e.type === "message_end"); + const messageEndEvents = events.filter(e => e.type === "message_end"); expect(messageEndEvents.length).toBeGreaterThan(0); }, 120000); }); diff --git a/packages/coding-agent/test/agent-session-concurrent.test.ts b/packages/coding-agent/test/agent-session-concurrent.test.ts index 06489e5a6..9b63a93ca 100644 --- a/packages/coding-agent/test/agent-session-concurrent.test.ts +++ b/packages/coding-agent/test/agent-session-concurrent.test.ts @@ -19,8 +19,8 @@ import { nanoid } from "nanoid"; class MockAssistantStream extends EventStream { constructor() { super( - (event) => event.type === "done" || event.type === "error", - (event) => { + event => event.type === "done" || event.type === "error", + event => { if (event.type === "done") return event.message; if (event.type === "error") return event.error; throw new Error("Unexpected event type"); diff --git a/packages/coding-agent/test/agent-session-tree-navigation.test.ts b/packages/coding-agent/test/agent-session-tree-navigation.test.ts index 129b72506..dfb4156ce 100644 --- a/packages/coding-agent/test/agent-session-tree-navigation.test.ts +++ b/packages/coding-agent/test/agent-session-tree-navigation.test.ts @@ -8,7 +8,6 @@ * - Summary attachment at correct position in tree * - Abort handling during summarization */ - import { afterEach, beforeEach, describe, expect, it } from "bun:test"; import { API_KEY, createTestSession, type TestSessionContext } from "./utilities"; @@ -62,7 +61,7 @@ describe.skipIf(!API_KEY)("AgentSession tree navigation e2e", () => { // Get the assistant message const entries = sessionManager.getEntries(); - const assistantEntry = entries.find((e) => e.type === "message" && e.message.role === "assistant"); + const assistantEntry = entries.find(e => e.type === "message" && e.message.role === "assistant"); expect(assistantEntry).toBeDefined(); // Navigate to assistant message @@ -118,11 +117,11 @@ describe.skipIf(!API_KEY)("AgentSession tree navigation e2e", () => { // Get the second user message (u2) const entries = sessionManager.getEntries(); - const userEntries = entries.filter((e) => e.type === "message" && e.message.role === "user"); + const userEntries = entries.filter(e => e.type === "message" && e.message.role === "user"); expect(userEntries.length).toBe(3); const u2 = userEntries[1]; - const a1 = entries.find((e) => e.id === u2.parentId); // a1 is parent of u2 + const a1 = entries.find(e => e.id === u2.parentId); // a1 is parent of u2 // Navigate to u2 with summarization const result = await session.navigateTree(u2.id, { summarize: true }); @@ -139,7 +138,7 @@ describe.skipIf(!API_KEY)("AgentSession tree navigation e2e", () => { const children = sessionManager.getChildren(a1!.id); expect(children.length).toBe(2); - const childTypes = children.map((c) => c.type).sort(); + const childTypes = children.map(c => c.type).sort(); expect(childTypes).toContain("branch_summary"); expect(childTypes).toContain("message"); }, 120000); @@ -155,7 +154,7 @@ describe.skipIf(!API_KEY)("AgentSession tree navigation e2e", () => { // Get the first assistant message (a1) const entries = sessionManager.getEntries(); - const assistantEntries = entries.filter((e) => e.type === "message" && e.message.role === "assistant"); + const assistantEntries = entries.filter(e => e.type === "message" && e.message.role === "assistant"); const a1 = assistantEntries[0]; // Navigate to a1 with summarization @@ -227,7 +226,7 @@ describe.skipIf(!API_KEY)("AgentSession tree navigation e2e", () => { expect(entriesAfter).toBe(entriesBefore); // No branch_summary entries - const summaries = sessionManager.getEntries().filter((e) => e.type === "branch_summary"); + const summaries = sessionManager.getEntries().filter(e => e.type === "branch_summary"); expect(summaries.length).toBe(0); }, 60000); @@ -295,7 +294,7 @@ describe.skipIf(!API_KEY)("AgentSession tree navigation - branch scenarios", () // Get a1 id for branching const entries = sessionManager.getEntries(); - const a1 = entries.find((e) => e.type === "message" && e.message.role === "assistant"); + const a1 = entries.find(e => e.type === "message" && e.message.role === "assistant"); // Create a branch from a1: a1 -> u3 -> a3 sessionManager.branch(a1!.id); @@ -303,7 +302,7 @@ describe.skipIf(!API_KEY)("AgentSession tree navigation - branch scenarios", () await session.agent.waitForIdle(); // Now navigate back to u2 (on main branch) with summarization - const userEntries = entries.filter((e) => e.type === "message" && e.message.role === "user"); + const userEntries = entries.filter(e => e.type === "message" && e.message.role === "user"); const u2 = userEntries[1]; // "Main branch continue" const result = await session.navigateTree(u2.id, { summarize: true }); diff --git a/packages/coding-agent/test/block-images.test.ts b/packages/coding-agent/test/block-images.test.ts index e7f9ea10c..97ae4829f 100644 --- a/packages/coding-agent/test/block-images.test.ts +++ b/packages/coding-agent/test/block-images.test.ts @@ -74,7 +74,7 @@ describe("blockImages setting", () => { // Should have text note + image content expect(result.content.length).toBeGreaterThanOrEqual(1); - const hasImage = result.content.some((c) => c.type === "image"); + const hasImage = result.content.some(c => c.type === "image"); expect(hasImage).toBe(true); }); diff --git a/packages/coding-agent/test/compaction-hooks-example.test.ts b/packages/coding-agent/test/compaction-hooks-example.test.ts index 6c81b899e..c88471bc5 100644 --- a/packages/coding-agent/test/compaction-hooks-example.test.ts +++ b/packages/coding-agent/test/compaction-hooks-example.test.ts @@ -32,8 +32,8 @@ describe("Documentation example", () => { expect(Array.isArray(branchEntries)).toBe(true); const summary = messagesToSummarize - .filter((m) => m.role === "user") - .map((m) => `- ${typeof m.content === "string" ? m.content.slice(0, 100) : "[complex]"}`) + .filter(m => m.role === "user") + .map(m => `- ${typeof m.content === "string" ? m.content.slice(0, 100) : "[complex]"}`) .join("\n"); // Hooks return compaction content - SessionManager adds id/parentId diff --git a/packages/coding-agent/test/compaction-hooks.test.ts b/packages/coding-agent/test/compaction-hooks.test.ts index 7434b25b1..658d44638 100644 --- a/packages/coding-agent/test/compaction-hooks.test.ts +++ b/packages/coding-agent/test/compaction-hooks.test.ts @@ -185,14 +185,14 @@ describe.skipIf(!API_KEY)("Compaction hooks", () => { await expect(session.compact()).rejects.toThrow("Compaction cancelled"); - const compactEvents = capturedEvents.filter((e) => e.type === "session_compact"); + const compactEvents = capturedEvents.filter(e => e.type === "session_compact"); expect(compactEvents.length).toBe(0); }, 120000); it("should allow hooks to provide custom compaction", async () => { const customSummary = "Custom summary from hook"; - const hook = createHook((event) => { + const hook = createHook(event => { if (event.type === "session_before_compact") { return { compaction: { @@ -216,7 +216,7 @@ describe.skipIf(!API_KEY)("Compaction hooks", () => { expect(result.summary).toBe(customSummary); - const compactEvents = capturedEvents.filter((e) => e.type === "session_compact"); + const compactEvents = capturedEvents.filter(e => e.type === "session_compact"); expect(compactEvents.length).toBe(1); const afterEvent = compactEvents[0]; @@ -235,7 +235,7 @@ describe.skipIf(!API_KEY)("Compaction hooks", () => { await session.compact(); - const compactEvents = capturedEvents.filter((e) => e.type === "session_compact"); + const compactEvents = capturedEvents.filter(e => e.type === "session_compact"); expect(compactEvents.length).toBe(1); const afterEvent = compactEvents[0]; @@ -366,7 +366,7 @@ describe.skipIf(!API_KEY)("Compaction hooks", () => { it("should pass correct data in before_compact event", async () => { let capturedBeforeEvent: SessionBeforeCompactEvent | null = null; - const hook = createHook((event) => { + const hook = createHook(event => { capturedBeforeEvent = event; return undefined; }); @@ -405,7 +405,7 @@ describe.skipIf(!API_KEY)("Compaction hooks", () => { it("should use hook compaction even with different values", async () => { const customSummary = "Custom summary with modified values"; - const hook = createHook((event) => { + const hook = createHook(event => { if (event.type === "session_before_compact") { return { compaction: { diff --git a/packages/coding-agent/test/compaction-thinking-model.test.ts b/packages/coding-agent/test/compaction-thinking-model.test.ts index b662a98bd..5f0a34619 100644 --- a/packages/coding-agent/test/compaction-thinking-model.test.ts +++ b/packages/coding-agent/test/compaction-thinking-model.test.ts @@ -101,7 +101,7 @@ describe.skipIf(!HAS_ANTIGRAVITY_AUTH)("Compaction with thinking models (Antigra const messages = session.messages; expect(messages.length).toBeGreaterThan(0); - const assistantMessages = messages.filter((m) => m.role === "assistant"); + const assistantMessages = messages.filter(m => m.role === "assistant"); expect(assistantMessages.length).toBeGreaterThan(0); // Now try to compact - this should not throw @@ -204,7 +204,7 @@ describe.skipIf(!HAS_ANTHROPIC_AUTH)("Compaction with thinking models (Anthropic const messages = session.messages; expect(messages.length).toBeGreaterThan(0); - const assistantMessages = messages.filter((m) => m.role === "assistant"); + const assistantMessages = messages.filter(m => m.role === "assistant"); expect(assistantMessages.length).toBeGreaterThan(0); // Now try to compact - this should not throw diff --git a/packages/coding-agent/test/compaction.test.ts b/packages/coding-agent/test/compaction.test.ts index c624d7dd6..5bba58c77 100644 --- a/packages/coding-agent/test/compaction.test.ts +++ b/packages/coding-agent/test/compaction.test.ts @@ -365,7 +365,7 @@ describe("Large session fixture", () => { const entries = await loadLargeSessionEntries(); expect(entries.length).toBeGreaterThan(100); - const messageCount = entries.filter((e) => e.type === "message").length; + const messageCount = entries.filter(e => e.type === "message").length; expect(messageCount).toBeGreaterThan(100); }); diff --git a/packages/coding-agent/test/core/apply-patch.test.ts b/packages/coding-agent/test/core/apply-patch.test.ts index d32ddb639..1c1be16ff 100644 --- a/packages/coding-agent/test/core/apply-patch.test.ts +++ b/packages/coding-agent/test/core/apply-patch.test.ts @@ -379,8 +379,8 @@ describe("apply-patch scenarios", () => { // Get all scenario directories const scenarioDirs = fs .readdirSync(fixturesDir, { withFileTypes: true }) - .filter((d) => d.isDirectory()) - .map((d) => d.name) + .filter(d => d.isDirectory()) + .map(d => d.name) .sort(); for (const scenarioName of scenarioDirs) { diff --git a/packages/coding-agent/test/core/python-executor-display.test.ts b/packages/coding-agent/test/core/python-executor-display.test.ts index 93aa85913..84cb9023e 100644 --- a/packages/coding-agent/test/core/python-executor-display.test.ts +++ b/packages/coding-agent/test/core/python-executor-display.test.ts @@ -30,7 +30,7 @@ describe("executePythonWithKernel display outputs", () => { const kernel = new FakeKernel( { status: "ok", cancelled: false, timedOut: false, stdinRequested: false }, - async (options) => { + async options => { if (!options?.onDisplay) return; for (const output of outputs) { await options.onDisplay(output); diff --git a/packages/coding-agent/test/core/python-executor-mapping.test.ts b/packages/coding-agent/test/core/python-executor-mapping.test.ts index 5d0053b9b..2327b19c7 100644 --- a/packages/coding-agent/test/core/python-executor-mapping.test.ts +++ b/packages/coding-agent/test/core/python-executor-mapping.test.ts @@ -27,7 +27,7 @@ describe("executePythonWithKernel mapping", () => { it("maps error status to non-zero exit code", async () => { const kernel = new FakeKernel( { status: "error", cancelled: false, timedOut: false, stdinRequested: false }, - (options) => { + options => { options?.onChunk?.("traceback\n"); }, ); diff --git a/packages/coding-agent/test/core/python-executor-streaming.test.ts b/packages/coding-agent/test/core/python-executor-streaming.test.ts index b79e96d75..069662ee3 100644 --- a/packages/coding-agent/test/core/python-executor-streaming.test.ts +++ b/packages/coding-agent/test/core/python-executor-streaming.test.ts @@ -23,7 +23,7 @@ describe("executePythonWithKernel streaming", () => { const largeOutput = "a".repeat(DEFAULT_MAX_BYTES + 128); const kernel = new FakeKernel( { status: "ok", cancelled: false, timedOut: false, stdinRequested: false }, - (options) => options?.onChunk?.(largeOutput), + options => options?.onChunk?.(largeOutput), ); const result = await executePythonWithKernel(kernel, "print('hi')"); @@ -46,7 +46,7 @@ describe("executePythonWithKernel streaming", () => { it("sanitizes ANSI and carriage returns", async () => { const kernel = new FakeKernel( { status: "ok", cancelled: false, timedOut: false, stdinRequested: false }, - (options) => options?.onChunk?.("\u001b[31mhello\r\n"), + options => options?.onChunk?.("\u001b[31mhello\r\n"), ); const result = await executePythonWithKernel(kernel, "print('hello')"); diff --git a/packages/coding-agent/test/core/python-executor-timeout.test.ts b/packages/coding-agent/test/core/python-executor-timeout.test.ts index 6dbad2b73..75095d6fc 100644 --- a/packages/coding-agent/test/core/python-executor-timeout.test.ts +++ b/packages/coding-agent/test/core/python-executor-timeout.test.ts @@ -21,7 +21,7 @@ describe("executePythonWithKernel cancellation", () => { it("annotates timeouts when cancelled", async () => { const kernel = new FakeKernel( { status: "ok", cancelled: true, timedOut: true, stdinRequested: false }, - (options) => { + options => { options?.onChunk?.("tick\n"); }, ); diff --git a/packages/coding-agent/test/core/python-executor.lifecycle.test.ts b/packages/coding-agent/test/core/python-executor.lifecycle.test.ts index abbcc1ce9..adf47cb35 100644 --- a/packages/coding-agent/test/core/python-executor.lifecycle.test.ts +++ b/packages/coding-agent/test/core/python-executor.lifecycle.test.ts @@ -61,7 +61,7 @@ describe("executePython session lifecycle", () => { it("reuses a session kernel across calls", async () => { let startCount = 0; - const kernel = new FakeKernel(okResult, { onExecute: (options) => options?.onChunk?.("ok\n") }); + const kernel = new FakeKernel(okResult, { onExecute: options => options?.onChunk?.("ok\n") }); PythonKernel.start = async () => { startCount += 1; return kernel as unknown as PythonKernel; @@ -78,7 +78,7 @@ describe("executePython session lifecycle", () => { it("restarts the session kernel when not alive", async () => { const deadKernel = new FakeKernel(okResult, { alive: false }); - const liveKernel = new FakeKernel(okResult, { onExecute: (options) => options?.onChunk?.("live\n") }); + const liveKernel = new FakeKernel(okResult, { onExecute: options => options?.onChunk?.("live\n") }); const kernels = [deadKernel, liveKernel]; let startCount = 0; diff --git a/packages/coding-agent/test/core/python-executor.result.test.ts b/packages/coding-agent/test/core/python-executor.result.test.ts index 608d0fd66..d77ef58d2 100644 --- a/packages/coding-agent/test/core/python-executor.result.test.ts +++ b/packages/coding-agent/test/core/python-executor.result.test.ts @@ -36,7 +36,7 @@ describe("executePythonWithKernel result mapping", () => { it("maps kernel error status to exit code 1", async () => { const kernel = new FakeKernel( { status: "error", cancelled: false, timedOut: false, stdinRequested: false }, - (options) => { + options => { options?.onChunk?.("Traceback...\n"); }, ); diff --git a/packages/coding-agent/test/core/python-executor.test.ts b/packages/coding-agent/test/core/python-executor.test.ts index b794915dc..ef44f6b1e 100644 --- a/packages/coding-agent/test/core/python-executor.test.ts +++ b/packages/coding-agent/test/core/python-executor.test.ts @@ -36,7 +36,7 @@ describe("executePythonWithKernel", () => { it("captures text and display outputs", async () => { const kernel = new FakeKernel( { status: "ok", cancelled: false, timedOut: false, stdinRequested: false }, - (options) => { + options => { options?.onChunk?.("hello\n"); options?.onDisplay?.({ type: "json", data: { foo: "bar" } }); }, @@ -65,7 +65,7 @@ describe("executePythonWithKernel", () => { it("maps error status to exit code 1", async () => { const kernel = new FakeKernel( { status: "error", cancelled: false, timedOut: false, stdinRequested: false }, - (options) => { + options => { options?.onChunk?.("Traceback\n"); }, ); @@ -80,7 +80,7 @@ describe("executePythonWithKernel", () => { it("sanitizes streamed chunks", async () => { const kernel = new FakeKernel( { status: "ok", cancelled: false, timedOut: false, stdinRequested: false }, - (options) => { + options => { options?.onChunk?.("\u001b[31mred\r\n"); }, ); @@ -93,7 +93,7 @@ describe("executePythonWithKernel", () => { it("returns cancelled result with timeout annotation", async () => { const kernel = new FakeKernel( { status: "ok", cancelled: true, timedOut: true, stdinRequested: false }, - (options) => { + options => { options?.onChunk?.("partial output\n"); }, ); @@ -108,7 +108,7 @@ describe("executePythonWithKernel", () => { it("returns cancelled result without timeout annotation", async () => { const kernel = new FakeKernel( { status: "ok", cancelled: true, timedOut: false, stdinRequested: false }, - (options) => { + options => { options?.onChunk?.("cancelled output\n"); }, ); @@ -131,7 +131,7 @@ describe("executePythonWithKernel", () => { const largeOutput = `${lines.join("\n")}\nTAIL\n`; const kernel = new FakeKernel( { status: "ok", cancelled: false, timedOut: false, stdinRequested: false }, - async (options) => { + async options => { await options?.onChunk?.(largeOutput); }, ); diff --git a/packages/coding-agent/test/core/python-kernel.lifecycle.test.ts b/packages/coding-agent/test/core/python-kernel.lifecycle.test.ts index 7eb53b63e..257132737 100644 --- a/packages/coding-agent/test/core/python-kernel.lifecycle.test.ts +++ b/packages/coding-agent/test/core/python-kernel.lifecycle.test.ts @@ -178,17 +178,15 @@ describe("PythonKernel gateway lifecycle", () => { "--JupyterApp.answer_yes=true", ]), ); - expect(env.fetchCalls.filter((call) => call.url.endsWith("/api/kernelspecs"))).toHaveLength(2); - expect(env.fetchCalls.some((call) => call.url.endsWith("/api/kernels") && call.init?.method === "POST")).toBe( - true, - ); + expect(env.fetchCalls.filter(call => call.url.endsWith("/api/kernelspecs"))).toHaveLength(2); + expect(env.fetchCalls.some(call => call.url.endsWith("/api/kernels") && call.init?.method === "POST")).toBe(true); await kernel.interrupt(); - expect(env.fetchCalls.some((call) => call.url.includes("/interrupt") && call.init?.method === "POST")).toBe(true); + expect(env.fetchCalls.some(call => call.url.includes("/interrupt") && call.init?.method === "POST")).toBe(true); expect(FakeWebSocket.instances[0]?.sent.length).toBe(1); await kernel.shutdown(); - expect(env.fetchCalls.some((call) => call.init?.method === "DELETE")).toBe(true); + expect(env.fetchCalls.some(call => call.init?.method === "DELETE")).toBe(true); expect(kernel.isAlive()).toBe(false); }); diff --git a/packages/coding-agent/test/core/python-kernel.test.ts b/packages/coding-agent/test/core/python-kernel.test.ts index 49e926758..f029d1df6 100644 --- a/packages/coding-agent/test/core/python-kernel.test.ts +++ b/packages/coding-agent/test/core/python-kernel.test.ts @@ -189,7 +189,7 @@ describe("PythonKernel (external gateway)", () => { await Bun.sleep(10); const ws = FakeWebSocket.lastInstance; if (!ws) throw new Error("WebSocket not initialized"); - ws.setSendHandler((data) => { + ws.setSendHandler(data => { const msg = typeof data === "string" ? (JSON.parse(data) as JupyterMessage) : decodeMessage(data); const code = String(msg.content.code ?? ""); if (!initSeen) { @@ -282,10 +282,10 @@ describe("PythonKernel (external gateway)", () => { const displays: KernelDisplayOutput[] = []; const result = await kernel.execute("print('hello')", { - onChunk: (text) => { + onChunk: text => { chunks.push(text); }, - onDisplay: (output) => { + onDisplay: output => { displays.push(output); }, }); @@ -321,7 +321,7 @@ describe("PythonKernel (external gateway)", () => { await Bun.sleep(10); const ws = FakeWebSocket.lastInstance; if (!ws) throw new Error("WebSocket not initialized"); - ws.setSendHandler((data) => { + ws.setSendHandler(data => { const msg = typeof data === "string" ? (JSON.parse(data) as JupyterMessage) : decodeMessage(data); const code = String(msg.content.code ?? ""); if (!initSeen) { @@ -363,7 +363,7 @@ describe("PythonKernel (external gateway)", () => { await Bun.sleep(10); const ws = FakeWebSocket.lastInstance; if (!ws) throw new Error("WebSocket not initialized"); - ws.setSendHandler((data) => { + ws.setSendHandler(data => { const msg = typeof data === "string" ? (JSON.parse(data) as JupyterMessage) : decodeMessage(data); const code = String(msg.content.code ?? ""); if (!initSeen) { @@ -409,7 +409,7 @@ describe("PythonKernel (external gateway)", () => { await Bun.sleep(10); const ws = FakeWebSocket.lastInstance; if (!ws) throw new Error("WebSocket not initialized"); - ws.setSendHandler((data) => { + ws.setSendHandler(data => { const msg = typeof data === "string" ? (JSON.parse(data) as JupyterMessage) : decodeMessage(data); const code = String(msg.content.code ?? ""); if (!initSeen) { diff --git a/packages/coding-agent/test/core/python-modules.test.ts b/packages/coding-agent/test/core/python-modules.test.ts index d227c9eae..05b1ce332 100644 --- a/packages/coding-agent/test/core/python-modules.test.ts +++ b/packages/coding-agent/test/core/python-modules.test.ts @@ -42,16 +42,16 @@ describe("python modules", () => { await writeModule(path.join(cwd, ".pi", "modules"), "gamma.py", "project-pi"); const modules = await discoverPythonModules({ cwd, homeDir }); - const names = modules.map((module) => path.basename(module.path)); + const names = modules.map(module => path.basename(module.path)); expect(names).toEqual(["alpha.py", "beta.py", "delta.py", "gamma.py"]); - expect(modules.map((module) => ({ name: path.basename(module.path), source: module.source }))).toEqual([ + expect(modules.map(module => ({ name: path.basename(module.path), source: module.source }))).toEqual([ { name: "alpha.py", source: "project" }, { name: "beta.py", source: "project" }, { name: "delta.py", source: "user" }, { name: "gamma.py", source: "project" }, ]); - expect(modules.find((module) => module.path.endsWith("alpha.py"))?.content).toContain("project-omp"); - expect(modules.find((module) => module.path.endsWith("delta.py"))?.content).toContain("user-pi"); + expect(modules.find(module => module.path.endsWith("alpha.py"))?.content).toContain("project-omp"); + expect(modules.find(module => module.path.endsWith("delta.py"))?.content).toContain("user-pi"); }); it("loads modules in sorted order with silent execution", async () => { @@ -72,7 +72,7 @@ describe("python modules", () => { }; await loadPythonModules(executor, { cwd, homeDir }); - expect(calls.map((call) => call.name)).toEqual(["alpha", "beta"]); + expect(calls.map(call => call.name)).toEqual(["alpha", "beta"]); for (const call of calls) { expect(call.options).toEqual({ silent: true, storeHistory: false }); } diff --git a/packages/coding-agent/test/core/python-prelude.test.ts b/packages/coding-agent/test/core/python-prelude.test.ts index c1af2f752..14ff250fc 100644 --- a/packages/coding-agent/test/core/python-prelude.test.ts +++ b/packages/coding-agent/test/core/python-prelude.test.ts @@ -116,7 +116,7 @@ describe.skipIf(!shouldRun)("PYTHON_PRELUDE integration", () => { `; const result = await tool.execute("tool-call-1", { cells: [{ code }] }); - const output = result.content.find((item) => item.type === "text")?.text ?? ""; + const output = result.content.find(item => item.type === "text")?.text ?? ""; expect(output).toContain("HELPERS_OK=1"); expect(output).toContain("DOCS_OK=1"); }); @@ -125,7 +125,7 @@ describe.skipIf(!shouldRun)("PYTHON_PRELUDE integration", () => { resetPreludeDocsCache(); const result = await warmPythonEnvironment(process.cwd(), undefined, false); expect(result.ok).toBe(true); - const names = result.docs.map((doc) => doc.name); + const names = result.docs.map(doc => doc.name); expect(names).toContain("pwd"); }); diff --git a/packages/coding-agent/test/extensions-discovery.test.ts b/packages/coding-agent/test/extensions-discovery.test.ts index ddc1a862f..1f3448efa 100644 --- a/packages/coding-agent/test/extensions-discovery.test.ts +++ b/packages/coding-agent/test/extensions-discovery.test.ts @@ -48,7 +48,7 @@ describe("extensions discovery", () => { expect(result.errors).toHaveLength(0); expect(result.extensions).toHaveLength(2); - expect(result.extensions.map((e) => path.basename(e.path)).sort()).toEqual(["bar.ts", "foo.ts"]); + expect(result.extensions.map(e => path.basename(e.path)).sort()).toEqual(["bar.ts", "foo.ts"]); }); it("discovers direct .js files in extensions/", async () => { diff --git a/packages/coding-agent/test/extensions-runner.test.ts b/packages/coding-agent/test/extensions-runner.test.ts index 8b006c54a..f6616bdb9 100644 --- a/packages/coding-agent/test/extensions-runner.test.ts +++ b/packages/coding-agent/test/extensions-runner.test.ts @@ -130,7 +130,7 @@ describe("ExtensionRunner", () => { const tools = runner.getAllRegisteredTools(); expect(tools.length).toBe(2); - expect(tools.map((t) => t.definition.name).sort()).toEqual(["tool_a", "tool_b"]); + expect(tools.map(t => t.definition.name).sort()).toEqual(["tool_a", "tool_b"]); }); }); @@ -158,7 +158,7 @@ describe("ExtensionRunner", () => { const commands = runner.getRegisteredCommands(); expect(commands.length).toBe(2); - expect(commands.map((c) => c.name).sort()).toEqual(["cmd-a", "cmd-b"]); + expect(commands.map(c => c.name).sort()).toEqual(["cmd-a", "cmd-b"]); }); it("gets command by name", async () => { @@ -212,7 +212,7 @@ describe("ExtensionRunner", () => { ); const errors: Array<{ extensionPath: string; event: string; error: string }> = []; - runner.onError((err) => { + runner.onError(err => { errors.push(err); }); diff --git a/packages/coding-agent/test/fuzzy.test.ts b/packages/coding-agent/test/fuzzy.test.ts index e81a98db2..23c211ee5 100644 --- a/packages/coding-agent/test/fuzzy.test.ts +++ b/packages/coding-agent/test/fuzzy.test.ts @@ -57,13 +57,13 @@ describe("fuzzyMatch", () => { describe("fuzzyFilter", () => { test("empty query returns all items unchanged", () => { const items = ["apple", "banana", "cherry"]; - const result = fuzzyFilter(items, "", (x) => x); + const result = fuzzyFilter(items, "", x => x); expect(result).toEqual(items); }); test("filters out non-matching items", () => { const items = ["apple", "banana", "cherry"]; - const result = fuzzyFilter(items, "an", (x) => x); + const result = fuzzyFilter(items, "an", x => x); expect(result).toContain("banana"); expect(result).not.toContain("apple"); expect(result).not.toContain("cherry"); @@ -71,7 +71,7 @@ describe("fuzzyFilter", () => { test("sorts results by match quality", () => { const items = ["a_p_p", "app", "application"]; - const result = fuzzyFilter(items, "app", (x) => x); + const result = fuzzyFilter(items, "app", x => x); // "app" should be first (exact consecutive match at start) expect(result[0]).toBe("app"); @@ -83,10 +83,10 @@ describe("fuzzyFilter", () => { { name: "bar", id: 2 }, { name: "foobar", id: 3 }, ]; - const result = fuzzyFilter(items, "foo", (item) => item.name); + const result = fuzzyFilter(items, "foo", item => item.name); expect(result.length).toBe(2); - expect(result.map((r) => r.name)).toContain("foo"); - expect(result.map((r) => r.name)).toContain("foobar"); + expect(result.map(r => r.name)).toContain("foo"); + expect(result.map(r => r.name)).toContain("foobar"); }); }); diff --git a/packages/coding-agent/test/model-registry.test.ts b/packages/coding-agent/test/model-registry.test.ts index 3f046a228..0b1547515 100644 --- a/packages/coding-agent/test/model-registry.test.ts +++ b/packages/coding-agent/test/model-registry.test.ts @@ -34,7 +34,7 @@ describe("ModelRegistry", () => { baseUrl, apiKey: "TEST_KEY", api, - models: models.map((m) => ({ + models: models.map(m => ({ id: m.id, name: m.name ?? m.id, reasoning: false, @@ -51,7 +51,7 @@ describe("ModelRegistry", () => { } function getModelsForProvider(registry: ModelRegistry, provider: string) { - return registry.getAll().filter((m) => m.provider === provider); + return registry.getAll().filter(m => m.provider === provider); } /** Create a baseUrl-only override (no custom models) */ @@ -75,7 +75,7 @@ describe("ModelRegistry", () => { // Should have multiple built-in models, not just one expect(anthropicModels.length).toBeGreaterThan(1); - expect(anthropicModels.some((m) => m.id.includes("claude"))).toBe(true); + expect(anthropicModels.some(m => m.id.includes("claude"))).toBe(true); }); test("overriding baseUrl changes URL on all built-in models", async () => { @@ -244,7 +244,7 @@ describe("ModelRegistry", () => { const anthropicModels = getModelsForProvider(registry, "anthropic"); expect(anthropicModels.length).toBeGreaterThan(1); - expect(anthropicModels.some((m) => m.id.includes("claude"))).toBe(true); + expect(anthropicModels.some(m => m.id.includes("claude"))).toBe(true); }); }); }); diff --git a/packages/coding-agent/test/python-tool-settings.test.ts b/packages/coding-agent/test/python-tool-settings.test.ts index d47da7684..21aa57f4e 100644 --- a/packages/coding-agent/test/python-tool-settings.test.ts +++ b/packages/coding-agent/test/python-tool-settings.test.ts @@ -51,7 +51,7 @@ describe("python tool settings", () => { vi.spyOn(pythonKernel, "checkPythonKernelAvailability").mockResolvedValue({ ok: true }); const tools = await createTools(createSession(testDir), ["python"]); - expect(tools.map((tool) => tool.name)).toEqual(["python"]); + expect(tools.map(tool => tool.name)).toEqual(["python"]); }); it("falls back to bash when python is unavailable", async () => { @@ -61,7 +61,7 @@ describe("python tool settings", () => { }); const tools = await createTools(createSession(testDir), ["python"]); - expect(tools.map((tool) => tool.name)).toEqual(["bash"]); + expect(tools.map(tool => tool.name)).toEqual(["bash"]); }); it("passes kernel mode from settings to executor", async () => { diff --git a/packages/coding-agent/test/rpc-example.ts b/packages/coding-agent/test/rpc-example.ts index 2fd31aec2..11372a91c 100644 --- a/packages/coding-agent/test/rpc-example.ts +++ b/packages/coding-agent/test/rpc-example.ts @@ -16,7 +16,7 @@ async function main() { }); // Stream events to console - client.onEvent((event) => { + client.onEvent(event => { if (event.type === "message_update") { const { assistantMessageEvent } = event; if (assistantMessageEvent.type === "text_delta" || assistantMessageEvent.type === "thinking_delta") { @@ -52,7 +52,7 @@ async function main() { if (!isWaiting) process.stdout.write("You: "); }; - rl.on("line", async (line) => { + rl.on("line", async line => { if (isWaiting) return; if (line.trim() === "exit") { await client.stop(); diff --git a/packages/coding-agent/test/rpc.test.ts b/packages/coding-agent/test/rpc.test.ts index a7a58dbf7..976b809e3 100644 --- a/packages/coding-agent/test/rpc.test.ts +++ b/packages/coding-agent/test/rpc.test.ts @@ -49,7 +49,7 @@ describe.skipIf(!process.env.ANTHROPIC_API_KEY && !process.env.ANTHROPIC_OAUTH_T const events = await client.promptAndWait("Reply with just the word 'hello'"); // Should have message events - const messageEndEvents = events.filter((e) => e.type === "message_end"); + const messageEndEvents = events.filter(e => e.type === "message_end"); expect(messageEndEvents.length).toBeGreaterThanOrEqual(2); // user + assistant // Wait for file writes @@ -63,11 +63,11 @@ describe.skipIf(!process.env.ANTHROPIC_API_KEY && !process.env.ANTHROPIC_OAUTH_T expect(sessionDirs.length).toBeGreaterThan(0); const cwdSessionDir = path.join(sessionsPath, sessionDirs[0]); - const sessionFiles = fs.readdirSync(cwdSessionDir).filter((f) => f.endsWith(".jsonl")); + const sessionFiles = fs.readdirSync(cwdSessionDir).filter(f => f.endsWith(".jsonl")); expect(sessionFiles.length).toBe(1); const sessionContent = await Bun.file(path.join(cwdSessionDir, sessionFiles[0])).text(); - const entries = sessionContent.split("\n").map((line) => JSON.parse(line)); + const entries = sessionContent.split("\n").map(line => JSON.parse(line)); // First entry should be session header expect(entries[0].type).toBe("session"); @@ -99,12 +99,12 @@ describe.skipIf(!process.env.ANTHROPIC_API_KEY && !process.env.ANTHROPIC_OAUTH_T const sessionsPath = path.join(sessionDir, "sessions"); const sessionDirs = fs.readdirSync(sessionsPath); const cwdSessionDir = path.join(sessionsPath, sessionDirs[0]); - const sessionFiles = fs.readdirSync(cwdSessionDir).filter((f) => f.endsWith(".jsonl")); + const sessionFiles = fs.readdirSync(cwdSessionDir).filter(f => f.endsWith(".jsonl")); const sessionContent = await Bun.file(path.join(cwdSessionDir, sessionFiles[0])).text(); const entries = sessionContent .trim() .split("\n") - .map((line) => JSON.parse(line)); + .map(line => JSON.parse(line)); const compactionEntries = entries.filter((e: { type: string }) => e.type === "compaction"); expect(compactionEntries.length).toBe(1); @@ -137,12 +137,12 @@ describe.skipIf(!process.env.ANTHROPIC_API_KEY && !process.env.ANTHROPIC_OAUTH_T const sessionsPath = path.join(sessionDir, "sessions"); const sessionDirs = fs.readdirSync(sessionsPath); const cwdSessionDir = path.join(sessionsPath, sessionDirs[0]); - const sessionFiles = fs.readdirSync(cwdSessionDir).filter((f) => f.endsWith(".jsonl")); + const sessionFiles = fs.readdirSync(cwdSessionDir).filter(f => f.endsWith(".jsonl")); const sessionContent = await Bun.file(path.join(cwdSessionDir, sessionFiles[0])).text(); const entries = sessionContent .trim() .split("\n") - .map((line) => JSON.parse(line)); + .map(line => JSON.parse(line)); const bashMessages = entries.filter( (e: { type: string; message?: { role: string } }) => @@ -165,9 +165,9 @@ describe.skipIf(!process.env.ANTHROPIC_API_KEY && !process.env.ANTHROPIC_OAUTH_T ); // Find assistant's response - const messageEndEvents = events.filter((e) => e.type === "message_end") as AgentEvent[]; + const messageEndEvents = events.filter(e => e.type === "message_end") as AgentEvent[]; const assistantMessage = messageEndEvents.find( - (e) => e.type === "message_end" && e.message?.role === "assistant", + e => e.type === "message_end" && e.message?.role === "assistant", ) as any; expect(assistantMessage).toBeDefined(); diff --git a/packages/coding-agent/test/session-manager/build-context.test.ts b/packages/coding-agent/test/session-manager/build-context.test.ts index d8b6c8680..6b62446aa 100644 --- a/packages/coding-agent/test/session-manager/build-context.test.ts +++ b/packages/coding-agent/test/session-manager/build-context.test.ts @@ -85,7 +85,7 @@ describe("buildSessionContext", () => { ]; const ctx = buildSessionContext(entries); expect(ctx.messages).toHaveLength(4); - expect(ctx.messages.map((m) => m.role)).toEqual(["user", "assistant", "user", "assistant"]); + expect(ctx.messages.map(m => m.role)).toEqual(["user", "assistant", "user", "assistant"]); }); it("tracks thinking level changes", () => { diff --git a/packages/coding-agent/test/session-manager/file-operations.test.ts b/packages/coding-agent/test/session-manager/file-operations.test.ts index acb3d9615..b90d48b93 100644 --- a/packages/coding-agent/test/session-manager/file-operations.test.ts +++ b/packages/coding-agent/test/session-manager/file-operations.test.ts @@ -109,7 +109,7 @@ describe("findMostRecentSession", () => { fs.writeFileSync(file1, '{"type":"session","id":"old","timestamp":"2025-01-01T00:00:00Z","cwd":"/tmp"}\n'); // Small delay to ensure different mtime - await new Promise((r) => setTimeout(r, 10)); + await new Promise(r => setTimeout(r, 10)); fs.writeFileSync(file2, '{"type":"session","id":"new","timestamp":"2025-01-01T00:00:00Z","cwd":"/tmp"}\n'); expect(await findMostRecentSession(tempDir)).toBe(file2); @@ -120,7 +120,7 @@ describe("findMostRecentSession", () => { const valid = path.join(tempDir, "valid.jsonl"); fs.writeFileSync(invalid, '{"type":"not-session"}\n'); - await new Promise((r) => setTimeout(r, 10)); + await new Promise(r => setTimeout(r, 10)); fs.writeFileSync(valid, '{"type":"session","id":"abc","timestamp":"2025-01-01T00:00:00Z","cwd":"/tmp"}\n'); expect(await findMostRecentSession(tempDir)).toBe(valid); diff --git a/packages/coding-agent/test/session-manager/labels.test.ts b/packages/coding-agent/test/session-manager/labels.test.ts index ca8b7413f..2358f3757 100644 --- a/packages/coding-agent/test/session-manager/labels.test.ts +++ b/packages/coding-agent/test/session-manager/labels.test.ts @@ -16,7 +16,7 @@ describe("SessionManager labels", () => { // Label entry should be in entries const entries = session.getEntries(); - const labelEntry = entries.find((e) => e.type === "label") as LabelEntry; + const labelEntry = entries.find(e => e.type === "label") as LabelEntry; expect(labelEntry).toBeDefined(); expect(labelEntry.id).toBe(labelId); expect(labelEntry.targetId).toBe(msgId); @@ -76,11 +76,11 @@ describe("SessionManager labels", () => { const tree = session.getTree(); // Find the message nodes (skip label entries) - const msg1Node = tree.find((n) => n.entry.id === msg1Id); + const msg1Node = tree.find(n => n.entry.id === msg1Id); expect(msg1Node?.label).toBe("start"); // msg2 is a child of msg1 - const msg2Node = msg1Node?.children.find((n) => n.entry.id === msg2Id); + const msg2Node = msg1Node?.children.find(n => n.entry.id === msg2Id); expect(msg2Node?.label).toBe("response"); }); @@ -118,7 +118,7 @@ describe("SessionManager labels", () => { // New label entries should exist const entries = session.getEntries(); - const labelEntries = entries.filter((e) => e.type === "label") as LabelEntry[]; + const labelEntries = entries.filter(e => e.type === "label") as LabelEntry[]; expect(labelEntries).toHaveLength(2); }); diff --git a/packages/coding-agent/test/session-manager/rewrite-tool-call.test.ts b/packages/coding-agent/test/session-manager/rewrite-tool-call.test.ts index c4112e144..0ecd5067d 100644 --- a/packages/coding-agent/test/session-manager/rewrite-tool-call.test.ts +++ b/packages/coding-agent/test/session-manager/rewrite-tool-call.test.ts @@ -45,8 +45,8 @@ describe("SessionManager tool call rewrite", () => { expect(updated).toBe(true); const ctx = session.buildSessionContext(); - const assistant = ctx.messages.find((m) => m.role === "assistant") as AssistantMessage; - const updatedCall = assistant.content.find((b) => b.type === "toolCall") as ToolCall; + const assistant = ctx.messages.find(m => m.role === "assistant") as AssistantMessage; + const updatedCall = assistant.content.find(b => b.type === "toolCall") as ToolCall; expect(updatedCall.arguments).toEqual({ path: "file.ts", op: "update", diff: "@@\n-old\n+newer" }); }); }); diff --git a/packages/coding-agent/test/session-manager/save-entry.test.ts b/packages/coding-agent/test/session-manager/save-entry.test.ts index e068bd664..129d6056e 100644 --- a/packages/coding-agent/test/session-manager/save-entry.test.ts +++ b/packages/coding-agent/test/session-manager/save-entry.test.ts @@ -34,7 +34,7 @@ describe("SessionManager.saveCustomEntry", () => { const entries = session.getEntries(); expect(entries).toHaveLength(3); - const customEntry = entries.find((e) => e.type === "custom") as CustomEntry; + const customEntry = entries.find(e => e.type === "custom") as CustomEntry; expect(customEntry).toBeDefined(); expect(customEntry.customType).toBe("my_hook"); expect(customEntry.data).toEqual({ foo: "bar" }); diff --git a/packages/coding-agent/test/session-manager/tree-traversal.test.ts b/packages/coding-agent/test/session-manager/tree-traversal.test.ts index 4753c4dcb..67ac149bd 100644 --- a/packages/coding-agent/test/session-manager/tree-traversal.test.ts +++ b/packages/coding-agent/test/session-manager/tree-traversal.test.ts @@ -35,7 +35,7 @@ describe("SessionManager append and tree traversal", () => { const entries = session.getEntries(); expect(entries).toHaveLength(3); - const thinkingEntry = entries.find((e) => e.type === "thinking_level_change"); + const thinkingEntry = entries.find(e => e.type === "thinking_level_change"); expect(thinkingEntry).toBeDefined(); expect(thinkingEntry!.id).toBe(thinkingId); expect(thinkingEntry!.parentId).toBe(msgId); @@ -51,7 +51,7 @@ describe("SessionManager append and tree traversal", () => { const _msg2Id = session.appendMessage(assistantMsg("response")); const entries = session.getEntries(); - const modelEntry = entries.find((e) => e.type === "model_change"); + const modelEntry = entries.find(e => e.type === "model_change"); expect(modelEntry).toBeDefined(); expect(modelEntry?.id).toBe(modelId); expect(modelEntry?.parentId).toBe(msgId); @@ -71,7 +71,7 @@ describe("SessionManager append and tree traversal", () => { const _id3 = session.appendMessage(userMsg("3")); const entries = session.getEntries(); - const compactionEntry = entries.find((e) => e.type === "compaction"); + const compactionEntry = entries.find(e => e.type === "compaction"); expect(compactionEntry).toBeDefined(); expect(compactionEntry?.id).toBe(compactionId); expect(compactionEntry?.parentId).toBe(id2); @@ -92,7 +92,7 @@ describe("SessionManager append and tree traversal", () => { const _msg2Id = session.appendMessage(assistantMsg("response")); const entries = session.getEntries(); - const customEntry = entries.find((e) => e.type === "custom") as CustomEntry; + const customEntry = entries.find(e => e.type === "custom") as CustomEntry; expect(customEntry).toBeDefined(); expect(customEntry.id).toBe(customId); expect(customEntry.parentId).toBe(msgId); @@ -143,7 +143,7 @@ describe("SessionManager append and tree traversal", () => { const path = session.getBranch(); expect(path).toHaveLength(4); - expect(path.map((e) => e.id)).toEqual([id1, id2, id3, id4]); + expect(path.map(e => e.id)).toEqual([id1, id2, id3, id4]); }); it("returns path from specified entry to root", () => { @@ -156,7 +156,7 @@ describe("SessionManager append and tree traversal", () => { const path = session.getBranch(id2); expect(path).toHaveLength(2); - expect(path.map((e) => e.id)).toEqual([id1, id2]); + expect(path.map(e => e.id)).toEqual([id1, id2]); }); }); @@ -208,7 +208,7 @@ describe("SessionManager append and tree traversal", () => { expect(node2.entry.id).toBe(id2); expect(node2.children).toHaveLength(2); // id3 and id4 are siblings - const childIds = node2.children.map((c) => c.entry.id).sort(); + const childIds = node2.children.map(c => c.entry.id).sort(); expect(childIds).toEqual([id3, id4].sort()); }); @@ -235,7 +235,7 @@ describe("SessionManager append and tree traversal", () => { expect(node2.entry.id).toBe(id2); expect(node2.children).toHaveLength(3); - const branchIds = node2.children.map((c) => c.entry.id).sort(); + const branchIds = node2.children.map(c => c.entry.id).sort(); expect(branchIds).toEqual([idA, idB, idC].sort()); }); @@ -263,10 +263,10 @@ describe("SessionManager append and tree traversal", () => { const node2 = tree[0].children[0]; expect(node2.children).toHaveLength(2); // id3 and id5 - const node5 = node2.children.find((c) => c.entry.id === id5)!; + const node5 = node2.children.find(c => c.entry.id === id5)!; expect(node5.children).toHaveLength(2); // id6 and id7 - const node3 = node2.children.find((c) => c.entry.id === id3)!; + const node3 = node2.children.find(c => c.entry.id === id3)!; expect(node3.children).toHaveLength(1); // id4 }); }); @@ -302,7 +302,7 @@ describe("SessionManager append and tree traversal", () => { const id3 = session.appendMessage(userMsg("branched")); const entries = session.getEntries(); - const branchedEntry = entries.find((e) => e.id === id3)!; + const branchedEntry = entries.find(e => e.id === id3)!; expect(branchedEntry.parentId).toBe(id1); // sibling of id2 }); }); @@ -320,7 +320,7 @@ describe("SessionManager append and tree traversal", () => { expect(session.getLeafId()).toBe(summaryId); const entries = session.getEntries(); - const summaryEntry = entries.find((e) => e.type === "branch_summary"); + const summaryEntry = entries.find(e => e.type === "branch_summary"); expect(summaryEntry).toBeDefined(); expect(summaryEntry?.parentId).toBe(id1); if (summaryEntry?.type === "branch_summary") { @@ -454,6 +454,6 @@ describe("createBranchedSession", () => { const entries = session.getEntries(); expect(entries).toHaveLength(4); - expect(entries.map((e) => e.id)).toEqual([id1, id2, id4, id5]); + expect(entries.map(e => e.id)).toEqual([id1, id2, id4, id5]); }); }); diff --git a/packages/coding-agent/test/skills.test.ts b/packages/coding-agent/test/skills.test.ts index 46b0a371a..82d51f5c1 100644 --- a/packages/coding-agent/test/skills.test.ts +++ b/packages/coding-agent/test/skills.test.ts @@ -143,7 +143,7 @@ describe("skills", () => { }); expect(skills.length).toBeGreaterThan(0); // Custom directory skills have source "custom:user" - expect(skills.every((s) => s.source.startsWith("custom"))).toBe(true); + expect(skills.every(s => s.source.startsWith("custom"))).toBe(true); }); it("should filter out ignoredSkills", async () => { @@ -169,7 +169,7 @@ describe("skills", () => { customDirectories: [fixturesDir], ignoredSkills: ["valid-*"], }); - expect(skills.every((s) => !s.name.startsWith("valid-"))).toBe(true); + expect(skills.every(s => !s.name.startsWith("valid-"))).toBe(true); }); it("should have ignoredSkills take precedence over includeSkills", async () => { @@ -184,7 +184,7 @@ describe("skills", () => { ignoredSkills: ["valid-skill"], }); // valid-skill should be excluded even though it matches includeSkills - expect(skills.every((s) => s.name !== "valid-skill")).toBe(true); + expect(skills.every(s => s.name !== "valid-skill")).toBe(true); }); it("should expand ~ in customDirectories", async () => { @@ -256,7 +256,7 @@ describe("skills", () => { includeSkills: ["valid-*"], }); expect(skills.length).toBeGreaterThan(0); - expect(skills.every((s) => s.name.startsWith("valid-"))).toBe(true); + expect(skills.every(s => s.name.startsWith("valid-"))).toBe(true); }); it("should return all skills when includeSkills is empty", async () => { diff --git a/packages/coding-agent/test/streaming-edit-abort.test.ts b/packages/coding-agent/test/streaming-edit-abort.test.ts index 4371875a4..dec5ae750 100644 --- a/packages/coding-agent/test/streaming-edit-abort.test.ts +++ b/packages/coding-agent/test/streaming-edit-abort.test.ts @@ -26,8 +26,8 @@ import { nanoid } from "nanoid"; class MockAssistantStream extends EventStream { constructor() { super( - (event) => event.type === "done" || event.type === "error", - (event) => { + event => event.type === "done" || event.type === "error", + event => { if (event.type === "done") return event.message; if (event.type === "error") return event.error; throw new Error("Unexpected event type"); diff --git a/packages/coding-agent/test/streaming-render-debug.ts b/packages/coding-agent/test/streaming-render-debug.ts index 3048c5209..c2fbe0777 100644 --- a/packages/coding-agent/test/streaming-render-debug.ts +++ b/packages/coding-agent/test/streaming-render-debug.ts @@ -3,7 +3,6 @@ * Uses real fixture data that caused the bug. * Run with: npx tsx test/streaming-render-debug.ts */ - import * as path from "node:path"; import type { AssistantMessage } from "@oh-my-pi/pi-ai"; import { AssistantMessageComponent } from "@oh-my-pi/pi-coding-agent/modes/components/assistant-message"; @@ -22,8 +21,8 @@ async function main() { ); // Extract thinking and text content - const thinkingContent = fixtureMessage.content.find((c) => c.type === "thinking"); - const textContent = fixtureMessage.content.find((c) => c.type === "text"); + const thinkingContent = fixtureMessage.content.find(c => c.type === "thinking"); + const textContent = fixtureMessage.content.find(c => c.type === "text"); if (!thinkingContent || thinkingContent.type !== "thinking") { console.error("No thinking content in fixture"); diff --git a/packages/coding-agent/test/tools.test.ts b/packages/coding-agent/test/tools.test.ts index 1bef799aa..a15e802e7 100644 --- a/packages/coding-agent/test/tools.test.ts +++ b/packages/coding-agent/test/tools.test.ts @@ -491,7 +491,7 @@ function b() { const outputLines = getTextOutput(result) .split("\n") - .map((line) => line.trim()) + .map(line => line.trim()) .filter(Boolean); expect(outputLines).toContain("visible.txt"); diff --git a/packages/coding-agent/test/tools/index.test.ts b/packages/coding-agent/test/tools/index.test.ts index b35f462b8..ce80cb655 100644 --- a/packages/coding-agent/test/tools/index.test.ts +++ b/packages/coding-agent/test/tools/index.test.ts @@ -31,7 +31,7 @@ describe("createTools", () => { it("creates all builtin tools by default", async () => { const session = createTestSession(); const tools = await createTools(session); - const names = tools.map((t) => t.name); + const names = tools.map(t => t.name); // Core tools should always be present expect(names).toContain("python"); @@ -59,7 +59,7 @@ describe("createTools", () => { }), }); const tools = await createTools(session); - const names = tools.map((t) => t.name); + const names = tools.map(t => t.name); expect(names).toContain("bash"); expect(names).toContain("python"); @@ -73,7 +73,7 @@ describe("createTools", () => { }), }); const tools = await createTools(session); - const names = tools.map((t) => t.name); + const names = tools.map(t => t.name); expect(names).toContain("bash"); expect(names).not.toContain("python"); @@ -82,7 +82,7 @@ describe("createTools", () => { it("excludes lsp tool when session disables LSP", async () => { const session = createTestSession({ enableLsp: false }); const tools = await createTools(session, ["read", "lsp", "write"]); - const names = tools.map((t) => t.name); + const names = tools.map(t => t.name); expect(names).toEqual(["read", "write"]); }); @@ -90,7 +90,7 @@ describe("createTools", () => { it("excludes lsp tool when disabled", async () => { const session = createTestSession({ enableLsp: false }); const tools = await createTools(session); - const names = tools.map((t) => t.name); + const names = tools.map(t => t.name); expect(names).not.toContain("lsp"); }); @@ -98,7 +98,7 @@ describe("createTools", () => { it("respects requested tool subset", async () => { const session = createTestSession(); const tools = await createTools(session, ["read", "write"]); - const names = tools.map((t) => t.name); + const names = tools.map(t => t.name); expect(names).toEqual(["read", "write"]); }); @@ -106,7 +106,7 @@ describe("createTools", () => { it("includes hidden tools when explicitly requested", async () => { const session = createTestSession(); const tools = await createTools(session, ["report_finding"]); - const names = tools.map((t) => t.name); + const names = tools.map(t => t.name); expect(names).toEqual(["report_finding"]); }); @@ -114,7 +114,7 @@ describe("createTools", () => { it("includes complete tool when required", async () => { const session = createTestSession({ requireCompleteTool: true }); const tools = await createTools(session); - const names = tools.map((t) => t.name); + const names = tools.map(t => t.name); expect(names).toContain("complete"); }); @@ -122,7 +122,7 @@ describe("createTools", () => { it("excludes ask tool when hasUI is false", async () => { const session = createTestSession({ hasUI: false }); const tools = await createTools(session); - const names = tools.map((t) => t.name); + const names = tools.map(t => t.name); expect(names).not.toContain("ask"); }); @@ -130,7 +130,7 @@ describe("createTools", () => { it("includes ask tool when hasUI is true", async () => { const session = createTestSession({ hasUI: true }); const tools = await createTools(session); - const names = tools.map((t) => t.name); + const names = tools.map(t => t.name); expect(names).toContain("ask"); }); diff --git a/packages/coding-agent/test/tools/python-execution.test.ts b/packages/coding-agent/test/tools/python-execution.test.ts index 9ed3f504c..e2fd7436d 100644 --- a/packages/coding-agent/test/tools/python-execution.test.ts +++ b/packages/coding-agent/test/tools/python-execution.test.ts @@ -60,7 +60,7 @@ describe("python tool execution", () => { reset: true, }), ); - const text = result.content.find((item) => item.type === "text")?.text; + const text = result.content.find(item => item.type === "text")?.text; expect(text).toBe("ok"); executeSpy.mockRestore(); diff --git a/packages/coding-agent/test/tools/python-fallback.test.ts b/packages/coding-agent/test/tools/python-fallback.test.ts index a47d6440f..4d7002f4b 100644 --- a/packages/coding-agent/test/tools/python-fallback.test.ts +++ b/packages/coding-agent/test/tools/python-fallback.test.ts @@ -41,7 +41,7 @@ describe("createTools python fallback", () => { }); const tools = await createTools(session, ["python"]); - const names = tools.map((tool) => tool.name).sort(); + const names = tools.map(tool => tool.name).sort(); expect(names).toEqual(["bash"]); @@ -61,7 +61,7 @@ describe("createTools python fallback", () => { }); const tools = await createTools(session); - const names = tools.map((tool) => tool.name); + const names = tools.map(tool => tool.name); expect(names).toContain("bash"); expect(names).not.toContain("python"); diff --git a/packages/coding-agent/test/tools/python-tool-mode.test.ts b/packages/coding-agent/test/tools/python-tool-mode.test.ts index 23ad83380..a8135288a 100644 --- a/packages/coding-agent/test/tools/python-tool-mode.test.ts +++ b/packages/coding-agent/test/tools/python-tool-mode.test.ts @@ -29,7 +29,7 @@ describe("createTools python fallback", () => { process.env.OMP_PYTHON_SKIP_CHECK = "1"; const session = createSession(); const tools = await createTools(session, ["python"]); - const names = tools.map((tool) => tool.name); + const names = tools.map(tool => tool.name); expect(names).toEqual(["bash"]); diff --git a/packages/coding-agent/test/tools/python.test.ts b/packages/coding-agent/test/tools/python.test.ts index 14a7b5174..825947711 100644 --- a/packages/coding-agent/test/tools/python.test.ts +++ b/packages/coding-agent/test/tools/python.test.ts @@ -100,7 +100,7 @@ describe("python tool exposure", () => { it("includes python only in ipy-only mode", async () => { const session = createSession({ settings: createSettings("ipy-only") }); const tools = await createTools(session); - const names = tools.map((tool) => tool.name); + const names = tools.map(tool => tool.name); expect(names).toContain("python"); expect(names).not.toContain("bash"); }); @@ -108,7 +108,7 @@ describe("python tool exposure", () => { it("includes bash only in bash-only mode", async () => { const session = createSession({ settings: createSettings("bash-only") }); const tools = await createTools(session); - const names = tools.map((tool) => tool.name); + const names = tools.map(tool => tool.name); expect(names).toContain("bash"); expect(names).not.toContain("python"); }); @@ -116,7 +116,7 @@ describe("python tool exposure", () => { it("includes bash and python in both mode", async () => { const session = createSession({ settings: createSettings("both") }); const tools = await createTools(session); - const names = tools.map((tool) => tool.name); + const names = tools.map(tool => tool.name); expect(names).toContain("bash"); expect(names).toContain("python"); }); diff --git a/packages/coding-agent/test/tools/schema-validation.test.ts b/packages/coding-agent/test/tools/schema-validation.test.ts index 4fcd3cf15..317899642 100644 --- a/packages/coding-agent/test/tools/schema-validation.test.ts +++ b/packages/coding-agent/test/tools/schema-validation.test.ts @@ -244,7 +244,7 @@ describe("tool schema validation (post-sanitization)", () => { // Apply the same sanitization that happens before sending to providers const sanitized = sanitizeSchemaForGoogle(schema); const violations = validateSchema(sanitized, tool.name); - const errors = violations.filter((v) => v.severity === "error"); + const errors = violations.filter(v => v.severity === "error"); if (errors.length > 0) { allViolations.push({ tool: tool.name, violations: errors }); @@ -254,7 +254,7 @@ describe("tool schema validation (post-sanitization)", () => { if (allViolations.length > 0) { const message = allViolations .map(({ tool, violations }) => { - const details = violations.map((v) => ` - ${v.path}: ${v.key} = ${JSON.stringify(v.value)}`).join("\n"); + const details = violations.map(v => ` - ${v.path}: ${v.key} = ${JSON.stringify(v.value)}`).join("\n"); return `${tool}:\n${details}`; }) .join("\n\n"); @@ -274,7 +274,7 @@ describe("tool schema validation (post-sanitization)", () => { if (!schema) continue; const sanitized = sanitizeSchemaForGoogle(schema); - const violations = validateSchema(sanitized, tool.name).filter((v) => v.key === "$schema"); + const violations = validateSchema(sanitized, tool.name).filter(v => v.key === "$schema"); expect(violations).toEqual([]); } }); @@ -288,7 +288,7 @@ describe("tool schema validation (post-sanitization)", () => { if (!schema) continue; const sanitized = sanitizeSchemaForGoogle(schema); - const violations = validateSchema(sanitized, tool.name).filter((v) => v.key === "$ref" || v.key === "$defs"); + const violations = validateSchema(sanitized, tool.name).filter(v => v.key === "$ref" || v.key === "$defs"); expect(violations).toEqual([]); } }); @@ -310,7 +310,7 @@ describe("tool schema validation (post-sanitization)", () => { if (!schema) continue; const sanitized = sanitizeSchemaForGoogle(schema); - const violations = validateSchema(sanitized, tool.name).filter((v) => draft2020Features.includes(v.key)); + const violations = validateSchema(sanitized, tool.name).filter(v => draft2020Features.includes(v.key)); expect(violations).toEqual([]); } }); @@ -324,7 +324,7 @@ describe("tool schema validation (post-sanitization)", () => { if (!schema) continue; const sanitized = sanitizeSchemaForGoogle(schema); - const violations = validateSchema(sanitized, tool.name).filter((v) => v.key === "const"); + const violations = validateSchema(sanitized, tool.name).filter(v => v.key === "const"); expect(violations).toEqual([]); } }); @@ -338,7 +338,7 @@ describe("tool schema validation (post-sanitization)", () => { if (!schema) continue; const sanitized = sanitizeSchemaForGoogle(schema); - const violations = validateSchema(sanitized, tool.name).filter((v) => v.key === "examples"); + const violations = validateSchema(sanitized, tool.name).filter(v => v.key === "examples"); expect(violations).toEqual([]); } }); @@ -355,10 +355,10 @@ describe("tool schema validation (post-sanitization)", () => { const sanitized = sanitizeSchemaForGoogle(schema); const violations = validateSchema(sanitized, name); - const errors = violations.filter((v) => v.severity === "error"); + const errors = violations.filter(v => v.severity === "error"); if (errors.length > 0) { - const details = errors.map((v) => ` - ${v.path}: ${v.key} = ${JSON.stringify(v.value)}`).join("\n"); + const details = errors.map(v => ` - ${v.path}: ${v.key} = ${JSON.stringify(v.value)}`).join("\n"); throw new Error(`Hidden tool ${name} has prohibited schema features after sanitization:\n${details}`); } } @@ -400,7 +400,7 @@ describe("tool schema validation (post-sanitization)", () => { const sanitized = sanitizeSchemaForGoogle(schema); const violations = validateSchema(sanitized, tool.name); - const toolWarnings = violations.filter((v) => v.severity === "warning"); + const toolWarnings = violations.filter(v => v.severity === "warning"); if (toolWarnings.length > 0) { warnings.push({ tool: tool.name, violations: toolWarnings }); @@ -411,7 +411,7 @@ describe("tool schema validation (post-sanitization)", () => { if (warnings.length > 0) { const message = warnings .map(({ tool, violations }) => { - const details = violations.map((v) => ` - ${v.path}: ${v.key} = ${JSON.stringify(v.value)}`).join("\n"); + const details = violations.map(v => ` - ${v.path}: ${v.key} = ${JSON.stringify(v.value)}`).join("\n"); return `${tool}:\n${details}`; }) .join("\n\n"); @@ -428,62 +428,62 @@ describe("validateSchema helper", () => { it("detects $schema declarations", () => { const schema = { $schema: "http://json-schema.org/draft-07/schema#", type: "object" }; const violations = validateSchema(schema); - expect(violations.some((v) => v.key === "$schema")).toBe(true); + expect(violations.some(v => v.key === "$schema")).toBe(true); }); it("detects $ref usage", () => { const schema = { type: "object", properties: { foo: { $ref: "#/$defs/Foo" } } }; const violations = validateSchema(schema); - expect(violations.some((v) => v.key === "$ref")).toBe(true); + expect(violations.some(v => v.key === "$ref")).toBe(true); }); it("detects $defs usage", () => { const schema = { $defs: { Foo: { type: "string" } }, type: "object" }; const violations = validateSchema(schema); - expect(violations.some((v) => v.key === "$defs")).toBe(true); + expect(violations.some(v => v.key === "$defs")).toBe(true); }); it("detects const usage (should be sanitized away)", () => { const schema = { type: "object", properties: { status: { const: "active" } } }; const violations = validateSchema(schema); - expect(violations.some((v) => v.key === "const")).toBe(true); + expect(violations.some(v => v.key === "const")).toBe(true); }); it("detects examples field", () => { const schema = { type: "string", examples: ["foo", "bar"] }; const violations = validateSchema(schema); - expect(violations.some((v) => v.key === "examples")).toBe(true); + expect(violations.some(v => v.key === "examples")).toBe(true); }); it("detects prefixItems (Draft 2020-12)", () => { const schema = { type: "array", prefixItems: [{ type: "string" }] }; const violations = validateSchema(schema); - expect(violations.some((v) => v.key === "prefixItems")).toBe(true); + expect(violations.some(v => v.key === "prefixItems")).toBe(true); }); it("detects unevaluatedProperties (Draft 2020-12)", () => { const schema = { type: "object", unevaluatedProperties: false }; const violations = validateSchema(schema); - expect(violations.some((v) => v.key === "unevaluatedProperties")).toBe(true); + expect(violations.some(v => v.key === "unevaluatedProperties")).toBe(true); }); it("warns on additionalProperties: false", () => { const schema = { type: "object", additionalProperties: false }; const violations = validateSchema(schema); - const warning = violations.find((v) => v.key === "additionalProperties"); + const warning = violations.find(v => v.key === "additionalProperties"); expect(warning?.severity).toBe("warning"); }); it("does not warn on additionalProperties: true", () => { const schema = { type: "object", additionalProperties: true }; const violations = validateSchema(schema); - expect(violations.some((v) => v.key === "additionalProperties")).toBe(false); + expect(violations.some(v => v.key === "additionalProperties")).toBe(false); }); it("warns on format keyword", () => { const schema = { type: "string", format: "uri" }; const violations = validateSchema(schema); - const warning = violations.find((v) => v.key === "format"); + const warning = violations.find(v => v.key === "format"); expect(warning?.severity).toBe("warning"); }); @@ -500,8 +500,8 @@ describe("validateSchema helper", () => { }, }; const violations = validateSchema(schema); - expect(violations.some((v) => v.key === "$ref")).toBe(true); - expect(violations.find((v) => v.key === "$ref")?.path).toContain("nested"); + expect(violations.some(v => v.key === "$ref")).toBe(true); + expect(violations.find(v => v.key === "$ref")?.path).toContain("nested"); }); it("validates array items", () => { @@ -510,7 +510,7 @@ describe("validateSchema helper", () => { items: [{ const: "first" }, { type: "string" }], }; const violations = validateSchema(schema); - expect(violations.some((v) => v.key === "const")).toBe(true); + expect(violations.some(v => v.key === "const")).toBe(true); }); it("returns empty array for valid schema", () => { @@ -523,6 +523,6 @@ describe("validateSchema helper", () => { required: ["name"], }; const violations = validateSchema(schema); - expect(violations.filter((v) => v.severity === "error")).toEqual([]); + expect(violations.filter(v => v.severity === "error")).toEqual([]); }); }); diff --git a/packages/coding-agent/test/tools/web-scrapers/academic.test.ts b/packages/coding-agent/test/tools/web-scrapers/academic.test.ts index 6c1a8a97c..43658791a 100644 --- a/packages/coding-agent/test/tools/web-scrapers/academic.test.ts +++ b/packages/coding-agent/test/tools/web-scrapers/academic.test.ts @@ -159,7 +159,7 @@ describe.skipIf(SKIP)("handleArxiv", () => { expect(result?.method).toBe("arxiv"); if (!result?.content.includes("Too Many Requests") && !result?.content.includes("Failed to fetch")) { expect(result?.content).toContain("Attention"); - expect(result?.notes?.some((n) => n.includes("PDF"))).toBe(true); + expect(result?.notes?.some(n => n.includes("PDF"))).toBe(true); } }); @@ -233,7 +233,7 @@ describe.skipIf(SKIP)("handleIacr", () => { expect(result).not.toBeNull(); expect(result?.method).toBe("iacr"); if (!result?.content.includes("Too Many Requests") && !result?.content.includes("Failed to fetch")) { - expect(result?.notes?.some((n) => n.includes("PDF"))).toBe(true); + expect(result?.notes?.some(n => n.includes("PDF"))).toBe(true); } }); }); diff --git a/packages/coding-agent/test/utilities.ts b/packages/coding-agent/test/utilities.ts index ae4d4510d..3e9fb5e45 100644 --- a/packages/coding-agent/test/utilities.ts +++ b/packages/coding-agent/test/utilities.ts @@ -1,7 +1,6 @@ /** * Shared test utilities for coding-agent tests. */ - import * as fs from "node:fs"; import * as os from "node:os"; import * as path from "node:path"; diff --git a/packages/pi-utils/src/logger.ts b/packages/pi-utils/src/logger.ts index f5aaeff79..b7463d80a 100644 --- a/packages/pi-utils/src/logger.ts +++ b/packages/pi-utils/src/logger.ts @@ -4,7 +4,6 @@ * Logs to ~/.omp/logs/ with size-based rotation, supporting concurrent omp instances. * Each log entry includes process.pid for traceability. */ - import * as fs from "node:fs"; import * as os from "node:os"; import * as path from "node:path"; diff --git a/packages/pi-utils/src/postmortem.ts b/packages/pi-utils/src/postmortem.ts index 524b9fe6b..a91a8a574 100644 --- a/packages/pi-utils/src/postmortem.ts +++ b/packages/pi-utils/src/postmortem.ts @@ -5,7 +5,6 @@ * in response to process exit, signals, or fatal exceptions. It is intended to * allow reliably releasing resources or shutting down subprocesses, files, sockets, etc. */ - import inspector from "node:inspector"; import { isMainThread } from "node:worker_threads"; import { logger } from "."; @@ -48,11 +47,11 @@ function runCleanup(reason: Reason): Promise { // Call .cleanup() for each callback that is still "armed". // Use Promise.try to handle sync/async, but only those armed. // Create a copy to avoid mutating the original array with reverse() - const promises = [...callbackList].reverse().map((callback) => { + const promises = [...callbackList].reverse().map(callback => { return Promise.try(() => callback(reason)); }); - return Promise.allSettled(promises).then((results) => { + return Promise.allSettled(promises).then(results => { for (const result of results) { if (result.status === "rejected") { const err = result.reason instanceof Error ? result.reason : new Error(String(result.reason)); @@ -81,12 +80,12 @@ if (isMainThread) { const url = inspector.url(); process.stderr.write(`Inspector opened: ${url}\n`); }) - .on("uncaughtException", async (err) => { + .on("uncaughtException", async err => { logger.error("Uncaught exception", { err, stack: err.stack }); await runCleanup(Reason.UNCAUGHT_EXCEPTION); process.exit(1); }) - .on("unhandledRejection", async (reason) => { + .on("unhandledRejection", async reason => { const err = reason instanceof Error ? reason : new Error(String(reason)); logger.error("Unhandled rejection", { err, stack: err.stack }); await runCleanup(Reason.UNHANDLED_REJECTION); diff --git a/packages/pi-utils/src/ptree.ts b/packages/pi-utils/src/ptree.ts index 7ccc507bb..f1cf69ea2 100644 --- a/packages/pi-utils/src/ptree.ts +++ b/packages/pi-utils/src/ptree.ts @@ -6,7 +6,6 @@ * - Windows and Unix support for proper tree killing. * - ChildProcess wrapper for capturing output, errors, and kill/detach. */ - import { type FileSink, type Spawn, type Subprocess, spawn, spawnSync } from "bun"; import { postmortem } from "."; @@ -49,7 +48,7 @@ class AsyncQueue { if (this.#closed) { return { value: undefined, done: true }; } - return await new Promise>((resolve) => { + return await new Promise>(resolve => { this.#resolvers.push(resolve); }); } @@ -57,7 +56,7 @@ class AsyncQueue { function createProcessStream(queue: AsyncQueue): ReadableStream { const stream = new ReadableStream({ - pull: async (controller) => { + pull: async controller => { const result = await queue.next(); if (result.done) { controller.close(); @@ -231,7 +230,7 @@ export class ChildProcess { this.#resolveExited = resolve; // On exit, resolve with a ChildError if nonzero code. - proc.exited.then((exitCode) => { + proc.exited.then(exitCode => { if (exitCode !== 0) { resolve(new NonZeroExitError(exitCode, this.#stderrBuffer)); } else { diff --git a/packages/pi-utils/src/stream.ts b/packages/pi-utils/src/stream.ts index 0c1e62217..e8b4448f2 100644 --- a/packages/pi-utils/src/stream.ts +++ b/packages/pi-utils/src/stream.ts @@ -14,7 +14,7 @@ export function sanitizeBinaryOutput(str: string): string { // This handles surrogate pairs correctly and catches edge cases where // codePointAt() might return undefined return Array.from(str) - .filter((char) => { + .filter(char => { // Filter out characters that cause string-width to crash // This includes: // - Unicode format characters diff --git a/packages/react-edit-benchmark/formatter.ts b/packages/react-edit-benchmark/formatter.ts index 3eafc9c29..544c3f78b 100644 --- a/packages/react-edit-benchmark/formatter.ts +++ b/packages/react-edit-benchmark/formatter.ts @@ -1,7 +1,6 @@ /** * Prettier formatting utilities for edit benchmarks. */ - import * as fs from "node:fs/promises"; import * as path from "node:path"; import * as prettier from "prettier"; diff --git a/packages/react-edit-benchmark/generate.ts b/packages/react-edit-benchmark/generate.ts index 418da83cf..ee1fc97e4 100644 --- a/packages/react-edit-benchmark/generate.ts +++ b/packages/react-edit-benchmark/generate.ts @@ -18,12 +18,11 @@ * - hard: Long files with similar blocks, no location hint * - nightmare: Long files where target line repeats, minimal info */ - -import { TempDir } from "@oh-my-pi/pi-utils"; -import { $ } from "bun"; import * as fs from "node:fs"; import { basename, dirname, join, relative } from "node:path"; import { parseArgs } from "node:util"; +import { TempDir } from "@oh-my-pi/pi-utils"; +import { $ } from "bun"; import { ALL_MUTATIONS, CATEGORY_MAP, type Mutation, type MutationInfo } from "./mutations"; const SCRIPT_DIR = import.meta.dir; diff --git a/packages/react-edit-benchmark/index.ts b/packages/react-edit-benchmark/index.ts index b52788fb2..1bbd7579c 100644 --- a/packages/react-edit-benchmark/index.ts +++ b/packages/react-edit-benchmark/index.ts @@ -8,15 +8,14 @@ * bun run bench:edit --runs 5 --output report.md * bun run bench:edit --fixtures fixtures.tar.gz */ - -import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core"; -import { TempDir } from "@oh-my-pi/pi-utils"; import * as fs from "node:fs"; import { join } from "node:path"; import { parseArgs } from "node:util"; +import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core"; +import { TempDir } from "@oh-my-pi/pi-utils"; import { generateJsonReport, generateReport } from "./report"; -import { runBenchmark, type BenchmarkConfig, type ProgressEvent } from "./runner"; -import { loadTasks, loadTasksFromDir, validateFixtures, type EditTask } from "./tasks"; +import { type BenchmarkConfig, type ProgressEvent, runBenchmark } from "./runner"; +import { type EditTask, loadTasks, loadTasksFromDir, validateFixtures } from "./tasks"; const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh"] as const; diff --git a/packages/react-edit-benchmark/report.ts b/packages/react-edit-benchmark/report.ts index 144819715..688618783 100644 --- a/packages/react-edit-benchmark/report.ts +++ b/packages/react-edit-benchmark/report.ts @@ -1,7 +1,6 @@ /** * Markdown report generator for edit benchmark results. */ - import type { BenchmarkResult, TaskResult } from "./runner"; function formatDuration(ms: number): string { diff --git a/packages/react-edit-benchmark/runner.ts b/packages/react-edit-benchmark/runner.ts index 920ebb4e5..c2816eefe 100644 --- a/packages/react-edit-benchmark/runner.ts +++ b/packages/react-edit-benchmark/runner.ts @@ -4,14 +4,13 @@ * Orchestrates benchmark runs by launching RPC clients, sending prompts, * and verifying results. Supports parallel runs for reliability measurement. */ - +import * as fs from "node:fs/promises"; +import { join } from "node:path"; import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core"; import { RpcClient } from "@oh-my-pi/pi-coding-agent"; import { TempDir } from "@oh-my-pi/pi-utils"; -import * as fs from "node:fs/promises"; -import { join } from "node:path"; import { formatDirectory } from "./formatter"; -import { extractTaskFiles, type EditTask } from "./tasks"; +import { type EditTask, extractTaskFiles } from "./tasks"; import { verifyExpectedFileSubset, verifyExpectedFiles } from "./verify"; const TMP_DIR = await TempDir.create("@reach-benchmark-"); diff --git a/packages/react-edit-benchmark/tarball.ts b/packages/react-edit-benchmark/tarball.ts index 457609750..c347b6586 100644 --- a/packages/react-edit-benchmark/tarball.ts +++ b/packages/react-edit-benchmark/tarball.ts @@ -2,7 +2,6 @@ * Tarball utilities for reading fixtures directly from .tar.gz archives. * Uses Bun.Archive for native tar.gz handling. */ - import * as fs from "node:fs/promises"; import { basename, dirname, join } from "node:path"; diff --git a/packages/react-edit-benchmark/tasks.ts b/packages/react-edit-benchmark/tasks.ts index 0e55e667c..fa93266fc 100644 --- a/packages/react-edit-benchmark/tasks.ts +++ b/packages/react-edit-benchmark/tasks.ts @@ -5,17 +5,15 @@ * - A fixtures directory (for development) * - A fixtures.tar.gz tarball (for distribution) */ - /// - import * as fs from "node:fs/promises"; import { basename, join } from "node:path"; import { + type FixtureValidationIssue, + type TarballTask, extractTaskFiles, loadTasksFromTarball, validateTarballFixtures, - type FixtureValidationIssue, - type TarballTask, } from "./tarball"; export interface EditTask { diff --git a/packages/react-edit-benchmark/verify.ts b/packages/react-edit-benchmark/verify.ts index 1e4a7c5e1..a204ddc3c 100644 --- a/packages/react-edit-benchmark/verify.ts +++ b/packages/react-edit-benchmark/verify.ts @@ -3,11 +3,10 @@ * * Compares output files against expected fixtures with byte-for-byte equality. */ - import * as fs from "node:fs/promises"; import * as path from "node:path"; -import { formatContent } from "./formatter"; import { diffLines } from "diff"; +import { formatContent } from "./formatter"; export interface VerificationResult { success: boolean; diff --git a/packages/stats/src/db.ts b/packages/stats/src/db.ts index 3a8516a12..4cca3ea36 100644 --- a/packages/stats/src/db.ts +++ b/packages/stats/src/db.ts @@ -245,7 +245,7 @@ export function getStatsByModel(): ModelStats[] { `); const rows = stmt.all() as any[]; - return rows.map((row) => ({ + return rows.map(row => ({ model: row.model, provider: row.provider, ...buildAggregatedStats([row]), @@ -279,7 +279,7 @@ export function getStatsByFolder(): FolderStats[] { `); const rows = stmt.all() as any[]; - return rows.map((row) => ({ + return rows.map(row => ({ folder: row.folder, ...buildAggregatedStats([row]), })); @@ -307,7 +307,7 @@ export function getTimeSeries(hours = 24): TimeSeriesPoint[] { `); const rows = stmt.all(cutoff) as any[]; - return rows.map((row) => ({ + return rows.map(row => ({ timestamp: row.bucket, requests: row.requests, errors: row.errors, diff --git a/packages/stats/src/index.ts b/packages/stats/src/index.ts index d57d0898e..9d727a66c 100755 --- a/packages/stats/src/index.ts +++ b/packages/stats/src/index.ts @@ -6,14 +6,7 @@ import { closeDb } from "./db"; import { startServer } from "./server"; export { getDashboardStats, getTotalMessageCount, syncAllSessions } from "./aggregator"; -export type { - AggregatedStats, - DashboardStats, - FolderStats, - MessageStats, - ModelStats, - TimeSeriesPoint, -} from "./types"; +export type { AggregatedStats, DashboardStats, FolderStats, MessageStats, ModelStats, TimeSeriesPoint } from "./types"; /** * Format a number with appropriate suffix (K, M, etc.) diff --git a/packages/stats/src/parser.ts b/packages/stats/src/parser.ts index 384ac7f29..83e0968bb 100644 --- a/packages/stats/src/parser.ts +++ b/packages/stats/src/parser.ts @@ -106,7 +106,7 @@ export async function parseSessionFile( export async function listSessionFolders(): Promise { try { const entries = await fs.readdir(SESSIONS_DIR, { withFileTypes: true }); - return entries.filter((e) => e.isDirectory()).map((e) => path.join(SESSIONS_DIR, e.name)); + return entries.filter(e => e.isDirectory()).map(e => path.join(SESSIONS_DIR, e.name)); } catch { return []; } @@ -118,7 +118,7 @@ export async function listSessionFolders(): Promise { export async function listSessionFiles(folderPath: string): Promise { try { const entries = await fs.readdir(folderPath, { withFileTypes: true }); - return entries.filter((e) => e.isFile() && e.name.endsWith(".jsonl")).map((e) => path.join(folderPath, e.name)); + return entries.filter(e => e.isFile() && e.name.endsWith(".jsonl")).map(e => path.join(folderPath, e.name)); } catch { return []; } diff --git a/packages/stats/src/server.ts b/packages/stats/src/server.ts index 3f87058ec..2739c1a7d 100644 --- a/packages/stats/src/server.ts +++ b/packages/stats/src/server.ts @@ -26,7 +26,7 @@ const ensureClientBuild = async () => { }); if (!result.success) { - const errors = result.logs.map((log) => log.message).join("\n"); + const errors = result.logs.map(log => log.message).join("\n"); throw new Error(`Failed to build stats client:\n${errors}`); } diff --git a/packages/tui/src/autocomplete.ts b/packages/tui/src/autocomplete.ts index 4e1c1618d..939a70b1d 100644 --- a/packages/tui/src/autocomplete.ts +++ b/packages/tui/src/autocomplete.ts @@ -134,11 +134,11 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider { // No space yet - complete command names const prefix = textBeforeCursor.slice(1); // Remove the "/" const filtered = this.commands - .filter((cmd) => { + .filter(cmd => { const name = "name" in cmd ? cmd.name : cmd.value; // Check if SlashCommand or AutocompleteItem return name?.toLowerCase().startsWith(prefix.toLowerCase()); }) - .map((cmd) => ({ + .map(cmd => ({ value: "name" in cmd ? cmd.name : cmd.value, label: "name" in cmd ? cmd.name : cmd.label, ...(cmd.description && { description: cmd.description }), @@ -155,7 +155,7 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider { const commandName = textBeforeCursor.slice(1, spaceIndex); // Command without "/" const argumentText = textBeforeCursor.slice(spaceIndex + 1); // Text after space - const command = this.commands.find((cmd) => { + const command = this.commands.find(cmd => { const name = "name" in cmd ? cmd.name : cmd.value; return name === commandName; }); @@ -537,11 +537,11 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider { const entries = await walkDirectoryWithFd(this.basePath, this.fdPath, query, 100); const scoredEntries = entries - .map((entry) => ({ + .map(entry => ({ ...entry, score: query ? this.scoreEntry(entry.path, query, entry.isDirectory) : 1, })) - .filter((entry) => entry.score > 0); + .filter(entry => entry.score > 0); scoredEntries.sort((a, b) => b.score - a.score); const topEntries = scoredEntries.slice(0, 20); diff --git a/packages/tui/src/components/editor.ts b/packages/tui/src/components/editor.ts index 27fb4910f..0a7bf9478 100644 --- a/packages/tui/src/components/editor.ts +++ b/packages/tui/src/components/editor.ts @@ -355,7 +355,7 @@ export class Editor implements Component, Focusable { setHistoryStorage(storage: HistoryStorage): void { this.historyStorage = storage; const recent = storage.getRecent(100); - this.history = recent.map((entry) => entry.prompt); + this.history = recent.map(entry => entry.prompt); this.historyIndex = -1; } @@ -547,7 +547,7 @@ export class Editor implements Component, Focusable { // Rebuild 'before' without the last grapheme const beforeWithoutLast = beforeGraphemes .slice(0, -1) - .map((g) => g.segment) + .map(g => g.segment) .join(""); displayText = beforeWithoutLast + marker + cursor; displayWidth -= 1; // Back to original width (reverse video replaces, doesn't add) @@ -1156,7 +1156,7 @@ export class Editor implements Component, Focusable { // Filter out non-printable characters except newlines let filteredText = tabExpandedText .split("") - .filter((char) => char === "\n" || char.charCodeAt(0) >= 32) + .filter(char => char === "\n" || char.charCodeAt(0) >= 32) .join(""); // If pasting a file path (starts with /, ~, or .) and the character before diff --git a/packages/tui/src/components/input.ts b/packages/tui/src/components/input.ts index e029277b9..65ce05ca5 100644 --- a/packages/tui/src/components/input.ts +++ b/packages/tui/src/components/input.ts @@ -186,7 +186,7 @@ export class Input implements Component, Focusable { // Regular character input - accept printable characters including Unicode, // but reject control characters (C0: 0x00-0x1F, DEL: 0x7F, C1: 0x80-0x9F) - const hasControlChars = [...data].some((ch) => { + const hasControlChars = [...data].some(ch => { const code = ch.charCodeAt(0); return code < 32 || code === 0x7f || (code >= 0x80 && code <= 0x9f); }); diff --git a/packages/tui/src/components/markdown.ts b/packages/tui/src/components/markdown.ts index 7bedd576f..46aadb13f 100644 --- a/packages/tui/src/components/markdown.ts +++ b/packages/tui/src/components/markdown.ts @@ -628,7 +628,7 @@ export class Markdown implements Component { } else { // Distribute space proportionally based on natural widths const totalNatural = naturalWidths.reduce((a, b) => a + b, 0); - columnWidths = naturalWidths.map((w) => { + columnWidths = naturalWidths.map(w => { const proportion = w / totalNatural; return Math.max(1, Math.floor(proportion * availableForCells)); }); @@ -648,7 +648,7 @@ export class Markdown implements Component { const v = t.vertical; // Render top border - const topBorderCells = columnWidths.map((w) => h.repeat(w)); + const topBorderCells = columnWidths.map(w => h.repeat(w)); lines.push(`${t.topLeft}${h}${topBorderCells.join(`${h}${t.teeDown}${h}`)}${h}${t.topRight}`); // Render header with wrapping @@ -656,7 +656,7 @@ export class Markdown implements Component { const text = this.renderInlineTokens(cell.tokens || []); return this.wrapCellText(text, columnWidths[i]); }); - const headerLineCount = Math.max(...headerCellLines.map((c) => c.length)); + const headerLineCount = Math.max(...headerCellLines.map(c => c.length)); for (let lineIdx = 0; lineIdx < headerLineCount; lineIdx++) { const rowParts = headerCellLines.map((cellLines, colIdx) => { @@ -668,7 +668,7 @@ export class Markdown implements Component { } // Render separator - const separatorCells = columnWidths.map((w) => h.repeat(w)); + const separatorCells = columnWidths.map(w => h.repeat(w)); lines.push(`${t.teeRight}${h}${separatorCells.join(`${h}${t.cross}${h}`)}${h}${t.teeLeft}`); // Render rows with wrapping @@ -677,7 +677,7 @@ export class Markdown implements Component { const text = this.renderInlineTokens(cell.tokens || []); return this.wrapCellText(text, columnWidths[i]); }); - const rowLineCount = Math.max(...rowCellLines.map((c) => c.length)); + const rowLineCount = Math.max(...rowCellLines.map(c => c.length)); for (let lineIdx = 0; lineIdx < rowLineCount; lineIdx++) { const rowParts = rowCellLines.map((cellLines, colIdx) => { @@ -689,7 +689,7 @@ export class Markdown implements Component { } // Render bottom border - const bottomBorderCells = columnWidths.map((w) => h.repeat(w)); + const bottomBorderCells = columnWidths.map(w => h.repeat(w)); lines.push(`${t.bottomLeft}${h}${bottomBorderCells.join(`${h}${t.teeUp}${h}`)}${h}${t.bottomRight}`); lines.push(""); // Add spacing after table diff --git a/packages/tui/src/components/select-list.ts b/packages/tui/src/components/select-list.ts index bf5ed4e05..a848bff4f 100644 --- a/packages/tui/src/components/select-list.ts +++ b/packages/tui/src/components/select-list.ts @@ -37,7 +37,7 @@ export class SelectList implements Component { } setFilter(filter: string): void { - this.filteredItems = this.items.filter((item) => item.value.toLowerCase().startsWith(filter.toLowerCase())); + this.filteredItems = this.items.filter(item => item.value.toLowerCase().startsWith(filter.toLowerCase())); // Reset selection when filter changes this.selectedIndex = 0; } diff --git a/packages/tui/src/components/settings-list.ts b/packages/tui/src/components/settings-list.ts index 319751231..25dcd3c19 100644 --- a/packages/tui/src/components/settings-list.ts +++ b/packages/tui/src/components/settings-list.ts @@ -53,7 +53,7 @@ export class SettingsList implements Component { /** Update an item's currentValue */ updateValue(id: string, newValue: string): void { - const item = this.items.find((i) => i.id === id); + const item = this.items.find(i => i.id === id); if (item) { item.currentValue = newValue; } @@ -88,7 +88,7 @@ export class SettingsList implements Component { const endIndex = Math.min(startIndex + this.maxVisible, this.items.length); // Calculate max label width for alignment - const maxLabelWidth = Math.min(30, Math.max(...this.items.map((item) => visibleWidth(item.label)))); + const maxLabelWidth = Math.min(30, Math.max(...this.items.map(item => visibleWidth(item.label)))); // Render visible items for (let i = startIndex; i < endIndex; i++) { diff --git a/packages/tui/src/components/tab-bar.ts b/packages/tui/src/components/tab-bar.ts index e85c25f5c..055a4a815 100644 --- a/packages/tui/src/components/tab-bar.ts +++ b/packages/tui/src/components/tab-bar.ts @@ -8,7 +8,6 @@ * - Tab / Arrow Right: Next tab (wraps around) * - Shift+Tab / Arrow Left: Previous tab (wraps around) */ - import { matchesKey } from "../keys"; import type { Component } from "../tui"; import { wrapTextWithAnsi } from "../utils"; diff --git a/packages/tui/src/fuzzy.ts b/packages/tui/src/fuzzy.ts index 5fc761e3f..2917ca31f 100644 --- a/packages/tui/src/fuzzy.ts +++ b/packages/tui/src/fuzzy.ts @@ -110,7 +110,7 @@ export function fuzzyFilter(items: T[], query: string, getText: (item: T) => const tokens = query .trim() .split(/\s+/) - .filter((t) => t.length > 0); + .filter(t => t.length > 0); if (tokens.length === 0) { return items; @@ -139,5 +139,5 @@ export function fuzzyFilter(items: T[], query: string, getText: (item: T) => } results.sort((a, b) => a.totalScore - b.totalScore); - return results.map((r) => r.item); + return results.map(r => r.item); } diff --git a/packages/tui/src/keybindings.ts b/packages/tui/src/keybindings.ts index ca1712bba..ee801537c 100644 --- a/packages/tui/src/keybindings.ts +++ b/packages/tui/src/keybindings.ts @@ -123,7 +123,7 @@ export class EditorKeybindingsManager { const keyArray = Array.isArray(keys) ? keys : [keys]; this.actionToKeys.set( action as EditorAction, - keyArray.map((key) => normalizeKeyId(key as KeyId)), + keyArray.map(key => normalizeKeyId(key as KeyId)), ); } @@ -133,7 +133,7 @@ export class EditorKeybindingsManager { const keyArray = Array.isArray(keys) ? keys : [keys]; this.actionToKeys.set( action as EditorAction, - keyArray.map((key) => normalizeKeyId(key as KeyId)), + keyArray.map(key => normalizeKeyId(key as KeyId)), ); } } diff --git a/packages/tui/src/stdin-buffer.ts b/packages/tui/src/stdin-buffer.ts index 5b2f977b0..f8dcc2c7b 100644 --- a/packages/tui/src/stdin-buffer.ts +++ b/packages/tui/src/stdin-buffer.ts @@ -16,7 +16,6 @@ * Based on code from OpenTUI (https://github.com/anomalyco/opentui) * MIT License - Copyright (c) 2025 opentui */ - import { EventEmitter } from "events"; const ESC = "\x1b"; @@ -111,7 +110,7 @@ function isCompleteCsiSequence(data: string): "complete" | "incomplete" { if (lastChar === "M" || lastChar === "m") { // Check if we have the right structure const parts = payload.slice(1, -1).split(";"); - if (parts.length === 3 && parts.every((p) => /^\d+$/.test(p))) { + if (parts.length === 3 && parts.every(p => /^\d+$/.test(p))) { return "complete"; } } diff --git a/packages/tui/src/tui.ts b/packages/tui/src/tui.ts index 57e17859b..dd2cfc98d 100644 --- a/packages/tui/src/tui.ts +++ b/packages/tui/src/tui.ts @@ -1,7 +1,6 @@ /** * Minimal TUI implementation with differential rendering */ - import * as fs from "node:fs"; import * as os from "node:os"; import * as path from "node:path"; @@ -337,7 +336,7 @@ export class TUI extends Container { /** Check if there are any visible overlays */ hasOverlay(): boolean { - return this.overlayStack.some((o) => this.isOverlayVisible(o)); + return this.overlayStack.some(o => this.isOverlayVisible(o)); } /** Check if an overlay entry is currently visible */ @@ -366,7 +365,7 @@ export class TUI extends Container { start(): void { this.terminal.start( - (data) => this.handleInput(data), + data => this.handleInput(data), () => this.requestRender(), ); this.terminal.hideCursor(); @@ -423,7 +422,7 @@ export class TUI extends Container { async waitForRender(): Promise { if (!this.renderRequested && !this.rendering) return; - await new Promise((resolve) => { + await new Promise(resolve => { const check = () => { if (!this.renderRequested && !this.rendering) { resolve(); @@ -467,7 +466,7 @@ export class TUI extends Container { // If focused component is an overlay, verify it's still visible // (visibility can change due to terminal resize or visible() callback) - const focusedOverlay = this.overlayStack.find((o) => o.component === this.focusedComponent); + const focusedOverlay = this.overlayStack.find(o => o.component === this.focusedComponent); if (focusedOverlay && !this.isOverlayVisible(focusedOverlay)) { // Focused overlay is no longer visible, redirect to topmost visible overlay const topVisible = this.getTopmostVisibleOverlay(); @@ -752,7 +751,7 @@ export class TUI extends Container { private applyLineResets(lines: string[]): string[] { const reset = TUI.SEGMENT_RESET; - return lines.map((line) => (this.containsImage(line) ? line : line + reset)); + return lines.map(line => (this.containsImage(line) ? line : line + reset)); } /** diff --git a/packages/tui/src/utils.ts b/packages/tui/src/utils.ts index 6d17acad4..a3eaaf179 100644 --- a/packages/tui/src/utils.ts +++ b/packages/tui/src/utils.ts @@ -555,7 +555,7 @@ function wrapSingleLine(line: string, width: number): string[] { } // Trailing whitespace can cause lines to exceed the requested width - return wrapped.length > 0 ? wrapped.map((line) => line.trimEnd()) : [""]; + return wrapped.length > 0 ? wrapped.map(line => line.trimEnd()) : [""]; } const PUNCTUATION_REGEX = /[(){}[\]<>.,;:'"!?+\-=*/\\|&%^$#@~`]/; diff --git a/packages/tui/test/chat-simple.ts b/packages/tui/test/chat-simple.ts index f71499c68..1b71ad42c 100644 --- a/packages/tui/test/chat-simple.ts +++ b/packages/tui/test/chat-simple.ts @@ -1,7 +1,6 @@ /** * Simple chat interface demo using tui.ts */ - import { CombinedAutocompleteProvider } from "@oh-my-pi/pi-tui/autocomplete"; import { Editor } from "@oh-my-pi/pi-tui/components/editor"; import { Loader } from "@oh-my-pi/pi-tui/components/loader"; @@ -87,8 +86,8 @@ editor.onSubmit = (value: string) => { const loader = new Loader( tui, - (s) => chalk.cyan(s), - (s) => chalk.dim(s), + s => chalk.cyan(s), + s => chalk.dim(s), "Thinking...", ); children.splice(children.length - 1, 0, loader); diff --git a/packages/tui/test/editor.test.ts b/packages/tui/test/editor.test.ts index e928cd16a..173f5524b 100644 --- a/packages/tui/test/editor.test.ts +++ b/packages/tui/test/editor.test.ts @@ -524,7 +524,7 @@ describe("Editor component", () => { // Verify content split correctly - extract content between borders // Middle lines use "│ " and " │", last line uses "╰─ " and " ─╯" - const contentLines = lines.slice(1).map((l) => { + const contentLines = lines.slice(1).map(l => { const stripped = stripVTControlCharacters(l); // Both border styles use 3 chars on each side return stripped.slice(3, -3).trim(); @@ -599,7 +599,7 @@ describe("Editor component", () => { // Extract text content (strip borders and control characters) const allText = lines - .map((l) => stripVTControlCharacters(l)) + .map(l => stripVTControlCharacters(l)) .join("") .replace(/[+\-|]/g, "") .replace(/\s+/g, " ") @@ -688,7 +688,7 @@ describe("Editor component", () => { // Extract and verify content const allText = lines - .map((l) => stripVTControlCharacters(l)) + .map(l => stripVTControlCharacters(l)) .join("") .replace(/[+\-|]/g, "") .trim(); diff --git a/packages/tui/test/image-test.ts b/packages/tui/test/image-test.ts index 8eb0aac4e..bdde146fe 100644 --- a/packages/tui/test/image-test.ts +++ b/packages/tui/test/image-test.ts @@ -34,7 +34,7 @@ tui.addChild(new Spacer(1)); if (dims) { tui.addChild( - new Image(base64Data, "image/png", { fallbackColor: (s) => `\x1b[33m${s}\x1b[0m` }, { maxWidthCells: 60 }, dims), + new Image(base64Data, "image/png", { fallbackColor: s => `\x1b[33m${s}\x1b[0m` }, { maxWidthCells: 60 }, dims), ); } else { tui.addChild(new Text("Could not parse image dimensions", 1, 0)); diff --git a/packages/tui/test/key-tester.ts b/packages/tui/test/key-tester.ts index 3aed4a7ad..fe21355c7 100755 --- a/packages/tui/test/key-tester.ts +++ b/packages/tui/test/key-tester.ts @@ -26,7 +26,7 @@ class KeyLogger implements Component { // Convert to various representations const hex = Buffer.from(data).toString("hex"); const charCodes = Array.from(data) - .map((c) => c.charCodeAt(0)) + .map(c => c.charCodeAt(0)) .join(", "); const repr = data .replace(/\x1b/g, "\\x1b") diff --git a/packages/tui/test/markdown.test.ts b/packages/tui/test/markdown.test.ts index f3f68c320..5891e4255 100644 --- a/packages/tui/test/markdown.test.ts +++ b/packages/tui/test/markdown.test.ts @@ -25,13 +25,13 @@ describe("Markdown component", () => { expect(lines.length > 0).toBeTruthy(); // Strip ANSI codes for checking - const plainLines = lines.map((line) => line.replace(/\x1b\[[0-9;]*m/g, "")); + const plainLines = lines.map(line => line.replace(/\x1b\[[0-9;]*m/g, "")); // Check structure - expect(plainLines.some((line) => line.includes("- Item 1"))).toBeTruthy(); - expect(plainLines.some((line) => line.includes(" - Nested 1.1"))).toBeTruthy(); - expect(plainLines.some((line) => line.includes(" - Nested 1.2"))).toBeTruthy(); - expect(plainLines.some((line) => line.includes("- Item 2"))).toBeTruthy(); + expect(plainLines.some(line => line.includes("- Item 1"))).toBeTruthy(); + expect(plainLines.some(line => line.includes(" - Nested 1.1"))).toBeTruthy(); + expect(plainLines.some(line => line.includes(" - Nested 1.2"))).toBeTruthy(); + expect(plainLines.some(line => line.includes("- Item 2"))).toBeTruthy(); }); it("should render deeply nested list", () => { @@ -46,13 +46,13 @@ describe("Markdown component", () => { ); const lines = markdown.render(80); - const plainLines = lines.map((line) => line.replace(/\x1b\[[0-9;]*m/g, "")); + const plainLines = lines.map(line => line.replace(/\x1b\[[0-9;]*m/g, "")); // Check proper indentation - expect(plainLines.some((line) => line.includes("- Level 1"))).toBeTruthy(); - expect(plainLines.some((line) => line.includes(" - Level 2"))).toBeTruthy(); - expect(plainLines.some((line) => line.includes(" - Level 3"))).toBeTruthy(); - expect(plainLines.some((line) => line.includes(" - Level 4"))).toBeTruthy(); + expect(plainLines.some(line => line.includes("- Level 1"))).toBeTruthy(); + expect(plainLines.some(line => line.includes(" - Level 2"))).toBeTruthy(); + expect(plainLines.some(line => line.includes(" - Level 3"))).toBeTruthy(); + expect(plainLines.some(line => line.includes(" - Level 4"))).toBeTruthy(); }); it("should render ordered nested list", () => { @@ -67,12 +67,12 @@ describe("Markdown component", () => { ); const lines = markdown.render(80); - const plainLines = lines.map((line) => line.replace(/\x1b\[[0-9;]*m/g, "")); + const plainLines = lines.map(line => line.replace(/\x1b\[[0-9;]*m/g, "")); - expect(plainLines.some((line) => line.includes("1. First"))).toBeTruthy(); - expect(plainLines.some((line) => line.includes(" 1. Nested first"))).toBeTruthy(); - expect(plainLines.some((line) => line.includes(" 2. Nested second"))).toBeTruthy(); - expect(plainLines.some((line) => line.includes("2. Second"))).toBeTruthy(); + expect(plainLines.some(line => line.includes("1. First"))).toBeTruthy(); + expect(plainLines.some(line => line.includes(" 1. Nested first"))).toBeTruthy(); + expect(plainLines.some(line => line.includes(" 2. Nested second"))).toBeTruthy(); + expect(plainLines.some(line => line.includes("2. Second"))).toBeTruthy(); }); it("should render mixed ordered and unordered nested lists", () => { @@ -88,11 +88,11 @@ describe("Markdown component", () => { ); const lines = markdown.render(80); - const plainLines = lines.map((line) => line.replace(/\x1b\[[0-9;]*m/g, "")); + const plainLines = lines.map(line => line.replace(/\x1b\[[0-9;]*m/g, "")); - expect(plainLines.some((line) => line.includes("1. Ordered item"))).toBeTruthy(); - expect(plainLines.some((line) => line.includes(" - Unordered nested"))).toBeTruthy(); - expect(plainLines.some((line) => line.includes("2. Second ordered"))).toBeTruthy(); + expect(plainLines.some(line => line.includes("1. Ordered item"))).toBeTruthy(); + expect(plainLines.some(line => line.includes(" - Unordered nested"))).toBeTruthy(); + expect(plainLines.some(line => line.includes("2. Second ordered"))).toBeTruthy(); }); }); @@ -109,16 +109,16 @@ describe("Markdown component", () => { ); const lines = markdown.render(80); - const plainLines = lines.map((line) => line.replace(/\x1b\[[0-9;]*m/g, "")); + const plainLines = lines.map(line => line.replace(/\x1b\[[0-9;]*m/g, "")); // Check table structure - expect(plainLines.some((line) => line.includes("Name"))).toBeTruthy(); - expect(plainLines.some((line) => line.includes("Age"))).toBeTruthy(); - expect(plainLines.some((line) => line.includes("Alice"))).toBeTruthy(); - expect(plainLines.some((line) => line.includes("Bob"))).toBeTruthy(); + expect(plainLines.some(line => line.includes("Name"))).toBeTruthy(); + expect(plainLines.some(line => line.includes("Age"))).toBeTruthy(); + expect(plainLines.some(line => line.includes("Alice"))).toBeTruthy(); + expect(plainLines.some(line => line.includes("Bob"))).toBeTruthy(); // Check for table borders - expect(plainLines.some((line) => line.includes("|"))).toBeTruthy(); - expect(plainLines.some((line) => line.includes("-"))).toBeTruthy(); + expect(plainLines.some(line => line.includes("|"))).toBeTruthy(); + expect(plainLines.some(line => line.includes("-"))).toBeTruthy(); }); it("should render table with alignment", () => { @@ -133,14 +133,14 @@ describe("Markdown component", () => { ); const lines = markdown.render(80); - const plainLines = lines.map((line) => line.replace(/\x1b\[[0-9;]*m/g, "")); + const plainLines = lines.map(line => line.replace(/\x1b\[[0-9;]*m/g, "")); // Check headers - expect(plainLines.some((line) => line.includes("Left"))).toBeTruthy(); - expect(plainLines.some((line) => line.includes("Center"))).toBeTruthy(); - expect(plainLines.some((line) => line.includes("Right"))).toBeTruthy(); + expect(plainLines.some(line => line.includes("Left"))).toBeTruthy(); + expect(plainLines.some(line => line.includes("Center"))).toBeTruthy(); + expect(plainLines.some(line => line.includes("Right"))).toBeTruthy(); // Check content - expect(plainLines.some((line) => line.includes("Long text"))).toBeTruthy(); + expect(plainLines.some(line => line.includes("Long text"))).toBeTruthy(); }); it("should handle tables with varying column widths", () => { @@ -159,9 +159,9 @@ describe("Markdown component", () => { // Should render without errors expect(lines.length > 0).toBeTruthy(); - const plainLines = lines.map((line) => line.replace(/\x1b\[[0-9;]*m/g, "")); - expect(plainLines.some((line) => line.includes("Very long column header"))).toBeTruthy(); - expect(plainLines.some((line) => line.includes("This is a much longer cell content"))).toBeTruthy(); + const plainLines = lines.map(line => line.replace(/\x1b\[[0-9;]*m/g, "")); + expect(plainLines.some(line => line.includes("Very long column header"))).toBeTruthy(); + expect(plainLines.some(line => line.includes("This is a much longer cell content"))).toBeTruthy(); }); it("should wrap table cells when table exceeds available width", () => { @@ -177,7 +177,7 @@ describe("Markdown component", () => { // Render at narrow width that forces wrapping const lines = markdown.render(50); - const plainLines = lines.map((line) => line.replace(/\x1b\[[0-9;]*m/g, "").trimEnd()); + const plainLines = lines.map(line => line.replace(/\x1b\[[0-9;]*m/g, "").trimEnd()); // All lines should fit within width for (const line of plainLines) { @@ -204,10 +204,10 @@ describe("Markdown component", () => { // Render at width that forces the cell to wrap const lines = markdown.render(25); - const plainLines = lines.map((line) => line.replace(/\x1b\[[0-9;]*m/g, "").trimEnd()); + const plainLines = lines.map(line => line.replace(/\x1b\[[0-9;]*m/g, "").trimEnd()); // Should have multiple data rows due to wrapping - const dataRows = plainLines.filter((line) => line.startsWith("|") && !line.includes("-")); + const dataRows = plainLines.filter(line => line.startsWith("|") && !line.includes("-")); expect(dataRows.length > 2).toBeTruthy(); // All content should be preserved (may be split across lines) @@ -230,7 +230,7 @@ describe("Markdown component", () => { const width = 30; const lines = markdown.render(width); - const plainLines = lines.map((line) => line.replace(/\x1b\[[0-9;]*m/g, "").trimEnd()); + const plainLines = lines.map(line => line.replace(/\x1b\[[0-9;]*m/g, "").trimEnd()); for (const line of plainLines) { expect( @@ -240,7 +240,7 @@ describe("Markdown component", () => { } // Borders should stay intact (exactly 2 vertical borders for a 1-col table) - const tableLines = plainLines.filter((line) => line.startsWith("|")); + const tableLines = plainLines.filter(line => line.startsWith("|")); for (const line of tableLines) { const borderCount = line.split("|").length - 1; expect(borderCount).toBe(2); @@ -268,7 +268,7 @@ describe("Markdown component", () => { const joinedOutput = lines.join("\n"); expect(joinedOutput.includes("\x1b[33m")).toBeTruthy(); - const plainLines = lines.map((line) => line.replace(/\x1b\[[0-9;]*m/g, "").trimEnd()); + const plainLines = lines.map(line => line.replace(/\x1b\[[0-9;]*m/g, "").trimEnd()); for (const line of plainLines) { expect( line.length <= width, @@ -276,7 +276,7 @@ describe("Markdown component", () => { ); } - const tableLines = plainLines.filter((line) => line.startsWith("|")); + const tableLines = plainLines.filter(line => line.startsWith("|")); for (const line of tableLines) { const borderCount = line.split("|").length - 1; expect(borderCount).toBe(2); @@ -295,7 +295,7 @@ describe("Markdown component", () => { // Very narrow width const lines = markdown.render(15); - const plainLines = lines.map((line) => line.replace(/\x1b\[[0-9;]*m/g, "").trimEnd()); + const plainLines = lines.map(line => line.replace(/\x1b\[[0-9;]*m/g, "").trimEnd()); // Should not crash and should produce output expect(lines.length > 0).toBeTruthy(); @@ -318,17 +318,17 @@ describe("Markdown component", () => { // Wide width where table fits naturally const lines = markdown.render(80); - const plainLines = lines.map((line) => line.replace(/\x1b\[[0-9;]*m/g, "").trimEnd()); + const plainLines = lines.map(line => line.replace(/\x1b\[[0-9;]*m/g, "").trimEnd()); // Should have proper table structure - const headerLine = plainLines.find((line) => line.includes("A") && line.includes("B")); + const headerLine = plainLines.find(line => line.includes("A") && line.includes("B")); expect(headerLine).toBeTruthy(); expect(headerLine?.includes("|")).toBeTruthy(); - const separatorLine = plainLines.find((line) => line.includes("+") && line.includes("+")); + const separatorLine = plainLines.find(line => line.includes("+") && line.includes("+")); expect(separatorLine).toBeTruthy(); - const dataLine = plainLines.find((line) => line.includes("1") && line.includes("2")); + const dataLine = plainLines.find(line => line.includes("1") && line.includes("2")); expect(dataLine).toBeTruthy(); }); @@ -344,7 +344,7 @@ describe("Markdown component", () => { // Width 40 with paddingX=2 means contentWidth=36 const lines = markdown.render(40); - const plainLines = lines.map((line) => line.replace(/\x1b\[[0-9;]*m/g, "").trimEnd()); + const plainLines = lines.map(line => line.replace(/\x1b\[[0-9;]*m/g, "").trimEnd()); // All lines should respect width for (const line of plainLines) { @@ -352,7 +352,7 @@ describe("Markdown component", () => { } // Table rows should have left padding - const tableRow = plainLines.find((line) => line.includes("|")); + const tableRow = plainLines.find(line => line.includes("|")); expect(tableRow?.startsWith(" ")).toBeTruthy(); }); }); @@ -375,16 +375,16 @@ describe("Markdown component", () => { ); const lines = markdown.render(80); - const plainLines = lines.map((line) => line.replace(/\x1b\[[0-9;]*m/g, "")); + const plainLines = lines.map(line => line.replace(/\x1b\[[0-9;]*m/g, "")); // Check heading - expect(plainLines.some((line) => line.includes("Test Document"))).toBeTruthy(); + expect(plainLines.some(line => line.includes("Test Document"))).toBeTruthy(); // Check list - expect(plainLines.some((line) => line.includes("- Item 1"))).toBeTruthy(); - expect(plainLines.some((line) => line.includes(" - Nested item"))).toBeTruthy(); + expect(plainLines.some(line => line.includes("- Item 1"))).toBeTruthy(); + expect(plainLines.some(line => line.includes(" - Nested item"))).toBeTruthy(); // Check table - expect(plainLines.some((line) => line.includes("Col1"))).toBeTruthy(); - expect(plainLines.some((line) => line.includes("|"))).toBeTruthy(); + expect(plainLines.some(line => line.includes("Col1"))).toBeTruthy(); + expect(plainLines.some(line => line.includes("|"))).toBeTruthy(); }); }); @@ -397,7 +397,7 @@ describe("Markdown component", () => { 0, defaultMarkdownTheme, { - color: (text) => chalk.gray(text), + color: text => chalk.gray(text), italic: true, }, ); @@ -424,7 +424,7 @@ describe("Markdown component", () => { 0, defaultMarkdownTheme, { - color: (text) => chalk.gray(text), + color: text => chalk.gray(text), italic: true, }, ); @@ -460,13 +460,13 @@ again, hello world`, ); const lines = markdown.render(80); - const plainLines = lines.map((line) => line.replace(/\x1b\[[0-9;]*m/g, "").trimEnd()); + const plainLines = lines.map(line => line.replace(/\x1b\[[0-9;]*m/g, "").trimEnd()); const closingBackticksIndex = plainLines.indexOf("```"); expect(closingBackticksIndex !== -1).toBeTruthy(); const afterBackticks = plainLines.slice(closingBackticksIndex + 1); - const emptyLineCount = afterBackticks.findIndex((line) => line !== ""); + const emptyLineCount = afterBackticks.findIndex(line => line !== ""); expect(emptyLineCount).toBe(1); }); @@ -486,13 +486,13 @@ again, hello world`, ); const lines = markdown.render(80); - const plainLines = lines.map((line) => line.replace(/\x1b\[[0-9;]*m/g, "").trimEnd()); + const plainLines = lines.map(line => line.replace(/\x1b\[[0-9;]*m/g, "").trimEnd()); - const dividerIndex = plainLines.findIndex((line) => line.includes("-")); + const dividerIndex = plainLines.findIndex(line => line.includes("-")); expect(dividerIndex !== -1).toBeTruthy(); const afterDivider = plainLines.slice(dividerIndex + 1); - const emptyLineCount = afterDivider.findIndex((line) => line !== ""); + const emptyLineCount = afterDivider.findIndex(line => line !== ""); expect(emptyLineCount).toBe(1); }); @@ -510,13 +510,13 @@ This is a paragraph`, ); const lines = markdown.render(80); - const plainLines = lines.map((line) => line.replace(/\x1b\[[0-9;]*m/g, "").trimEnd()); + const plainLines = lines.map(line => line.replace(/\x1b\[[0-9;]*m/g, "").trimEnd()); - const headingIndex = plainLines.findIndex((line) => line.includes("Hello")); + const headingIndex = plainLines.findIndex(line => line.includes("Hello")); expect(headingIndex !== -1).toBeTruthy(); const afterHeading = plainLines.slice(headingIndex + 1); - const emptyLineCount = afterHeading.findIndex((line) => line !== ""); + const emptyLineCount = afterHeading.findIndex(line => line !== ""); expect(emptyLineCount).toBe(1); }); @@ -536,13 +536,13 @@ again, hello world`, ); const lines = markdown.render(80); - const plainLines = lines.map((line) => line.replace(/\x1b\[[0-9;]*m/g, "").trimEnd()); + const plainLines = lines.map(line => line.replace(/\x1b\[[0-9;]*m/g, "").trimEnd()); - const quoteIndex = plainLines.findIndex((line) => line.includes("This is a quote")); + const quoteIndex = plainLines.findIndex(line => line.includes("This is a quote")); expect(quoteIndex !== -1).toBeTruthy(); const afterQuote = plainLines.slice(quoteIndex + 1); - const emptyLineCount = afterQuote.findIndex((line) => line !== ""); + const emptyLineCount = afterQuote.findIndex(line => line !== ""); expect(emptyLineCount).toBe(1); }); @@ -560,7 +560,7 @@ again, hello world`, ); const lines = markdown.render(80); - const plainLines = lines.map((line) => line.replace(/\x1b\[[0-9;]*m/g, "")); + const plainLines = lines.map(line => line.replace(/\x1b\[[0-9;]*m/g, "")); const joinedPlain = plainLines.join(" "); // The content inside the tags should be visible @@ -571,7 +571,7 @@ again, hello world`, const markdown = new Markdown("```html\n
Some HTML
\n```", 0, 0, defaultMarkdownTheme); const lines = markdown.render(80); - const plainLines = lines.map((line) => line.replace(/\x1b\[[0-9;]*m/g, "")); + const plainLines = lines.map(line => line.replace(/\x1b\[[0-9;]*m/g, "")); const joinedPlain = plainLines.join("\n"); // HTML in code blocks should be visible diff --git a/packages/tui/test/test-themes.ts b/packages/tui/test/test-themes.ts index 1bf214935..3bc50d6f2 100644 --- a/packages/tui/test/test-themes.ts +++ b/packages/tui/test/test-themes.ts @@ -1,7 +1,6 @@ /** * Default themes for TUI tests using chalk */ - import type { EditorTheme, MarkdownTheme, SelectListTheme, SymbolTheme } from "@oh-my-pi/pi-tui"; import { Chalk } from "chalk"; diff --git a/packages/tui/test/virtual-terminal.ts b/packages/tui/test/virtual-terminal.ts index 6b85de91d..eefba8765 100644 --- a/packages/tui/test/virtual-terminal.ts +++ b/packages/tui/test/virtual-terminal.ts @@ -124,7 +124,7 @@ export class VirtualTerminal implements Terminal { */ async flush(): Promise { // Write an empty string to ensure all previous writes are flushed - return new Promise((resolve) => { + return new Promise(resolve => { this.xterm.write("", () => resolve()); }); }