diff --git a/biome.json b/biome.json index 95ba4216..e1ab3f25 100644 --- a/biome.json +++ b/biome.json @@ -7,11 +7,7 @@ "style": { "noNonNullAssertion": "off", "useConst": "error", - "useNodejsImportProtocol": "off", - "useTemplate": "off" - }, - "correctness": { - "noUnusedVariables": "off" + "useNodejsImportProtocol": "off" }, "suspicious": { "noExplicitAny": "off", diff --git a/packages/ai/src/agent/agent-loop.ts b/packages/ai/src/agent/agent-loop.ts index a33d1ffb..badb1bca 100644 --- a/packages/ai/src/agent/agent-loop.ts +++ b/packages/ai/src/agent/agent-loop.ts @@ -177,6 +177,7 @@ async function streamAssistantResponse( systemPrompt: context.systemPrompt, messages: [...processedMessages].map((m) => { if (m.role === "toolResult") { + // biome-ignore lint/correctness/noUnusedVariables: fine here const { details, ...rest } = m; return rest; } else { diff --git a/packages/ai/src/agent/tools/calculate.ts b/packages/ai/src/agent/tools/calculate.ts index ef34359d..afc75889 100644 --- a/packages/ai/src/agent/tools/calculate.ts +++ b/packages/ai/src/agent/tools/calculate.ts @@ -8,7 +8,7 @@ export interface CalculateResult extends AgentToolResult { export function calculate(expression: string): CalculateResult { try { - const result = new Function("return " + expression)(); + const result = new Function(`return ${expression}`)(); return { content: [{ type: "text", text: `${expression} = ${result}` }], details: undefined }; } catch (e: any) { throw new Error(e.message || String(e)); diff --git a/packages/ai/src/agent/tools/get-current-time.ts b/packages/ai/src/agent/tools/get-current-time.ts index a9ffed25..2d989243 100644 --- a/packages/ai/src/agent/tools/get-current-time.ts +++ b/packages/ai/src/agent/tools/get-current-time.ts @@ -17,7 +17,7 @@ export async function getCurrentTime(timezone?: string): Promise = ( currentItem = item; currentBlock = { type: "toolCall", - id: item.call_id + "|" + item.id, + id: `${item.call_id}|${item.id}`, name: item.name, arguments: {}, partialJson: item.arguments || "", @@ -251,7 +251,7 @@ export const streamOpenAIResponses: StreamFunction<"openai-responses"> = ( } else if (item.type === "function_call") { const toolCall: ToolCall = { type: "toolCall", - id: item.call_id + "|" + item.id, + id: `${item.call_id}|${item.id}`, name: item.name, arguments: JSON.parse(item.arguments), }; @@ -462,9 +462,9 @@ function convertMessages(model: Model<"openai-responses">, context: Context): Re // OpenAI requires id to be max 64 characters let msgId = textBlock.textSignature; if (!msgId) { - msgId = "msg_" + msgIndex; + msgId = `msg_${msgIndex}`; } else if (msgId.length > 64) { - msgId = "msg_" + shortHash(msgId); + msgId = `msg_${shortHash(msgId)}`; } output.push({ type: "message", diff --git a/packages/ai/src/utils/oauth/google-antigravity.ts b/packages/ai/src/utils/oauth/google-antigravity.ts index 645ef94a..df5b28a5 100644 --- a/packages/ai/src/utils/oauth/google-antigravity.ts +++ b/packages/ai/src/utils/oauth/google-antigravity.ts @@ -27,9 +27,6 @@ const SCOPES = [ const AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth"; const TOKEN_URL = "https://oauth2.googleapis.com/token"; -// Antigravity uses sandbox endpoint -const CODE_ASSIST_ENDPOINT = "https://daily-cloudcode-pa.sandbox.googleapis.com"; - // Fallback project ID when discovery fails const DEFAULT_PROJECT_ID = "rising-fact-p41fc"; @@ -115,13 +112,6 @@ interface LoadCodeAssistPayload { allowedTiers?: Array<{ id?: string; isDefault?: boolean }>; } -/** - * Wait helper for onboarding retries - */ -function wait(ms: number): Promise { - return new Promise((resolve) => setTimeout(resolve, ms)); -} - /** * Discover or provision a project for the user */ diff --git a/packages/ai/src/utils/validation.ts b/packages/ai/src/utils/validation.ts index 4c778880..233d6893 100644 --- a/packages/ai/src/utils/validation.ts +++ b/packages/ai/src/utils/validation.ts @@ -21,7 +21,7 @@ if (!isBrowserExtension) { strict: false, }); addFormats(ajv); - } catch (e) { + } catch (_e) { // AJV initialization failed (likely CSP restriction) console.warn("AJV validation disabled due to CSP restrictions"); } diff --git a/packages/ai/test/agent.test.ts b/packages/ai/test/agent.test.ts index 434b0056..39454d52 100644 --- a/packages/ai/test/agent.test.ts +++ b/packages/ai/test/agent.test.ts @@ -215,7 +215,7 @@ async function abortTest(model: Model, options: OptionsF const stream = agentLoop(userPrompt, context, config, abortController.signal); // Abort after first tool execution - const abortPromise = (async () => { + (async () => { for await (const event of stream) { events.push(event); diff --git a/packages/ai/test/context-overflow.test.ts b/packages/ai/test/context-overflow.test.ts index 4bb17059..33fb5ed2 100644 --- a/packages/ai/test/context-overflow.test.ts +++ b/packages/ai/test/context-overflow.test.ts @@ -444,7 +444,7 @@ describe("Context overflow error handling", () => { console.log("Pulling gpt-oss:20b model for Ollama overflow tests..."); try { execSync("ollama pull gpt-oss:20b", { stdio: "inherit" }); - } catch (e) { + } catch (_e) { console.warn("Failed to pull gpt-oss:20b model, tests will be skipped"); return; } diff --git a/packages/ai/test/handoff.test.ts b/packages/ai/test/handoff.test.ts index 72ea83eb..61dc3d78 100644 --- a/packages/ai/test/handoff.test.ts +++ b/packages/ai/test/handoff.test.ts @@ -554,7 +554,7 @@ describe("Cross-Provider Handoff Tests", () => { let successCount = 0; const totalTests = contextTests.length; - for (const { label, context, sourceModel } of contextTests) { + for (const { label, context } of contextTests) { const success = await testProviderHandoff(model, label, context); if (success) successCount++; } diff --git a/packages/ai/test/stream.test.ts b/packages/ai/test/stream.test.ts index eda6e3fb..158b4ab0 100644 --- a/packages/ai/test/stream.test.ts +++ b/packages/ai/test/stream.test.ts @@ -866,7 +866,7 @@ describe("Generate E2E Tests", () => { console.log("Pulling gpt-oss:20b model for Ollama tests..."); try { execSync("ollama pull gpt-oss:20b", { stdio: "inherit" }); - } catch (e) { + } catch (_e) { console.warn("Failed to pull gpt-oss:20b model, tests will be skipped"); return; } diff --git a/packages/ai/test/tool-call-without-result.test.ts b/packages/ai/test/tool-call-without-result.test.ts index cde53b94..63f303b1 100644 --- a/packages/ai/test/tool-call-without-result.test.ts +++ b/packages/ai/test/tool-call-without-result.test.ts @@ -1,4 +1,4 @@ -import { type Static, Type } from "@sinclair/typebox"; +import { Type } from "@sinclair/typebox"; import { describe, expect, it } from "vitest"; import { getModel } from "../src/models.js"; import { complete, resolveApiKey } from "../src/stream.js"; @@ -18,8 +18,6 @@ const calculateSchema = Type.Object({ expression: Type.String({ description: "The mathematical expression to evaluate" }), }); -type CalculateParams = Static; - const calculateTool: Tool = { name: "calculate", description: "Evaluate mathematical expressions", diff --git a/packages/coding-agent/src/core/compaction.ts b/packages/coding-agent/src/core/compaction.ts index 58920895..7ea09f44 100644 --- a/packages/coding-agent/src/core/compaction.ts +++ b/packages/coding-agent/src/core/compaction.ts @@ -420,7 +420,7 @@ export async function compact( generateTurnPrefixSummary(turnPrefixMessages, model, settings.reserveTokens, apiKey, signal), ]); // Merge into single summary - summary = historyResult + "\n\n---\n\n**Turn Context (split turn):**\n\n" + turnPrefixResult; + summary = `${historyResult}\n\n---\n\n**Turn Context (split turn):**\n\n${turnPrefixResult}`; } else { // Just generate history summary summary = await generateSummary( diff --git a/packages/coding-agent/src/core/export-html.ts b/packages/coding-agent/src/core/export-html.ts index 15078536..deceddd6 100644 --- a/packages/coding-agent/src/core/export-html.ts +++ b/packages/coding-agent/src/core/export-html.ts @@ -84,7 +84,7 @@ function escapeHtml(text: string): string { function shortenPath(path: string): string { const home = homedir(); - return path.startsWith(home) ? "~" + path.slice(home.length) : path; + return path.startsWith(home) ? `~${path.slice(home.length)}` : path; } function replaceTabs(text: string): string { diff --git a/packages/coding-agent/src/core/messages.ts b/packages/coding-agent/src/core/messages.ts index dfa6aa19..947801f8 100644 --- a/packages/coding-agent/src/core/messages.ts +++ b/packages/coding-agent/src/core/messages.ts @@ -54,7 +54,7 @@ export function isBashExecutionMessage(msg: AppMessage | Message): msg is BashEx export function bashExecutionToText(msg: BashExecutionMessage): string { let text = `Ran \`${msg.command}\`\n`; if (msg.output) { - text += "```\n" + msg.output + "\n```"; + text += `\`\`\`\n${msg.output}\n\`\`\``; } else { text += "(no output)"; } diff --git a/packages/coding-agent/src/core/model-config.ts b/packages/coding-agent/src/core/model-config.ts index 7d7d9334..a188b61e 100644 --- a/packages/coding-agent/src/core/model-config.ts +++ b/packages/coding-agent/src/core/model-config.ts @@ -76,8 +76,6 @@ const ModelsConfigSchema = Type.Object({ }); type ModelsConfig = Static; -type ProviderConfig = Static; -type ModelDefinition = Static; // Custom provider API key mappings (provider name -> apiKey config) const customProviderApiKeys: Map = new Map(); diff --git a/packages/coding-agent/src/core/session-manager.ts b/packages/coding-agent/src/core/session-manager.ts index f82bc400..81050b46 100644 --- a/packages/coding-agent/src/core/session-manager.ts +++ b/packages/coding-agent/src/core/session-manager.ts @@ -243,7 +243,7 @@ export class SessionManager { private getSessionDirectory(): string { const cwd = process.cwd(); // Replace all path separators and colons (for Windows drive letters) with dashes - const safePath = "--" + cwd.replace(/^[/\\]/, "").replace(/[/\\:]/g, "-") + "--"; + const safePath = `--${cwd.replace(/^[/\\]/, "").replace(/[/\\:]/g, "-")}--`; const configDir = getAgentDir(); const sessionDir = join(configDir, "sessions", safePath); @@ -325,9 +325,9 @@ export class SessionManager { // Write to file only if enabled if (this.enabled) { - appendFileSync(this.sessionFile, JSON.stringify(entry) + "\n"); + appendFileSync(this.sessionFile, `${JSON.stringify(entry)}\n`); for (const memEntry of this.inMemoryEntries.slice(1)) { - appendFileSync(this.sessionFile, JSON.stringify(memEntry) + "\n"); + appendFileSync(this.sessionFile, `${JSON.stringify(memEntry)}\n`); } } } @@ -346,7 +346,7 @@ export class SessionManager { this.inMemoryEntries.push(entry); // Write to file only if enabled if (this.enabled) { - appendFileSync(this.sessionFile, JSON.stringify(entry) + "\n"); + appendFileSync(this.sessionFile, `${JSON.stringify(entry)}\n`); } } } @@ -365,7 +365,7 @@ export class SessionManager { this.inMemoryEntries.push(entry); // Write to file only if enabled if (this.enabled) { - appendFileSync(this.sessionFile, JSON.stringify(entry) + "\n"); + appendFileSync(this.sessionFile, `${JSON.stringify(entry)}\n`); } } } @@ -385,7 +385,7 @@ export class SessionManager { this.inMemoryEntries.push(entry); // Write to file only if enabled if (this.enabled) { - appendFileSync(this.sessionFile, JSON.stringify(entry) + "\n"); + appendFileSync(this.sessionFile, `${JSON.stringify(entry)}\n`); } } } @@ -395,7 +395,7 @@ export class SessionManager { this.inMemoryEntries.push(entry); // Write to file only if enabled if (this.enabled) { - appendFileSync(this.sessionFile, JSON.stringify(entry) + "\n"); + appendFileSync(this.sessionFile, `${JSON.stringify(entry)}\n`); } } @@ -625,7 +625,7 @@ export class SessionManager { thinkingLevel: state.thinkingLevel, branchedFrom: this.sessionFile, }; - appendFileSync(newSessionFile, JSON.stringify(entry) + "\n"); + appendFileSync(newSessionFile, `${JSON.stringify(entry)}\n`); // Write messages up to and including the branch point (if >= 0) if (branchFromIndex >= 0) { @@ -636,7 +636,7 @@ export class SessionManager { timestamp: new Date().toISOString(), message, }; - appendFileSync(newSessionFile, JSON.stringify(messageEntry) + "\n"); + appendFileSync(newSessionFile, `${JSON.stringify(messageEntry)}\n`); } } @@ -675,7 +675,7 @@ export class SessionManager { if (this.enabled) { // Write to file for (const entry of newEntries) { - appendFileSync(newSessionFile, JSON.stringify(entry) + "\n"); + appendFileSync(newSessionFile, `${JSON.stringify(entry)}\n`); } return newSessionFile; } else { diff --git a/packages/coding-agent/src/core/slash-commands.ts b/packages/coding-agent/src/core/slash-commands.ts index 5450d622..9c599c83 100644 --- a/packages/coding-agent/src/core/slash-commands.ts +++ b/packages/coding-agent/src/core/slash-commands.ts @@ -153,12 +153,12 @@ function loadCommandsFromDir(dir: string, source: "user" | "project", subdir: st content, source: sourceStr, }); - } catch (error) { + } catch (_error) { // Silently skip files that can't be read } } } - } catch (error) { + } catch (_error) { // Silently skip directories that can't be read } diff --git a/packages/coding-agent/src/core/tools/grep.ts b/packages/coding-agent/src/core/tools/grep.ts index 7e75178e..c0cef5b8 100644 --- a/packages/coding-agent/src/core/tools/grep.ts +++ b/packages/coding-agent/src/core/tools/grep.ts @@ -89,7 +89,7 @@ export const grepTool: AgentTool = { let searchStat: Stats; try { searchStat = statSync(searchPath); - } catch (err) { + } catch (_err) { settle(() => reject(new Error(`Path not found: ${searchPath}`))); return; } diff --git a/packages/coding-agent/src/core/tools/read.ts b/packages/coding-agent/src/core/tools/read.ts index 4b83534b..fdadd528 100644 --- a/packages/coding-agent/src/core/tools/read.ts +++ b/packages/coding-agent/src/core/tools/read.ts @@ -126,7 +126,6 @@ export const readTool: AgentTool = { details = { truncation }; } else if (userLimitedLines !== undefined && startLine + userLimitedLines < allLines.length) { // User specified limit, there's more content, but no truncation - const endLineDisplay = startLineDisplay + userLimitedLines - 1; const remaining = allLines.length - (startLine + userLimitedLines); const nextOffset = startLine + userLimitedLines + 1; diff --git a/packages/coding-agent/src/core/tools/truncate.ts b/packages/coding-agent/src/core/tools/truncate.ts index 12d618eb..18ac5d74 100644 --- a/packages/coding-agent/src/core/tools/truncate.ts +++ b/packages/coding-agent/src/core/tools/truncate.ts @@ -261,5 +261,5 @@ export function truncateLine( if (line.length <= maxChars) { return { text: line, wasTruncated: false }; } - return { text: line.slice(0, maxChars) + "... [truncated]", wasTruncated: true }; + return { text: `${line.slice(0, maxChars)}... [truncated]`, wasTruncated: true }; } diff --git a/packages/coding-agent/src/modes/interactive/components/armin.ts b/packages/coding-agent/src/modes/interactive/components/armin.ts index 39d74757..aedfe61c 100644 --- a/packages/coding-agent/src/modes/interactive/components/armin.ts +++ b/packages/coding-agent/src/modes/interactive/components/armin.ts @@ -95,13 +95,13 @@ export class ArminComponent implements Component { // Clip row to available width before applying color const clipped = row.slice(0, availableWidth).join(""); const padRight = Math.max(0, width - padding - clipped.length); - return " " + theme.fg("accent", clipped) + " ".repeat(padRight); + return ` ${theme.fg("accent", clipped)}${" ".repeat(padRight)}`; }); // Add "ARMIN SAYS HI" at the end const message = "ARMIN SAYS HI"; const msgPadRight = Math.max(0, width - padding - message.length); - this.cachedLines.push(" " + theme.fg("accent", message) + " ".repeat(msgPadRight)); + this.cachedLines.push(` ${theme.fg("accent", message)}${" ".repeat(msgPadRight)}`); this.cachedWidth = width; this.cachedVersion = this.gridVersion; diff --git a/packages/coding-agent/src/modes/interactive/components/bash-execution.ts b/packages/coding-agent/src/modes/interactive/components/bash-execution.ts index 918129d5..106907ce 100644 --- a/packages/coding-agent/src/modes/interactive/components/bash-execution.ts +++ b/packages/coding-agent/src/modes/interactive/components/bash-execution.ts @@ -132,11 +132,11 @@ export class BashExecutionComponent extends Container { if (this.expanded) { // Show all lines const displayText = availableLines.map((line) => theme.fg("muted", line)).join("\n"); - this.contentContainer.addChild(new Text("\n" + displayText, 1, 0)); + this.contentContainer.addChild(new Text(`\n${displayText}`, 1, 0)); } else { // Render preview lines, then cap at PREVIEW_LINES visual lines const tempText = new Text( - "\n" + previewLogicalLines.map((line) => theme.fg("muted", line)).join("\n"), + `\n${previewLogicalLines.map((line) => theme.fg("muted", line)).join("\n")}`, 1, 0, ); @@ -170,7 +170,7 @@ export class BashExecutionComponent extends Container { } if (statusParts.length > 0) { - this.contentContainer.addChild(new Text("\n" + statusParts.join("\n"), 1, 0)); + this.contentContainer.addChild(new Text(`\n${statusParts.join("\n")}`, 1, 0)); } } } diff --git a/packages/coding-agent/src/modes/interactive/components/footer.ts b/packages/coding-agent/src/modes/interactive/components/footer.ts index 6d31fd1b..ce2ad8af 100644 --- a/packages/coding-agent/src/modes/interactive/components/footer.ts +++ b/packages/coding-agent/src/modes/interactive/components/footer.ts @@ -168,17 +168,17 @@ export class FooterComponent implements Component { // Format token counts (similar to web-ui) const formatTokens = (count: number): string => { if (count < 1000) return count.toString(); - if (count < 10000) return (count / 1000).toFixed(1) + "k"; - if (count < 1000000) return Math.round(count / 1000) + "k"; - if (count < 10000000) return (count / 1000000).toFixed(1) + "M"; - return Math.round(count / 1000000) + "M"; + if (count < 10000) return `${(count / 1000).toFixed(1)}k`; + if (count < 1000000) return `${Math.round(count / 1000)}k`; + if (count < 10000000) return `${(count / 1000000).toFixed(1)}M`; + return `${Math.round(count / 1000000)}M`; }; // Replace home directory with ~ let pwd = process.cwd(); const home = process.env.HOME || process.env.USERPROFILE; if (home && pwd.startsWith(home)) { - pwd = "~" + pwd.slice(home.length); + pwd = `~${pwd.slice(home.length)}`; } // Add git branch if available @@ -247,7 +247,7 @@ export class FooterComponent implements Component { if (statsLeftWidth > width) { // Truncate statsLeft to fit width (no room for right side) const plainStatsLeft = statsLeft.replace(/\x1b\[[0-9;]*m/g, ""); - statsLeft = plainStatsLeft.substring(0, width - 3) + "..."; + statsLeft = `${plainStatsLeft.substring(0, width - 3)}...`; statsLeftWidth = visibleWidth(statsLeft); } diff --git a/packages/coding-agent/src/modes/interactive/components/hook-selector.ts b/packages/coding-agent/src/modes/interactive/components/hook-selector.ts index cc00b299..e39a9119 100644 --- a/packages/coding-agent/src/modes/interactive/components/hook-selector.ts +++ b/packages/coding-agent/src/modes/interactive/components/hook-selector.ts @@ -58,7 +58,7 @@ export class HookSelectorComponent extends Container { if (isSelected) { text = theme.fg("accent", "→ ") + theme.fg("accent", option); } else { - text = " " + theme.fg("text", option); + text = ` ${theme.fg("text", option)}`; } this.listContainer.addChild(new Text(text, 1, 0)); diff --git a/packages/coding-agent/src/modes/interactive/components/model-selector.ts b/packages/coding-agent/src/modes/interactive/components/model-selector.ts index 674d5a81..2639aa96 100644 --- a/packages/coding-agent/src/modes/interactive/components/model-selector.ts +++ b/packages/coding-agent/src/modes/interactive/components/model-selector.ts @@ -175,12 +175,12 @@ export class ModelSelectorComponent extends Container { const modelText = `${item.id}`; const providerBadge = theme.fg("muted", `[${item.provider}]`); const checkmark = isCurrent ? theme.fg("success", " ✓") : ""; - line = prefix + theme.fg("accent", modelText) + " " + providerBadge + checkmark; + line = `${prefix + theme.fg("accent", modelText)} ${providerBadge}${checkmark}`; } else { const modelText = ` ${item.id}`; const providerBadge = theme.fg("muted", `[${item.provider}]`); const checkmark = isCurrent ? theme.fg("success", " ✓") : ""; - line = modelText + " " + providerBadge + checkmark; + line = `${modelText} ${providerBadge}${checkmark}`; } this.listContainer.addChild(new Text(line, 0, 0)); diff --git a/packages/coding-agent/src/modes/interactive/components/tool-execution.ts b/packages/coding-agent/src/modes/interactive/components/tool-execution.ts index 4de9433c..0191ccd7 100644 --- a/packages/coding-agent/src/modes/interactive/components/tool-execution.ts +++ b/packages/coding-agent/src/modes/interactive/components/tool-execution.ts @@ -21,7 +21,7 @@ import { renderDiff } from "./diff.js"; function shortenPath(path: string): string { const home = os.homedir(); if (path.startsWith(home)) { - return "~" + path.slice(home.length); + return `~${path.slice(home.length)}`; } return path; } @@ -269,7 +269,7 @@ export class ToolExecutionComponent extends Container { ); } } - text += "\n" + theme.fg("warning", `[${warnings.join(". ")}]`); + text += `\n${theme.fg("warning", `[${warnings.join(". ")}]`)}`; } } } else if (this.toolName === "read") { @@ -284,7 +284,7 @@ export class ToolExecutionComponent extends Container { pathDisplay += theme.fg("warning", `:${startLine}${endLine ? `-${endLine}` : ""}`); } - text = theme.fg("toolTitle", theme.bold("read")) + " " + pathDisplay; + text = `${theme.fg("toolTitle", theme.bold("read"))} ${pathDisplay}`; if (this.result) { const output = this.getTextOutput(); @@ -377,17 +377,17 @@ export class ToolExecutionComponent extends Container { if (this.result.isError) { const errorText = this.getTextOutput(); if (errorText) { - text += "\n\n" + theme.fg("error", errorText); + text += `\n\n${theme.fg("error", errorText)}`; } } else if (this.result.details?.diff) { - text += "\n\n" + renderDiff(this.result.details.diff, { filePath: rawPath }); + text += `\n\n${renderDiff(this.result.details.diff, { filePath: rawPath })}`; } } } else if (this.toolName === "ls") { const path = shortenPath(this.args?.path || "."); const limit = this.args?.limit; - text = theme.fg("toolTitle", theme.bold("ls")) + " " + theme.fg("accent", path); + text = `${theme.fg("toolTitle", theme.bold("ls"))} ${theme.fg("accent", path)}`; if (limit !== undefined) { text += theme.fg("toolOutput", ` (limit ${limit})`); } @@ -400,7 +400,7 @@ export class ToolExecutionComponent extends Container { const displayLines = lines.slice(0, maxLines); const remaining = lines.length - maxLines; - text += "\n\n" + displayLines.map((line: string) => theme.fg("toolOutput", line)).join("\n"); + text += `\n\n${displayLines.map((line: string) => theme.fg("toolOutput", line)).join("\n")}`; if (remaining > 0) { text += theme.fg("toolOutput", `\n... (${remaining} more lines)`); } @@ -416,7 +416,7 @@ export class ToolExecutionComponent extends Container { if (truncation?.truncated) { warnings.push(`${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit`); } - text += "\n" + theme.fg("warning", `[Truncated: ${warnings.join(", ")}]`); + text += `\n${theme.fg("warning", `[Truncated: ${warnings.join(", ")}]`)}`; } } } else if (this.toolName === "find") { @@ -441,7 +441,7 @@ export class ToolExecutionComponent extends Container { const displayLines = lines.slice(0, maxLines); const remaining = lines.length - maxLines; - text += "\n\n" + displayLines.map((line: string) => theme.fg("toolOutput", line)).join("\n"); + text += `\n\n${displayLines.map((line: string) => theme.fg("toolOutput", line)).join("\n")}`; if (remaining > 0) { text += theme.fg("toolOutput", `\n... (${remaining} more lines)`); } @@ -457,7 +457,7 @@ export class ToolExecutionComponent extends Container { if (truncation?.truncated) { warnings.push(`${formatSize(truncation.maxBytes ?? DEFAULT_MAX_BYTES)} limit`); } - text += "\n" + theme.fg("warning", `[Truncated: ${warnings.join(", ")}]`); + text += `\n${theme.fg("warning", `[Truncated: ${warnings.join(", ")}]`)}`; } } } else if (this.toolName === "grep") { @@ -486,7 +486,7 @@ export class ToolExecutionComponent extends Container { const displayLines = lines.slice(0, maxLines); const remaining = lines.length - maxLines; - text += "\n\n" + displayLines.map((line: string) => theme.fg("toolOutput", line)).join("\n"); + text += `\n\n${displayLines.map((line: string) => theme.fg("toolOutput", line)).join("\n")}`; if (remaining > 0) { text += theme.fg("toolOutput", `\n... (${remaining} more lines)`); } @@ -506,7 +506,7 @@ export class ToolExecutionComponent extends Container { if (linesTruncated) { warnings.push("some lines truncated"); } - text += "\n" + theme.fg("warning", `[Truncated: ${warnings.join(", ")}]`); + text += `\n${theme.fg("warning", `[Truncated: ${warnings.join(", ")}]`)}`; } } } else { @@ -514,10 +514,10 @@ export class ToolExecutionComponent extends Container { text = theme.fg("toolTitle", theme.bold(this.toolName)); const content = JSON.stringify(this.args, null, 2); - text += "\n\n" + content; + text += `\n\n${content}`; const output = this.getTextOutput(); if (output) { - text += "\n" + output; + text += `\n${output}`; } } diff --git a/packages/coding-agent/src/modes/interactive/interactive-mode.ts b/packages/coding-agent/src/modes/interactive/interactive-mode.ts index 8e2140a6..3ebf416e 100644 --- a/packages/coding-agent/src/modes/interactive/interactive-mode.ts +++ b/packages/coding-agent/src/modes/interactive/interactive-mode.ts @@ -239,7 +239,7 @@ export class InteractiveMode { "\n" + theme.fg("dim", "drop files") + theme.fg("muted", " to attach"); - const header = new Text(logo + "\n" + instructions, 1, 0); + const header = new Text(`${logo}\n${instructions}`, 1, 0); // Setup UI layout this.ui.addChild(new Spacer(1)); @@ -1340,7 +1340,7 @@ export class InteractiveMode { if (queuedMessages.length > 0) { this.pendingMessagesContainer.addChild(new Spacer(1)); for (const message of queuedMessages) { - const queuedText = theme.fg("dim", "Queued: " + message); + const queuedText = theme.fg("dim", `Queued: ${message}`); this.pendingMessagesContainer.addChild(new TruncatedText(queuedText, 1, 0)); } } @@ -1808,7 +1808,7 @@ export class InteractiveMode { this.chatContainer.addChild(new Spacer(1)); this.chatContainer.addChild( - new Text(theme.fg("accent", "✓ Context cleared") + "\n" + theme.fg("muted", "Started fresh session"), 1, 1), + new Text(`${theme.fg("accent", "✓ Context cleared")}\n${theme.fg("muted", "Started fresh session")}`, 1, 1), ); this.ui.requestRender(); } @@ -1840,7 +1840,7 @@ export class InteractiveMode { this.chatContainer.addChild(new Spacer(1)); this.chatContainer.addChild( - new Text(theme.fg("accent", "✓ Debug log written") + "\n" + theme.fg("muted", debugLogPath), 1, 1), + new Text(`${theme.fg("accent", "✓ Debug log written")}\n${theme.fg("muted", debugLogPath)}`, 1, 1), ); this.ui.requestRender(); } diff --git a/packages/coding-agent/src/modes/interactive/theme/theme.ts b/packages/coding-agent/src/modes/interactive/theme/theme.ts index 885f2c01..3a6705ce 100644 --- a/packages/coding-agent/src/modes/interactive/theme/theme.ts +++ b/packages/coding-agent/src/modes/interactive/theme/theme.ts @@ -534,7 +534,7 @@ export function initTheme(themeName?: string, enableWatcher: boolean = false): v if (enableWatcher) { startThemeWatcher(); } - } catch (error) { + } catch (_error) { // Theme is invalid - fall back to dark theme silently currentThemeName = "dark"; theme = loadTheme("dark"); @@ -598,7 +598,7 @@ function startThemeWatcher(): void { if (onThemeChangeCallback) { onThemeChangeCallback(); } - } catch (error) { + } catch (_error) { // Ignore errors (file might be in invalid state while being edited) } }, 100); @@ -619,7 +619,7 @@ function startThemeWatcher(): void { }, 100); } }); - } catch (error) { + } catch (_error) { // Ignore errors starting watcher } } diff --git a/packages/coding-agent/src/modes/rpc/rpc-client.ts b/packages/coding-agent/src/modes/rpc/rpc-client.ts index 99b95f75..ff744640 100644 --- a/packages/coding-agent/src/modes/rpc/rpc-client.ts +++ b/packages/coding-agent/src/modes/rpc/rpc-client.ts @@ -456,7 +456,7 @@ export class RpcClient { }, }); - this.process!.stdin!.write(JSON.stringify(fullCommand) + "\n"); + this.process!.stdin!.write(`${JSON.stringify(fullCommand)}\n`); }); } diff --git a/packages/coding-agent/src/utils/shell.ts b/packages/coding-agent/src/utils/shell.ts index 85de9c12..bde51928 100644 --- a/packages/coding-agent/src/utils/shell.ts +++ b/packages/coding-agent/src/utils/shell.ts @@ -44,7 +44,7 @@ export function getShellConfig(): { shell: string; args: string[] } { return cachedShellConfig; } throw new Error( - `Custom shell path not found: ${customShellPath}\n` + `Please update shellPath in ~/.pi/agent/settings.json`, + `Custom shell path not found: ${customShellPath}\nPlease update shellPath in ~/.pi/agent/settings.json`, ); } diff --git a/packages/coding-agent/test/skills.test.ts b/packages/coding-agent/test/skills.test.ts index 94859eb6..863d16b0 100644 --- a/packages/coding-agent/test/skills.test.ts +++ b/packages/coding-agent/test/skills.test.ts @@ -106,7 +106,7 @@ describe("skills", () => { }); it("should load all skills from fixture directory", () => { - const { skills, warnings } = loadSkillsFromDir({ + const { skills } = loadSkillsFromDir({ dir: fixturesDir, source: "test", }); diff --git a/packages/mom/src/agent.ts b/packages/mom/src/agent.ts index 9959085b..87152924 100644 --- a/packages/mom/src/agent.ts +++ b/packages/mom/src/agent.ts @@ -20,26 +20,6 @@ import { createMomTools, setUploadFunction } from "./tools/index.js"; // Hardcoded model for now - TODO: make configurable (issue #63) const model = getModel("anthropic", "claude-sonnet-4-5"); -/** - * Convert Date.now() to Slack timestamp format (seconds.microseconds) - * Uses a monotonic counter to ensure ordering even within the same millisecond - */ -let lastTsMs = 0; -let tsCounter = 0; - -function toSlackTs(): string { - const now = Date.now(); - if (now === lastTsMs) { - tsCounter++; - } else { - lastTsMs = now; - tsCounter = 0; - } - const seconds = Math.floor(now / 1000); - const micros = (now % 1000) * 1000 + tsCounter; - return `${seconds}.${micros.toString().padStart(6, "0")}`; -} - export interface PendingMessage { userName: string; text: string; @@ -85,7 +65,7 @@ function getMemory(channelDir: string): string { try { const content = readFileSync(workspaceMemoryPath, "utf-8").trim(); if (content) { - parts.push("### Global Workspace Memory\n" + content); + parts.push(`### Global Workspace Memory\n${content}`); } } catch (error) { log.logWarning("Failed to read workspace memory", `${workspaceMemoryPath}: ${error}`); @@ -98,7 +78,7 @@ function getMemory(channelDir: string): string { try { const content = readFileSync(channelMemoryPath, "utf-8").trim(); if (content) { - parts.push("### Channel-Specific Memory\n" + content); + parts.push(`### Channel-Specific Memory\n${content}`); } } catch (error) { log.logWarning("Failed to read channel memory", `${channelMemoryPath}: ${error}`); @@ -340,7 +320,7 @@ Each tool requires a "label" parameter (shown to user). function truncate(text: string, maxLen: number): string { if (text.length <= maxLen) return text; - return text.substring(0, maxLen - 3) + "..."; + return `${text.substring(0, maxLen - 3)}...`; } function extractToolResultText(result: unknown): string { @@ -530,8 +510,8 @@ function createRunner(sandboxConfig: SandboxConfig, channelId: string, channelDi let threadMessage = `*${agentEvent.isError ? "✗" : "✓"} ${agentEvent.toolName}*`; if (label) threadMessage += `: ${label}`; threadMessage += ` (${duration}s)\n`; - if (argsFormatted) threadMessage += "```\n" + argsFormatted + "\n```\n"; - threadMessage += "*Result:*\n```\n" + resultStr + "\n```"; + if (argsFormatted) threadMessage += `\`\`\`\n${argsFormatted}\n\`\`\`\n`; + threadMessage += `*Result:*\n\`\`\`\n${resultStr}\n\`\`\``; queue.enqueueMessage(threadMessage, "thread", "tool result thread", false); @@ -804,7 +784,7 @@ function createRunner(sandboxConfig: SandboxConfig, channelId: string, channelDi try { const mainText = finalText.length > SLACK_MAX_LENGTH - ? finalText.substring(0, SLACK_MAX_LENGTH - 50) + "\n\n_(see thread for full response)_" + ? `${finalText.substring(0, SLACK_MAX_LENGTH - 50)}\n\n_(see thread for full response)_` : finalText; await ctx.replaceMessage(mainText); } catch (err) { diff --git a/packages/mom/src/context.ts b/packages/mom/src/context.ts index acab5461..ee93298a 100644 --- a/packages/mom/src/context.ts +++ b/packages/mom/src/context.ts @@ -92,7 +92,7 @@ export class MomSessionManager { }; this.inMemoryEntries.push(entry); - appendFileSync(this.contextFile, JSON.stringify(entry) + "\n"); + appendFileSync(this.contextFile, `${JSON.stringify(entry)}\n`); } /** @@ -215,7 +215,7 @@ export class MomSessionManager { }; this.inMemoryEntries.push(entry); - appendFileSync(this.contextFile, JSON.stringify(entry) + "\n"); + appendFileSync(this.contextFile, `${JSON.stringify(entry)}\n`); } } @@ -270,9 +270,9 @@ export class MomSessionManager { this.pendingEntries = []; // Write to file - appendFileSync(this.contextFile, JSON.stringify(entry) + "\n"); + appendFileSync(this.contextFile, `${JSON.stringify(entry)}\n`); for (const memEntry of this.inMemoryEntries.slice(1)) { - appendFileSync(this.contextFile, JSON.stringify(memEntry) + "\n"); + appendFileSync(this.contextFile, `${JSON.stringify(memEntry)}\n`); } } @@ -287,7 +287,7 @@ export class MomSessionManager { this.pendingEntries.push(entry); } else { this.inMemoryEntries.push(entry); - appendFileSync(this.contextFile, JSON.stringify(entry) + "\n"); + appendFileSync(this.contextFile, `${JSON.stringify(entry)}\n`); } } @@ -302,7 +302,7 @@ export class MomSessionManager { this.pendingEntries.push(entry); } else { this.inMemoryEntries.push(entry); - appendFileSync(this.contextFile, JSON.stringify(entry) + "\n"); + appendFileSync(this.contextFile, `${JSON.stringify(entry)}\n`); } } @@ -318,13 +318,13 @@ export class MomSessionManager { this.pendingEntries.push(entry); } else { this.inMemoryEntries.push(entry); - appendFileSync(this.contextFile, JSON.stringify(entry) + "\n"); + appendFileSync(this.contextFile, `${JSON.stringify(entry)}\n`); } } saveCompaction(entry: CompactionEntry): void { this.inMemoryEntries.push(entry); - appendFileSync(this.contextFile, JSON.stringify(entry) + "\n"); + appendFileSync(this.contextFile, `${JSON.stringify(entry)}\n`); } /** Load session with compaction support */ @@ -587,7 +587,6 @@ export function syncLogToContext(channelDir: string, excludeAfterTs?: string): n if (logMessages.length === 0) return 0; // Read existing timestamps from context.jsonl - const existingTs = new Set(); if (existsSync(contextFile)) { const contextContent = readFileSync(contextFile, "utf-8"); const contextLines = contextContent.trim().split("\n").filter(Boolean); @@ -656,7 +655,7 @@ export function syncLogToContext(channelDir: string, excludeAfterTs?: string): n mkdirSync(channelDir, { recursive: true }); } - appendFileSync(contextFile, JSON.stringify(entry) + "\n"); + appendFileSync(contextFile, `${JSON.stringify(entry)}\n`); existingMessages.add(content); // Track to avoid duplicates within this sync syncedCount++; } diff --git a/packages/mom/src/log.ts b/packages/mom/src/log.ts index f8862e36..9da283e4 100644 --- a/packages/mom/src/log.ts +++ b/packages/mom/src/log.ts @@ -27,7 +27,7 @@ function formatContext(ctx: LogContext): string { function truncate(text: string, maxLen: number): string { if (text.length <= maxLen) return text; - return text.substring(0, maxLen) + `\n(truncated at ${maxLen} chars)`; + return `${text.substring(0, maxLen)}\n(truncated at ${maxLen} chars)`; } function formatToolArgs(args: Record): string { @@ -200,9 +200,9 @@ export function logUsageSummary( ): string { const formatTokens = (count: number): string => { if (count < 1000) return count.toString(); - if (count < 10000) return (count / 1000).toFixed(1) + "k"; - if (count < 1000000) return Math.round(count / 1000) + "k"; - return (count / 1000000).toFixed(1) + "M"; + if (count < 10000) return `${(count / 1000).toFixed(1)}k`; + if (count < 1000000) return `${Math.round(count / 1000)}k`; + return `${(count / 1000000).toFixed(1)}M`; }; const lines: string[] = []; diff --git a/packages/mom/src/main.ts b/packages/mom/src/main.ts index c1c483e3..1e97867c 100644 --- a/packages/mom/src/main.ts +++ b/packages/mom/src/main.ts @@ -144,7 +144,7 @@ function createSlackContext(event: SlackEvent, slack: SlackBot, state: ChannelSt respond: async (text: string, shouldLog = true) => { updatePromise = updatePromise.then(async () => { - accumulatedText = accumulatedText ? accumulatedText + "\n" + text : text; + accumulatedText = accumulatedText ? `${accumulatedText}\n${text}` : text; const displayText = isWorking ? accumulatedText + workingIndicator : accumulatedText; if (messageTs) { diff --git a/packages/mom/src/slack.ts b/packages/mom/src/slack.ts index 2f9d72f0..2662387f 100644 --- a/packages/mom/src/slack.ts +++ b/packages/mom/src/slack.ts @@ -220,7 +220,7 @@ export class SlackBot { logToFile(channel: string, entry: object): void { const dir = join(this.workingDir, channel); if (!existsSync(dir)) mkdirSync(dir, { recursive: true }); - appendFileSync(join(dir, "log.jsonl"), JSON.stringify(entry) + "\n"); + appendFileSync(join(dir, "log.jsonl"), `${JSON.stringify(entry)}\n`); } /** diff --git a/packages/mom/src/store.ts b/packages/mom/src/store.ts index 59a5a519..d0ada3fe 100644 --- a/packages/mom/src/store.ts +++ b/packages/mom/src/store.ts @@ -139,7 +139,7 @@ export class ChannelStore { message.date = date.toISOString(); } - const line = JSON.stringify(message) + "\n"; + const line = `${JSON.stringify(message)}\n`; await appendFile(logPath, line, "utf-8"); return true; } diff --git a/packages/pods/src/commands/models.ts b/packages/pods/src/commands/models.ts index eb6b60a2..4a118ecc 100644 --- a/packages/pods/src/commands/models.ts +++ b/packages/pods/src/commands/models.ts @@ -207,7 +207,7 @@ export const startModel = async ( .replace("{{VLLM_ARGS}}", vllmArgs.join(" ")); // Upload customized script - const result = await sshExec( + await sshExec( pod.ssh, `cat > /tmp/model_run_${name}.sh << 'EOF' ${scriptContent} @@ -341,7 +341,7 @@ WRAPPER if (startupFailed) { // Model failed to start - clean up and report error - console.log("\n" + chalk.red(`✗ Model failed to start: ${failureReason}`)); + console.log(`\n${chalk.red(`✗ Model failed to start: ${failureReason}`)}`); // Remove the failed model from config const config = loadConfig(); @@ -352,7 +352,7 @@ WRAPPER // Provide helpful suggestions based on failure reason if (failureReason.includes("OOM") || failureReason.includes("memory")) { - console.log("\n" + chalk.bold("Suggestions:")); + console.log(`\n${chalk.bold("Suggestions:")}`); console.log(" • Try reducing GPU memory utilization: --memory 50%"); console.log(" • Use a smaller context window: --context 4k"); console.log(" • Use a quantized version of the model (e.g., FP8)"); @@ -360,24 +360,24 @@ WRAPPER console.log(" • Try a smaller model variant"); } - console.log("\n" + chalk.cyan('Check full logs: pi ssh "tail -100 ~/.vllm_logs/' + name + '.log"')); + console.log(`\n${chalk.cyan(`Check full logs: pi ssh "tail -100 ~/.vllm_logs/${name}.log"`)}`); process.exit(1); } else if (startupComplete) { // Model started successfully - output connection details - console.log("\n" + chalk.green("✓ Model started successfully!")); - console.log("\n" + chalk.bold("Connection Details:")); + console.log(`\n${chalk.green("✓ Model started successfully!")}`); + console.log(`\n${chalk.bold("Connection Details:")}`); console.log(chalk.cyan("─".repeat(50))); console.log(chalk.white("Base URL: ") + chalk.yellow(`http://${host}:${port}/v1`)); console.log(chalk.white("Model: ") + chalk.yellow(modelId)); console.log(chalk.white("API Key: ") + chalk.yellow(process.env.PI_API_KEY || "(not set)")); console.log(chalk.cyan("─".repeat(50))); - console.log("\n" + chalk.bold("Export for shell:")); + console.log(`\n${chalk.bold("Export for shell:")}`); console.log(chalk.gray(`export OPENAI_BASE_URL="http://${host}:${port}/v1"`)); console.log(chalk.gray(`export OPENAI_API_KEY="${process.env.PI_API_KEY || "your-api-key"}"`)); console.log(chalk.gray(`export OPENAI_MODEL="${modelId}"`)); - console.log("\n" + chalk.bold("Example usage:")); + console.log(`\n${chalk.bold("Example usage:")}`); console.log( chalk.gray(` # Python diff --git a/packages/tui/src/autocomplete.ts b/packages/tui/src/autocomplete.ts index 9dd6988c..d0eadace 100644 --- a/packages/tui/src/autocomplete.ts +++ b/packages/tui/src/autocomplete.ts @@ -202,7 +202,7 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider { const isSlashCommand = prefix.startsWith("/") && beforePrefix.trim() === "" && !prefix.slice(1).includes("/"); if (isSlashCommand) { // This is a command name completion - const newLine = beforePrefix + "/" + item.value + " " + afterCursor; + const newLine = `${beforePrefix}/${item.value} ${afterCursor}`; const newLines = [...lines]; newLines[cursorLine] = newLine; @@ -216,7 +216,7 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider { // Check if we're completing a file attachment (prefix starts with "@") if (prefix.startsWith("@")) { // This is a file attachment completion - const newLine = beforePrefix + item.value + " " + afterCursor; + const newLine = `${beforePrefix + item.value} ${afterCursor}`; const newLines = [...lines]; newLines[cursorLine] = newLine; @@ -299,7 +299,7 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider { if (path.startsWith("~/")) { const expandedPath = join(homedir(), path.slice(2)); // Preserve trailing slash if original path had one - return path.endsWith("/") && !expandedPath.endsWith("/") ? expandedPath + "/" : expandedPath; + return path.endsWith("/") && !expandedPath.endsWith("/") ? `${expandedPath}/` : expandedPath; } else if (path === "~") { return homedir(); } @@ -387,20 +387,20 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider { if (isAtPrefix) { const pathWithoutAt = expandedPrefix; if (pathWithoutAt.endsWith("/")) { - relativePath = "@" + pathWithoutAt + name; + relativePath = `@${pathWithoutAt}${name}`; } else if (pathWithoutAt.includes("/")) { if (pathWithoutAt.startsWith("~/")) { const homeRelativeDir = pathWithoutAt.slice(2); // Remove ~/ const dir = dirname(homeRelativeDir); - relativePath = "@~/" + (dir === "." ? name : join(dir, name)); + relativePath = `@~/${dir === "." ? name : join(dir, name)}`; } else { - relativePath = "@" + join(dirname(pathWithoutAt), name); + relativePath = `@${join(dirname(pathWithoutAt), name)}`; } } else { if (pathWithoutAt.startsWith("~")) { - relativePath = "@~/" + name; + relativePath = `@~/${name}`; } else { - relativePath = "@" + name; + relativePath = `@${name}`; } } } else if (prefix.endsWith("/")) { @@ -411,14 +411,14 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider { if (prefix.startsWith("~/")) { const homeRelativeDir = prefix.slice(2); // Remove ~/ const dir = dirname(homeRelativeDir); - relativePath = "~/" + (dir === "." ? name : join(dir, name)); + relativePath = `~/${dir === "." ? name : join(dir, name)}`; } else if (prefix.startsWith("/")) { // Absolute path - construct properly const dir = dirname(prefix); if (dir === "/") { - relativePath = "/" + name; + relativePath = `/${name}`; } else { - relativePath = dir + "/" + name; + relativePath = `${dir}/${name}`; } } else { relativePath = join(dirname(prefix), name); @@ -426,14 +426,14 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider { } else { // For standalone entries, preserve ~/ if original prefix was ~/ if (prefix.startsWith("~")) { - relativePath = "~/" + name; + relativePath = `~/${name}`; } else { relativePath = name; } } suggestions.push({ - value: isDirectory ? relativePath + "/" : relativePath, + value: isDirectory ? `${relativePath}/` : relativePath, label: name, description: isDirectory ? "directory" : "file", }); @@ -449,7 +449,7 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider { }); return suggestions; - } catch (e) { + } catch (_e) { // Directory doesn't exist or not accessible return []; } @@ -509,7 +509,7 @@ export class CombinedAutocompleteProvider implements AutocompleteProvider { const entryName = basename(pathWithoutSlash); suggestions.push({ - value: "@" + entryPath, + value: `@${entryPath}`, label: entryName + (isDirectory ? "/" : ""), description: pathWithoutSlash, }); diff --git a/packages/tui/src/components/editor.ts b/packages/tui/src/components/editor.ts index ae4b5118..e7f46c47 100644 --- a/packages/tui/src/components/editor.ts +++ b/packages/tui/src/components/editor.ts @@ -1310,10 +1310,6 @@ https://github.com/EsotericSoftware/spine-runtimes/actions/runs/19536643416/job/ // Always create new SelectList to ensure update this.autocompleteList = new SelectList(suggestions.items, 5, this.theme.selectList); } else { - // No matches - check if we're still in a valid context before cancelling - const currentLine = this.state.lines[this.state.cursorLine] || ""; - const textBeforeCursor = currentLine.slice(0, this.state.cursorCol); - this.cancelAutocomplete(); } } diff --git a/packages/tui/src/components/markdown.ts b/packages/tui/src/components/markdown.ts index 6e0cdc56..c43f8f2f 100644 --- a/packages/tui/src/components/markdown.ts +++ b/packages/tui/src/components/markdown.ts @@ -235,7 +235,7 @@ export class Markdown implements Component { switch (token.type) { case "heading": { const headingLevel = token.depth; - const headingPrefix = "#".repeat(headingLevel) + " "; + const headingPrefix = `${"#".repeat(headingLevel)} `; const headingText = this.renderInlineTokens(token.tokens || []); let styledHeading: string; if (headingLevel === 1) { @@ -263,17 +263,17 @@ export class Markdown implements Component { } case "code": { - lines.push(this.theme.codeBlockBorder("```" + (token.lang || ""))); + lines.push(this.theme.codeBlockBorder(`\`\`\`${token.lang || ""}`)); if (this.theme.highlightCode) { const highlightedLines = this.theme.highlightCode(token.text, token.lang); for (const hlLine of highlightedLines) { - lines.push(" " + hlLine); + lines.push(` ${hlLine}`); } } else { // Split code by newlines and style each line const codeLines = token.text.split("\n"); for (const codeLine of codeLines) { - lines.push(" " + this.theme.codeBlock(codeLine)); + lines.push(` ${this.theme.codeBlock(codeLine)}`); } } lines.push(this.theme.codeBlockBorder("```")); @@ -443,7 +443,7 @@ export class Markdown implements Component { lines.push(line); } else { // Regular content - add parent indent + 2 spaces for continuation - lines.push(indent + " " + line); + lines.push(`${indent} ${line}`); } } } else { @@ -478,16 +478,16 @@ export class Markdown implements Component { lines.push(text); } else if (token.type === "code") { // Code block in list item - lines.push(this.theme.codeBlockBorder("```" + (token.lang || ""))); + lines.push(this.theme.codeBlockBorder(`\`\`\`${token.lang || ""}`)); if (this.theme.highlightCode) { const highlightedLines = this.theme.highlightCode(token.text, token.lang); for (const hlLine of highlightedLines) { - lines.push(" " + hlLine); + lines.push(` ${hlLine}`); } } else { const codeLines = token.text.split("\n"); for (const codeLine of codeLines) { - lines.push(" " + this.theme.codeBlock(codeLine)); + lines.push(` ${this.theme.codeBlock(codeLine)}`); } } lines.push(this.theme.codeBlockBorder("```")); @@ -587,7 +587,7 @@ export class Markdown implements Component { // Render top border const topBorderCells = columnWidths.map((w) => "─".repeat(w)); - lines.push("┌─" + topBorderCells.join("─┬─") + "─┐"); + lines.push(`┌─${topBorderCells.join("─┬─")}─┐`); // Render header with wrapping const headerCellLines: string[][] = token.header.map((cell, i) => { @@ -602,12 +602,12 @@ export class Markdown implements Component { const padded = text + " ".repeat(Math.max(0, columnWidths[colIdx] - visibleWidth(text))); return this.theme.bold(padded); }); - lines.push("│ " + rowParts.join(" │ ") + " │"); + lines.push(`│ ${rowParts.join(" │ ")} │`); } // Render separator const separatorCells = columnWidths.map((w) => "─".repeat(w)); - lines.push("├─" + separatorCells.join("─┼─") + "─┤"); + lines.push(`├─${separatorCells.join("─┼─")}─┤`); // Render rows with wrapping for (const row of token.rows) { @@ -622,13 +622,13 @@ export class Markdown implements Component { const text = cellLines[lineIdx] || ""; return text + " ".repeat(Math.max(0, columnWidths[colIdx] - visibleWidth(text))); }); - lines.push("│ " + rowParts.join(" │ ") + " │"); + lines.push(`│ ${rowParts.join(" │ ")} │`); } } // Render bottom border const bottomBorderCells = columnWidths.map((w) => "─".repeat(w)); - lines.push("└─" + bottomBorderCells.join("─┴─") + "─┘"); + lines.push(`└─${bottomBorderCells.join("─┴─")}─┘`); lines.push(""); // Add spacing after table return lines; diff --git a/packages/tui/src/components/select-list.ts b/packages/tui/src/components/select-list.ts index 21432bbe..2342c7c4 100644 --- a/packages/tui/src/components/select-list.ts +++ b/packages/tui/src/components/select-list.ts @@ -90,16 +90,16 @@ export class SelectList implements Component { if (remainingWidth > 10) { const truncatedDesc = truncateToWidth(item.description, remainingWidth, ""); // Apply selectedText to entire line content - line = this.theme.selectedText("→ " + truncatedValue + spacing + truncatedDesc); + line = this.theme.selectedText(`→ ${truncatedValue}${spacing}${truncatedDesc}`); } else { // Not enough space for description const maxWidth = width - prefixWidth - 2; - line = this.theme.selectedText("→ " + truncateToWidth(displayValue, maxWidth, "")); + line = this.theme.selectedText(`→ ${truncateToWidth(displayValue, maxWidth, "")}`); } } else { // No description or not enough width const maxWidth = width - prefixWidth - 2; - line = this.theme.selectedText("→ " + truncateToWidth(displayValue, maxWidth, "")); + line = this.theme.selectedText(`→ ${truncateToWidth(displayValue, maxWidth, "")}`); } } else { const displayValue = item.label || item.value; diff --git a/packages/tui/src/utils.ts b/packages/tui/src/utils.ts index 0603beaa..4614605a 100644 --- a/packages/tui/src/utils.ts +++ b/packages/tui/src/utils.ts @@ -565,5 +565,5 @@ export function truncateToWidth(text: string, maxWidth: number, ellipsis: string } // Add reset code before ellipsis to prevent styling leaking into it - return result + "\x1b[0m" + ellipsis; + return `${result}\x1b[0m${ellipsis}`; } diff --git a/packages/tui/test/image-test.ts b/packages/tui/test/image-test.ts index f24d24c0..6a29d16d 100644 --- a/packages/tui/test/image-test.ts +++ b/packages/tui/test/image-test.ts @@ -14,7 +14,7 @@ console.log("Loading image from:", testImagePath); let imageBuffer: Buffer; try { imageBuffer = readFileSync(testImagePath); -} catch (e) { +} catch (_e) { console.error(`Failed to load image: ${testImagePath}`); console.error("Usage: npx tsx test/image-test.ts [path-to-image.png]"); process.exit(1); diff --git a/packages/tui/test/truncated-text.test.ts b/packages/tui/test/truncated-text.test.ts index a3772a91..ac139179 100644 --- a/packages/tui/test/truncated-text.test.ts +++ b/packages/tui/test/truncated-text.test.ts @@ -49,7 +49,7 @@ describe("TruncatedText component", () => { }); it("preserves ANSI codes in output and pads correctly", () => { - const styledText = chalk.red("Hello") + " " + chalk.blue("world"); + const styledText = `${chalk.red("Hello")} ${chalk.blue("world")}`; const text = new TruncatedText(styledText, 1, 0); const lines = text.render(40); diff --git a/packages/web-ui/src/agent/agent.ts b/packages/web-ui/src/agent/agent.ts index 893354ca..f751e2e1 100644 --- a/packages/web-ui/src/agent/agent.ts +++ b/packages/web-ui/src/agent/agent.ts @@ -23,6 +23,8 @@ function defaultMessageTransformer(messages: AppMessage[]): Message[] { .map((m) => { if (m.role === "user") { // Strip attachments field (app-specific) + + // biome-ignore lint/correctness/noUnusedVariables: fine here const { attachments, ...rest } = m as any; return rest as Message; } diff --git a/packages/web-ui/src/components/AttachmentTile.ts b/packages/web-ui/src/components/AttachmentTile.ts index 800acc4a..823001d3 100644 --- a/packages/web-ui/src/components/AttachmentTile.ts +++ b/packages/web-ui/src/components/AttachmentTile.ts @@ -31,12 +31,6 @@ export class AttachmentTile extends LitElement { const hasPreview = !!this.attachment.preview; const isImage = this.attachment.type === "image"; const isPdf = this.attachment.mimeType === "application/pdf"; - const isDocx = - this.attachment.mimeType?.includes("wordprocessingml") || - this.attachment.fileName.toLowerCase().endsWith(".docx"); - const isPptx = - this.attachment.mimeType?.includes("presentationml") || - this.attachment.fileName.toLowerCase().endsWith(".pptx"); const isExcel = this.attachment.mimeType?.includes("spreadsheetml") || this.attachment.fileName.toLowerCase().endsWith(".xlsx") || @@ -84,7 +78,7 @@ export class AttachmentTile extends LitElement {
${ this.attachment.fileName.length > 10 - ? this.attachment.fileName.substring(0, 8) + "..." + ? `${this.attachment.fileName.substring(0, 8)}...` : this.attachment.fileName }
diff --git a/packages/web-ui/src/components/sandbox/ArtifactsRuntimeProvider.ts b/packages/web-ui/src/components/sandbox/ArtifactsRuntimeProvider.ts index ad59dfa1..eeda4f3c 100644 --- a/packages/web-ui/src/components/sandbox/ArtifactsRuntimeProvider.ts +++ b/packages/web-ui/src/components/sandbox/ArtifactsRuntimeProvider.ts @@ -140,7 +140,7 @@ export class ArtifactsRuntimeProvider implements SandboxRuntimeProvider { return; } - const { action, filename, content, mimeType } = message; + const { action, filename, content } = message; try { switch (action) { diff --git a/packages/web-ui/src/components/sandbox/AttachmentsRuntimeProvider.ts b/packages/web-ui/src/components/sandbox/AttachmentsRuntimeProvider.ts index 5009cfd3..9c8b3d7d 100644 --- a/packages/web-ui/src/components/sandbox/AttachmentsRuntimeProvider.ts +++ b/packages/web-ui/src/components/sandbox/AttachmentsRuntimeProvider.ts @@ -40,18 +40,18 @@ export class AttachmentsRuntimeProvider implements SandboxRuntimeProvider { (window as any).readTextAttachment = (attachmentId: string) => { const a = ((window as any).attachments || []).find((x: any) => x.id === attachmentId); - if (!a) throw new Error("Attachment not found: " + attachmentId); + if (!a) throw new Error(`Attachment not found: ${attachmentId}`); if (a.extractedText) return a.extractedText; try { return atob(a.content); } catch { - throw new Error("Failed to decode text content for: " + attachmentId); + throw new Error(`Failed to decode text content for: ${attachmentId}`); } }; (window as any).readBinaryAttachment = (attachmentId: string) => { const a = ((window as any).attachments || []).find((x: any) => x.id === attachmentId); - if (!a) throw new Error("Attachment not found: " + attachmentId); + if (!a) throw new Error(`Attachment not found: ${attachmentId}`); const bin = atob(a.content); const bytes = new Uint8Array(bin.length); for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i); diff --git a/packages/web-ui/src/components/sandbox/ConsoleRuntimeProvider.ts b/packages/web-ui/src/components/sandbox/ConsoleRuntimeProvider.ts index 389ce1e1..326361c7 100644 --- a/packages/web-ui/src/components/sandbox/ConsoleRuntimeProvider.ts +++ b/packages/web-ui/src/components/sandbox/ConsoleRuntimeProvider.ts @@ -92,8 +92,7 @@ export class ConsoleRuntimeProvider implements SandboxRuntimeProvider { // Error handlers - track errors but don't log them // (they'll be shown via execution-error message) window.addEventListener("error", (e) => { - const text = - (e.error?.stack || e.message || String(e)) + " at line " + (e.lineno || "?") + ":" + (e.colno || "?"); + const text = `${e.error?.stack || e.message || String(e)} at line ${e.lineno || "?"}:${e.colno || "?"}`; lastError = { message: e.error?.message || e.message || String(e), @@ -102,7 +101,7 @@ export class ConsoleRuntimeProvider implements SandboxRuntimeProvider { }); window.addEventListener("unhandledrejection", (e) => { - const text = "Unhandled promise rejection: " + (e.reason?.message || e.reason || "Unknown error"); + const text = `Unhandled promise rejection: ${e.reason?.message || e.reason || "Unknown error"}`; lastError = { message: e.reason?.message || String(e.reason) || "Unhandled promise rejection", diff --git a/packages/web-ui/src/dialogs/ProvidersModelsTab.ts b/packages/web-ui/src/dialogs/ProvidersModelsTab.ts index eb7a21cd..e2c21a02 100644 --- a/packages/web-ui/src/dialogs/ProvidersModelsTab.ts +++ b/packages/web-ui/src/dialogs/ProvidersModelsTab.ts @@ -65,7 +65,7 @@ export class ProvidersModelsTab extends SettingsTab { ); this.providerStatus.set(provider.id, { modelCount: models.length, status: "connected" }); - } catch (error) { + } catch (_error) { this.providerStatus.set(provider.id, { modelCount: 0, status: "disconnected" }); } this.requestUpdate(); diff --git a/packages/web-ui/src/storage/backends/indexeddb-storage-backend.ts b/packages/web-ui/src/storage/backends/indexeddb-storage-backend.ts index 1b51946f..619c41d9 100644 --- a/packages/web-ui/src/storage/backends/indexeddb-storage-backend.ts +++ b/packages/web-ui/src/storage/backends/indexeddb-storage-backend.ts @@ -87,7 +87,7 @@ export class IndexedDBStorageBackend implements StorageBackend { if (prefix) { // Use IDBKeyRange for efficient prefix filtering - const range = IDBKeyRange.bound(prefix, prefix + "\uffff", false, false); + const range = IDBKeyRange.bound(prefix, `${prefix}\uffff`, false, false); const keys = await this.promisifyRequest(store.getAllKeys(range)); return keys.map((k) => String(k)); } else { diff --git a/packages/web-ui/src/tools/extract-document.ts b/packages/web-ui/src/tools/extract-document.ts index f277413e..73eddcb4 100644 --- a/packages/web-ui/src/tools/extract-document.ts +++ b/packages/web-ui/src/tools/extract-document.ts @@ -137,7 +137,7 @@ export function createExtractDocumentTool(): AgentTool 0) { for (const entry of result.console) { - output += entry.text + "\n"; + output += `${entry.text}\n`; } } @@ -230,7 +230,7 @@ export const javascriptReplRenderer: ToolRenderer 0) { - text = "\n[Table]\n" + tableTexts.join("\n") + "\n[/Table]\n"; + text = `\n[Table]\n${tableTexts.join("\n")}\n[/Table]\n`; } } } @@ -397,7 +397,7 @@ async function processPptx(arrayBuffer: ArrayBuffer, fileName: string): Promise< .filter((t) => t.trim()); if (slideTexts.length > 0) { - extractedText += "\n" + slideTexts.join("\n"); + extractedText += `\n${slideTexts.join("\n")}`; } extractedText += "\n"; } diff --git a/packages/web-ui/src/utils/format.ts b/packages/web-ui/src/utils/format.ts index 76e24aa5..aaaa9387 100644 --- a/packages/web-ui/src/utils/format.ts +++ b/packages/web-ui/src/utils/format.ts @@ -37,6 +37,6 @@ export function formatUsage(usage: Usage) { export function formatTokenCount(count: number): string { if (count < 1000) return count.toString(); - if (count < 10000) return (count / 1000).toFixed(1) + "k"; - return Math.round(count / 1000) + "k"; + if (count < 10000) return `${(count / 1000).toFixed(1)}k`; + return `${Math.round(count / 1000)}k`; }