From 6090508bad9cc64f95a1d1b925774c404b96efb3 Mon Sep 17 00:00:00 2001 From: William Valentin Date: Wed, 11 Feb 2026 10:30:24 -0800 Subject: [PATCH] style: auto-fix ESLint issues (curly braces and formatting) - Add curly braces to all if/else/for/while statements - Fix indentation and trailing spaces - Auto-fixed 372 linting errors using eslint --fix - Remaining issues are warnings only (non-null assertions, explicit any types) --- src/auth/github.ts | 2 +- src/automation/cron.ts | 4 +- src/automation/gmail.test.ts | 4 +- src/automation/gmail.ts | 14 ++-- src/automation/heartbeat.ts | 4 +- src/automation/webhooks.test.ts | 2 +- src/automation/webhooks.ts | 4 +- src/backends/native/orchestrator.test.ts | 2 +- src/backends/native/orchestrator.ts | 6 +- src/channels/discord/adapter.test.ts | 4 +- src/channels/discord/adapter.ts | 6 +- src/channels/pairing.ts | 2 +- src/channels/registry.ts | 2 +- src/channels/slack/adapter.test.ts | 2 +- src/channels/slack/adapter.ts | 22 ++--- src/channels/telegram/adapter.ts | 30 +++---- src/channels/whatsapp/adapter.ts | 10 +-- src/cli/completion.ts | 18 ++--- src/cli/doctor.ts | 6 +- src/cli/send.ts | 2 +- src/cli/sessions.test.ts | 2 +- src/cli/setup/automation.ts | 6 +- src/cli/setup/channels.ts | 4 +- src/cli/setup/config.ts | 12 +-- src/cli/setup/memory.ts | 4 +- src/cli/setup/orchestrator.ts | 2 +- src/cli/setup/prompts.ts | 6 +- src/cli/setup/providers.ts | 6 +- src/cli/setup/summary.ts | 36 ++++----- src/cli/shared.ts | 6 +- src/cli/tui.ts | 4 +- src/daemon/lifecycle.ts | 2 +- src/daemon/memory.ts | 2 +- src/daemon/models.ts | 10 +-- src/daemon/services.ts | 2 +- src/frontends/telegram/bot.ts | 2 +- src/frontends/tui/commands.ts | 26 +++--- src/frontends/tui/components/App.tsx | 12 +-- src/frontends/tui/components/InputBar.tsx | 12 +-- src/frontends/tui/components/MessageList.tsx | 24 +++--- src/frontends/tui/fullscreen.ts | 2 +- src/frontends/tui/markdown.ts | 14 ++-- src/frontends/tui/minimal.ts | 10 +-- src/gateway/auth.ts | 2 +- src/gateway/handlers/config.ts | 12 +-- src/gateway/handlers/sessions.ts | 2 +- src/gateway/lane-queue.ts | 4 +- src/gateway/protocol.ts | 4 +- src/gateway/server.ts | 4 +- src/gateway/session-bridge.ts | 10 +-- src/gateway/ui/app.js | 4 +- src/gateway/ui/lib/ws-client.js | 6 +- src/gateway/ui/pages/chat.js | 44 +++++----- src/gateway/ui/pages/dashboard.js | 24 +++--- src/gateway/ui/pages/sessions.js | 6 +- src/gateway/ui/pages/settings.js | 4 +- src/gateway/ui/pages/usage.js | 10 +-- src/logger.ts | 8 +- src/mcp/bridge.ts | 2 +- src/mcp/manager.ts | 6 +- src/memory/vector-store.ts | 4 +- src/models/bedrock.ts | 8 +- src/models/gemini.test.ts | 4 +- src/models/github.ts | 4 +- src/models/local/llamacpp.ts | 12 +-- src/models/local/ollama.ts | 4 +- src/models/media.ts | 2 +- src/models/retry.test.ts | 4 +- src/models/router.test.ts | 2 +- src/models/router.ts | 16 ++-- src/sandbox/docker.ts | 6 +- src/sandbox/manager.ts | 4 +- src/session/manager.ts | 4 +- src/session/store.ts | 16 ++-- src/skills/installer.ts | 2 +- src/skills/registry.ts | 2 +- src/tools/builtin/agents-list.ts | 6 +- src/tools/builtin/browser/manager.ts | 4 +- src/tools/builtin/cron.ts | 2 +- src/tools/builtin/gcal.test.ts | 4 +- src/tools/builtin/gcal.ts | 6 +- src/tools/builtin/gdocs.test.ts | 4 +- src/tools/builtin/gdocs.ts | 6 +- src/tools/builtin/gdrive.test.ts | 4 +- src/tools/builtin/gdrive.ts | 14 ++-- src/tools/builtin/gmail.test.ts | 4 +- src/tools/builtin/gmail.ts | 10 +-- src/tools/builtin/gtasks.test.ts | 4 +- src/tools/builtin/gtasks.ts | 4 +- src/tools/builtin/image-analyze.test.ts | 84 ++++++++++---------- src/tools/builtin/image-analyze.ts | 48 +++++------ src/tools/builtin/memory-search.ts | 4 +- src/tools/builtin/process/list.ts | 2 +- src/tools/builtin/process/manager.test.ts | 2 +- src/tools/builtin/process/manager.ts | 12 +-- src/tools/builtin/process/status.ts | 6 +- src/tools/executor.ts | 2 +- src/tools/registry.ts | 2 +- src/utils/html.ts | 4 +- 99 files changed, 418 insertions(+), 418 deletions(-) diff --git a/src/auth/github.ts b/src/auth/github.ts index d5f0fa0..7e6528b 100644 --- a/src/auth/github.ts +++ b/src/auth/github.ts @@ -140,7 +140,7 @@ export function storeToken(token: string): void { export function getGitHubToken(): string | null { // 1. Environment variable const envToken = process.env.GITHUB_TOKEN; - if (envToken) return envToken; + if (envToken) {return envToken;} // 2. Stored OAuth token return loadStoredToken(); diff --git a/src/automation/cron.ts b/src/automation/cron.ts index 95e190e..5670a13 100644 --- a/src/automation/cron.ts +++ b/src/automation/cron.ts @@ -31,7 +31,7 @@ export class CronScheduler implements ChannelAdapter { this._status = 'connected'; for (const job of this.jobConfigs) { - if (!job.enabled) continue; + if (!job.enabled) {continue;} const cronInstance = new Cron(job.schedule, { timezone: job.timezone, @@ -81,7 +81,7 @@ export class CronScheduler implements ChannelAdapter { /** Manually trigger a job (also called by cron on schedule). */ triggerJob(jobName: string): void { const job = this.jobs.get(jobName); - if (!job) return; + if (!job) {return;} const msg: InboundMessage = { id: `cron-${jobName}-${Date.now()}`, diff --git a/src/automation/gmail.test.ts b/src/automation/gmail.test.ts index 25e2e87..1a5c931 100644 --- a/src/automation/gmail.test.ts +++ b/src/automation/gmail.test.ts @@ -181,8 +181,8 @@ describe('GmailWatcher', () => { // credentials file exists but token file does not mockExistsSync.mockImplementation((path: unknown) => { const p = String(path); - if (p.includes('credentials')) return true; - if (p.includes('token')) return false; + if (p.includes('credentials')) {return true;} + if (p.includes('token')) {return false;} return true; }); diff --git a/src/automation/gmail.ts b/src/automation/gmail.ts index ba2a6ef..3a81946 100644 --- a/src/automation/gmail.ts +++ b/src/automation/gmail.ts @@ -212,7 +212,7 @@ export class GmailWatcher implements ChannelAdapter { * Calls gmail.users.watch() and schedules renewal before expiry. */ private async setupWatch(): Promise { - if (!this.oauth2Client) return; + if (!this.oauth2Client) {return;} const gmail = google.gmail({ version: 'v1', auth: this.oauth2Client }); @@ -243,7 +243,7 @@ export class GmailWatcher implements ChannelAdapter { * Fallback mechanism when Pub/Sub push is not available. */ private async pollForNewMessages(): Promise { - if (!this.oauth2Client) return; + if (!this.oauth2Client) {return;} const gmail = google.gmail({ version: 'v1', auth: this.oauth2Client }); @@ -268,7 +268,7 @@ export class GmailWatcher implements ChannelAdapter { * Updates lastHistoryId to the latest value from the response. */ private async processHistoryChanges(startHistoryId: string): Promise { - if (!this.oauth2Client) return; + if (!this.oauth2Client) {return;} const gmail = google.gmail({ version: 'v1', auth: this.oauth2Client }); @@ -287,17 +287,17 @@ export class GmailWatcher implements ChannelAdapter { const addedMessages = record.messagesAdded ?? []; for (const added of addedMessages) { const messageId = added.message?.id; - if (!messageId || processedIds.has(messageId)) continue; + if (!messageId || processedIds.has(messageId)) {continue;} processedIds.add(messageId); const email = await this.getMessageDetails(messageId); - if (!email) continue; + if (!email) {continue;} // Skip messages before history_start if configured if (this.config.history_start) { const emailDate = new Date(email.date); const startDate = new Date(this.config.history_start); - if (emailDate < startDate) continue; + if (emailDate < startDate) {continue;} } const text = this.renderTemplate(email); @@ -348,7 +348,7 @@ export class GmailWatcher implements ChannelAdapter { * Fetch full message details by ID and extract relevant headers. */ private async getMessageDetails(messageId: string): Promise { - if (!this.oauth2Client) return null; + if (!this.oauth2Client) {return null;} const gmail = google.gmail({ version: 'v1', auth: this.oauth2Client }); diff --git a/src/automation/heartbeat.ts b/src/automation/heartbeat.ts index 414c797..a470d9a 100644 --- a/src/automation/heartbeat.ts +++ b/src/automation/heartbeat.ts @@ -73,7 +73,7 @@ export class HeartbeatMonitor { /** Start the heartbeat monitor. Does nothing if disabled. */ start(): void { - if (!this.deps.config.enabled) return; + if (!this.deps.config.enabled) {return;} const intervalMs = parseInterval(this.deps.config.interval); console.log(`HeartbeatMonitor: starting (interval=${this.deps.config.interval}, checks=[${this.deps.config.checks.join(', ')}])`); @@ -290,7 +290,7 @@ export class HeartbeatMonitor { private async notify(text: string): Promise { const notifyConfig = this.deps.config.notify; - if (!notifyConfig) return; + if (!notifyConfig) {return;} const adapter = this.deps.channelLookup.get(notifyConfig.channel); if (!adapter) { diff --git a/src/automation/webhooks.test.ts b/src/automation/webhooks.test.ts index 3fba3e6..0f2c07b 100644 --- a/src/automation/webhooks.test.ts +++ b/src/automation/webhooks.test.ts @@ -36,7 +36,7 @@ function mockResponse(): ServerResponse & { statusCode_: number; body_: string; headers_: {}, writeHead(code: number, headers?: Record) { res.statusCode_ = code; - if (headers) res.headers_ = headers; + if (headers) {res.headers_ = headers;} return res; }, end(body?: string) { diff --git a/src/automation/webhooks.ts b/src/automation/webhooks.ts index eed77e5..60254cf 100644 --- a/src/automation/webhooks.ts +++ b/src/automation/webhooks.ts @@ -23,7 +23,7 @@ function verifyHmac(body: string, secret: string, signature: string): boolean { const expected = createHmac('sha256', secret).update(body).digest('hex'); const sig = signature.startsWith('sha256=') ? signature.slice(7) : signature; - if (expected.length !== sig.length) return false; + if (expected.length !== sig.length) {return false;} try { return timingSafeEqual(Buffer.from(expected, 'hex'), Buffer.from(sig, 'hex')); @@ -51,7 +51,7 @@ function renderTemplate(template: string, body: string): string { } } const value = parsed[field]; - if (value === undefined || value === null) return ''; + if (value === undefined || value === null) {return '';} return typeof value === 'string' ? value : JSON.stringify(value); }); diff --git a/src/backends/native/orchestrator.test.ts b/src/backends/native/orchestrator.test.ts index c1966f8..ff046da 100644 --- a/src/backends/native/orchestrator.test.ts +++ b/src/backends/native/orchestrator.test.ts @@ -243,7 +243,7 @@ describe('AgentOrchestrator', () => { }); expect(consoleSpy).toHaveBeenCalledWith( - '[Flynn:delegate] tier=fast tokens=50+25' + '[Flynn:delegate] tier=fast tokens=50+25', ); consoleSpy.mockRestore(); diff --git a/src/backends/native/orchestrator.ts b/src/backends/native/orchestrator.ts index 5c43ff3..e7db3d0 100644 --- a/src/backends/native/orchestrator.ts +++ b/src/backends/native/orchestrator.ts @@ -364,10 +364,10 @@ export class AgentOrchestrator { * Called before each `process()` call when compaction is configured. */ private async compactIfNeeded(): Promise { - if (!this._compactionConfig) return; + if (!this._compactionConfig) {return;} const messages = this.getHistory(); - if (messages.length === 0) return; + if (messages.length === 0) {return;} const model = this._modelName ?? 'unknown'; const needs = shouldCompact({ @@ -377,7 +377,7 @@ export class AgentOrchestrator { thresholdPct: this._compactionConfig.thresholdPct, }); - if (!needs) return; + if (!needs) {return;} await this.compact(); } diff --git a/src/channels/discord/adapter.test.ts b/src/channels/discord/adapter.test.ts index 131ae41..c7f5935 100644 --- a/src/channels/discord/adapter.test.ts +++ b/src/channels/discord/adapter.test.ts @@ -14,7 +14,7 @@ function createMockClient() { _handlers: handlers, user: null as { id: string; tag: string } | null, on: vi.fn((event: string, handler: (...args: unknown[]) => void) => { - if (!handlers.has(event)) handlers.set(event, []); + if (!handlers.has(event)) {handlers.set(event, []);} handlers.get(event)!.push(handler); }), login: vi.fn(async (_token: string) => { @@ -23,7 +23,7 @@ function createMockClient() { // Trigger ready event asynchronously setTimeout(() => { const readyHandlers = handlers.get('ready') ?? []; - for (const h of readyHandlers) h(); + for (const h of readyHandlers) {h();} }, 0); }), destroy: vi.fn(), diff --git a/src/channels/discord/adapter.ts b/src/channels/discord/adapter.ts index 440def9..1d8b44a 100644 --- a/src/channels/discord/adapter.ts +++ b/src/channels/discord/adapter.ts @@ -117,7 +117,7 @@ export class DiscordAdapter implements ChannelAdapter { /** Send an outbound message, automatically chunking if it exceeds Discord's 2000-char limit. */ async send(peerId: string, message: OutboundMessage): Promise { - if (!this.client) throw new Error('Discord adapter not connected'); + if (!this.client) {throw new Error('Discord adapter not connected');} const channel = await this.client.channels.fetch(peerId); if (!channel || !('send' in channel)) { @@ -163,10 +163,10 @@ export class DiscordAdapter implements ChannelAdapter { /** Internal: process an inbound Discord message. */ private handleMessage(message: DiscordMessage): void { - if (!this.messageHandler) return; + if (!this.messageHandler) {return;} // Ignore bot messages - if (message.author.bot) return; + if (message.author.bot) {return;} const isDM = !message.guild; diff --git a/src/channels/pairing.ts b/src/channels/pairing.ts index dd33d26..8224b10 100644 --- a/src/channels/pairing.ts +++ b/src/channels/pairing.ts @@ -83,7 +83,7 @@ export class PairingManager { const normalizedCode = code.trim().toUpperCase(); const pending = this.pendingCodes.get(normalizedCode); - if (!pending) return false; + if (!pending) {return false;} if (Date.now() > pending.expiresAt) { this.pendingCodes.delete(normalizedCode); return false; diff --git a/src/channels/registry.ts b/src/channels/registry.ts index 95b149f..8eb0fe7 100644 --- a/src/channels/registry.ts +++ b/src/channels/registry.ts @@ -32,7 +32,7 @@ export class ChannelRegistry { /** Unregister an adapter by name. Calls disconnect() if connected. */ async unregister(name: string): Promise { const adapter = this.adapters.get(name); - if (!adapter) return; + if (!adapter) {return;} if (adapter.status === 'connected' || adapter.status === 'connecting') { await adapter.disconnect(); diff --git a/src/channels/slack/adapter.test.ts b/src/channels/slack/adapter.test.ts index 7424e1f..2506373 100644 --- a/src/channels/slack/adapter.test.ts +++ b/src/channels/slack/adapter.test.ts @@ -50,7 +50,7 @@ const baseConfig: SlackAdapterConfig = { /** Helper: simulate a Slack message event through the captured handler. */ async function simulateMessage(message: Record) { - if (!capturedMessageHandler) throw new Error('No message handler captured — call connect() first'); + if (!capturedMessageHandler) {throw new Error('No message handler captured — call connect() first');} await capturedMessageHandler({ message }); } diff --git a/src/channels/slack/adapter.ts b/src/channels/slack/adapter.ts index 752d6b9..6c7e5fb 100644 --- a/src/channels/slack/adapter.ts +++ b/src/channels/slack/adapter.ts @@ -128,15 +128,15 @@ export class SlackAdapter implements ChannelAdapter { /** Send an outbound message, automatically chunking if it exceeds 4000 chars. */ async send(peerId: string, message: OutboundMessage): Promise { - if (!this.app) throw new Error('Slack adapter not connected'); + if (!this.app) {throw new Error('Slack adapter not connected');} // Parse peerId: "channelId:threadTs" const colonIndex = peerId.indexOf(':'); - if (colonIndex === -1) throw new Error(`Invalid peer ID format: ${peerId}`); + if (colonIndex === -1) {throw new Error(`Invalid peer ID format: ${peerId}`);} const channel = peerId.slice(0, colonIndex); const threadTs = peerId.slice(colonIndex + 1); - if (!channel || !threadTs) throw new Error(`Invalid peer ID format: ${peerId}`); + if (!channel || !threadTs) {throw new Error(`Invalid peer ID format: ${peerId}`);} const text = message.text; @@ -171,7 +171,7 @@ export class SlackAdapter implements ChannelAdapter { threadTs: string, attachment: OutboundAttachment, ): Promise { - if (!this.app) return; + if (!this.app) {return;} try { if (attachment.data) { @@ -200,7 +200,7 @@ export class SlackAdapter implements ChannelAdapter { /** Resolve a Slack user ID to a display name, with caching. */ private async resolveUserName(userId: string): Promise { const cached = this.userNameCache.get(userId); - if (cached) return cached; + if (cached) {return cached;} try { const result = await this.app!.client.users.info({ user: userId }); @@ -219,16 +219,16 @@ export class SlackAdapter implements ChannelAdapter { private async extractMediaAttachments( files?: SlackMessageEvent['files'], ): Promise { - if (!files || files.length === 0) return []; + if (!files || files.length === 0) {return [];} const attachments: Attachment[] = []; for (const file of files) { // Only process image and audio files - if (!file.mimetype?.startsWith('image/') && !file.mimetype?.startsWith('audio/')) continue; + if (!file.mimetype?.startsWith('image/') && !file.mimetype?.startsWith('audio/')) {continue;} const downloadUrl = file.url_private_download || file.url_private; - if (!downloadUrl) continue; + if (!downloadUrl) {continue;} try { const response = await fetch(downloadUrl, { @@ -264,13 +264,13 @@ export class SlackAdapter implements ChannelAdapter { /** Internal: process an inbound Slack message event. */ private async handleMessage(message: SlackMessageEvent): Promise { - if (!this.messageHandler) return; + if (!this.messageHandler) {return;} // Ignore bot messages - if (message.bot_id || message.subtype === 'bot_message') return; + if (message.bot_id || message.subtype === 'bot_message') {return;} const channelId = message.channel; - if (!channelId) return; + if (!channelId) {return;} // Check allowed channel IDs if ( diff --git a/src/channels/telegram/adapter.ts b/src/channels/telegram/adapter.ts index 3e7cc3b..340fd2a 100644 --- a/src/channels/telegram/adapter.ts +++ b/src/channels/telegram/adapter.ts @@ -53,13 +53,13 @@ export class TelegramAdapter implements ChannelAdapter { private async downloadFileToBase64(fileId: string): Promise { try { const file = await this.bot?.api.getFile(fileId); - if (!file || !file.file_path) return null; + if (!file || !file.file_path) {return null;} const token = this.config.botToken; const url = `https://api.telegram.org/file/bot${token}/${file.file_path}`; const response = await fetch(url); - if (!response.ok) return null; + if (!response.ok) {return null;} const buffer = Buffer.from(await response.arrayBuffer()); return buffer.toString('base64'); @@ -82,7 +82,7 @@ export class TelegramAdapter implements ChannelAdapter { // ── Auth middleware — reject messages from unknown chats (with pairing fallback) ── this.bot.use(async (ctx, next) => { const chatId = ctx.chat?.id; - if (chatId === undefined) return; + if (chatId === undefined) {return;} // Allowlist check if (isAllowedChat(chatId, this.config.allowedChatIds)) { @@ -166,7 +166,7 @@ export class TelegramAdapter implements ChannelAdapter { // ── Text message handler ── this.bot.on('message:text', async (ctx) => { - if (!this.messageHandler) return; + if (!this.messageHandler) {return;} // Group chat mention gating const isGroup = ctx.chat.type === 'group' || ctx.chat.type === 'supergroup'; @@ -212,10 +212,10 @@ export class TelegramAdapter implements ChannelAdapter { // ── Photo message handler ── this.bot.on('message:photo', async (ctx) => { - if (!this.messageHandler) return; + if (!this.messageHandler) {return;} const photo = ctx.message.photo; - if (!photo || photo.length === 0) return; + if (!photo || photo.length === 0) {return;} const largestPhoto = photo[photo.length - 1]; @@ -250,13 +250,13 @@ export class TelegramAdapter implements ChannelAdapter { // ── Image document handler ── this.bot.on('message:document', async (ctx) => { - if (!this.messageHandler) return; + if (!this.messageHandler) {return;} const document = ctx.message.document; - if (!document) return; + if (!document) {return;} const mimeType = document.mime_type ?? ''; - if (!mimeType.startsWith('image/')) return; + if (!mimeType.startsWith('image/')) {return;} await ctx.replyWithChatAction('typing'); @@ -290,10 +290,10 @@ export class TelegramAdapter implements ChannelAdapter { // ── Voice message handler ── this.bot.on('message:voice', async (ctx) => { - if (!this.messageHandler) return; + if (!this.messageHandler) {return;} const voice = ctx.message.voice; - if (!voice) return; + if (!voice) {return;} await ctx.replyWithChatAction('typing'); @@ -327,10 +327,10 @@ export class TelegramAdapter implements ChannelAdapter { // ── Audio message handler ── this.bot.on('message:audio', async (ctx) => { - if (!this.messageHandler) return; + if (!this.messageHandler) {return;} const audio = ctx.message.audio; - if (!audio) return; + if (!audio) {return;} await ctx.replyWithChatAction('typing'); @@ -388,7 +388,7 @@ export class TelegramAdapter implements ChannelAdapter { /** Send an outbound message, automatically chunking if it exceeds Telegram's limit. */ async send(peerId: string, message: OutboundMessage): Promise { - if (!this.bot) throw new Error('Telegram adapter not connected'); + if (!this.bot) {throw new Error('Telegram adapter not connected');} const chatId = Number(peerId); const text = message.text; @@ -413,7 +413,7 @@ export class TelegramAdapter implements ChannelAdapter { /** Send a single outbound attachment via the Telegram API. */ private async sendAttachment(chatId: number, attachment: OutboundAttachment): Promise { - if (!this.bot) return; + if (!this.bot) {return;} try { const file = attachment.data diff --git a/src/channels/whatsapp/adapter.ts b/src/channels/whatsapp/adapter.ts index bc5753e..1d70984 100644 --- a/src/channels/whatsapp/adapter.ts +++ b/src/channels/whatsapp/adapter.ts @@ -146,7 +146,7 @@ export class WhatsAppAdapter implements ChannelAdapter { /** Send an outbound message, automatically chunking if it exceeds 4096 chars. */ async send(peerId: string, message: OutboundMessage): Promise { - if (!this.client) throw new Error('WhatsApp adapter not connected'); + if (!this.client) {throw new Error('WhatsApp adapter not connected');} const text = message.text; @@ -169,7 +169,7 @@ export class WhatsAppAdapter implements ChannelAdapter { /** Send a single outbound attachment via WhatsApp using MessageMedia. */ private async sendAttachment(peerId: string, attachment: OutboundAttachment): Promise { - if (!this.client) return; + if (!this.client) {return;} try { if (attachment.data) { @@ -194,10 +194,10 @@ export class WhatsAppAdapter implements ChannelAdapter { /** Internal: process an inbound WhatsApp message. */ private async handleMessage(message: WhatsAppMessage): Promise { - if (!this.messageHandler) return; + if (!this.messageHandler) {return;} // Ignore messages from the bot itself - if (message.fromMe) return; + if (message.fromMe) {return;} const from = message.from; @@ -223,7 +223,7 @@ export class WhatsAppAdapter implements ChannelAdapter { ? message.body?.includes(`@${this.botId.replace(/@c\.us$/, '')}`) || (message as any).mentionedIds?.some((id: string) => id === this.botId) : false; - if (!mentionsBot) return; + if (!mentionsBot) {return;} } } diff --git a/src/cli/completion.ts b/src/cli/completion.ts index 8043a5e..e3fa6f5 100644 --- a/src/cli/completion.ts +++ b/src/cli/completion.ts @@ -74,18 +74,18 @@ compdef _flynn flynn export function generateFishCompletion(): string { const lines = [ - `# Flynn fish completion — generated by 'flynn completion fish'`, - `# Disable file completions by default`, - `complete -c flynn -f`, - ``, - `# Subcommands`, + '# Flynn fish completion — generated by \'flynn completion fish\'', + '# Disable file completions by default', + 'complete -c flynn -f', + '', + '# Subcommands', ]; for (const cmd of SUBCOMMANDS) { lines.push(`complete -c flynn -n '__fish_use_subcommand' -a '${cmd}' -d '${cmd} subcommand'`); } - lines.push(`complete -c flynn -n '__fish_use_subcommand' -l help -d 'Show help'`); - lines.push(`complete -c flynn -n '__fish_use_subcommand' -l version -d 'Show version'`); + lines.push('complete -c flynn -n \'__fish_use_subcommand\' -l help -d \'Show help\''); + lines.push('complete -c flynn -n \'__fish_use_subcommand\' -l version -d \'Show version\''); lines.push(''); lines.push('# Subcommand options'); @@ -98,7 +98,7 @@ export function generateFishCompletion(): string { } } - lines.push(`complete -c flynn -n '__fish_seen_subcommand_from completion' -a 'bash zsh fish' -d 'Shell type'`); + lines.push('complete -c flynn -n \'__fish_seen_subcommand_from completion\' -a \'bash zsh fish\' -d \'Shell type\''); lines.push(''); return lines.join('\n'); @@ -141,7 +141,7 @@ export function registerCompletionCommand(program: Command): void { writeFileSync(installPath, script, 'utf-8'); console.log(`Completion script installed to: ${installPath}`); if (shell === 'zsh') { - console.log(`Ensure ~/.zfunc is in your fpath: fpath=(~/.zfunc $fpath)`); + console.log('Ensure ~/.zfunc is in your fpath: fpath=(~/.zfunc $fpath)'); } } else { process.stdout.write(script); diff --git a/src/cli/doctor.ts b/src/cli/doctor.ts index 339a4af..f8764b4 100644 --- a/src/cli/doctor.ts +++ b/src/cli/doctor.ts @@ -152,9 +152,9 @@ const checkModelConnectivity: Check = async (ctx) => { // Build a summary of the model stack const parts = [`default: ${model.provider}/${model.model}`]; - if (models.fast) parts.push(`fast: ${models.fast.provider}/${models.fast.model}`); - if (models.complex) parts.push(`complex: ${models.complex.provider}/${models.complex.model}`); - if (models.local) parts.push(`local: ${models.local.provider}/${models.local.model}`); + if (models.fast) {parts.push(`fast: ${models.fast.provider}/${models.fast.model}`);} + if (models.complex) {parts.push(`complex: ${models.complex.provider}/${models.complex.model}`);} + if (models.local) {parts.push(`local: ${models.local.provider}/${models.local.model}`);} parts.push(`fallback: [${models.fallback_chain.join(', ')}]`); return { status: 'pass', label: 'Model connectivity', detail: parts.join(', ') }; diff --git a/src/cli/send.ts b/src/cli/send.ts index b3043ed..81a2a47 100644 --- a/src/cli/send.ts +++ b/src/cli/send.ts @@ -38,7 +38,7 @@ function loadSystemPrompt(): string { resolve(import.meta.dirname, '../../SOUL.md'), ]; for (const p of paths) { - if (existsSync(p)) return readFileSync(p, 'utf-8'); + if (existsSync(p)) {return readFileSync(p, 'utf-8');} } return 'You are Flynn, a helpful personal AI assistant.'; } diff --git a/src/cli/sessions.test.ts b/src/cli/sessions.test.ts index 2517ba7..52a8b4a 100644 --- a/src/cli/sessions.test.ts +++ b/src/cli/sessions.test.ts @@ -11,7 +11,7 @@ describe('sessions command', () => { afterEach(() => { store?.close(); - if (existsSync(dbPath)) unlinkSync(dbPath); + if (existsSync(dbPath)) {unlinkSync(dbPath);} }); it('returns empty list when no sessions', () => { diff --git a/src/cli/setup/automation.ts b/src/cli/setup/automation.ts index 84709fd..51fa53d 100644 --- a/src/cli/setup/automation.ts +++ b/src/cli/setup/automation.ts @@ -74,7 +74,7 @@ export async function setupAutomation(p: Prompter, builder: ConfigBuilder): Prom // Google services const wantGoogle = await p.confirm('Configure Google services (Gmail, Calendar, Docs, Drive, Tasks)?', false); - if (!wantGoogle) return; + if (!wantGoogle) {return;} p.println(); for (const line of GOOGLE_SETUP_INSTRUCTIONS) { @@ -112,7 +112,7 @@ export async function setupAutomation(p: Prompter, builder: ConfigBuilder): Prom */ export async function runGoogleAuth(p: Prompter, config: Record): Promise { const automation = config.automation as Record | undefined; - if (!automation) return; + if (!automation) {return;} const pending: { name: string; authCmd: string }[] = []; for (const svc of GOOGLE_SERVICES) { @@ -122,7 +122,7 @@ export async function runGoogleAuth(p: Prompter, config: Record): P } } - if (pending.length === 0) return; + if (pending.length === 0) {return;} p.println(); const runAuth = await p.confirm(`Run OAuth authentication for ${pending.map(s => s.name).join(', ')}?`, true); diff --git a/src/cli/setup/channels.ts b/src/cli/setup/channels.ts index f2d3005..f6c6cbb 100644 --- a/src/cli/setup/channels.ts +++ b/src/cli/setup/channels.ts @@ -87,10 +87,10 @@ export async function setupChannels(p: Prompter, builder: ConfigBuilder): Promis if (choice === 'more') { const moreChoice = await p.choose('Channel:', MORE_CHANNEL_OPTIONS); const setup = CHANNEL_SETUP[moreChoice]; - if (setup) await setup(p, builder); + if (setup) {await setup(p, builder);} } else { const setup = CHANNEL_SETUP[choice]; - if (setup) await setup(p, builder); + if (setup) {await setup(p, builder);} } p.println(); diff --git a/src/cli/setup/config.ts b/src/cli/setup/config.ts index bdf07c2..9eba148 100644 --- a/src/cli/setup/config.ts +++ b/src/cli/setup/config.ts @@ -39,9 +39,9 @@ export class ConfigBuilder { setProvider(tier: 'default' | 'fast' | 'complex' | 'local', cfg: ProviderConfig): void { const models = (this.config.models ?? {}) as Record; const entry: Record = { provider: cfg.provider, model: cfg.model }; - if (cfg.api_key) entry.api_key = cfg.api_key; - if (cfg.auth_token) entry.auth_token = cfg.auth_token; - if (cfg.endpoint) entry.endpoint = cfg.endpoint; + if (cfg.api_key) {entry.api_key = cfg.api_key;} + if (cfg.auth_token) {entry.auth_token = cfg.auth_token;} + if (cfg.endpoint) {entry.endpoint = cfg.endpoint;} models[tier] = entry; this.config.models = models; } @@ -91,8 +91,8 @@ export class ConfigBuilder { setMemoryEmbedding(cfg: EmbeddingConfig): void { const memory = (this.config.memory ?? {}) as Record; const embedding: Record = { enabled: true, provider: cfg.provider }; - if (cfg.api_key) embedding.api_key = cfg.api_key; - if (cfg.endpoint) embedding.endpoint = cfg.endpoint; + if (cfg.api_key) {embedding.api_key = cfg.api_key;} + if (cfg.endpoint) {embedding.endpoint = cfg.endpoint;} memory.embedding = embedding; this.config.memory = memory; } @@ -151,7 +151,7 @@ export class ConfigBuilder { setCronEnabled(): void { const automation = (this.config.automation ?? {}) as Record; - if (!automation.cron) automation.cron = []; + if (!automation.cron) {automation.cron = [];} this.config.automation = automation; } diff --git a/src/cli/setup/memory.ts b/src/cli/setup/memory.ts index 83c577b..53b7551 100644 --- a/src/cli/setup/memory.ts +++ b/src/cli/setup/memory.ts @@ -14,7 +14,7 @@ export async function setupMemory(p: Prompter, builder: ConfigBuilder): Promise< p.println(' Vector search enables semantic memory — Flynn remembers and retrieves'); p.println(' information based on meaning, not just keywords.'); const enable = await p.confirm('Enable vector search for semantic memory?', false); - if (!enable) return; + if (!enable) {return;} p.println(' Pick a provider to generate embeddings (vector representations of text).'); p.println(' If you already configured OpenAI or Gemini as a model, you can reuse that key.'); @@ -43,7 +43,7 @@ function findReusableApiKey(config: Record, embeddingProvider: stri const models = config.models ?? {}; for (const tier of ['default', 'fast', 'complex', 'local']) { const m = models[tier]; - if (m?.provider === embeddingProvider && m?.api_key) return m.api_key; + if (m?.provider === embeddingProvider && m?.api_key) {return m.api_key;} } return undefined; } diff --git a/src/cli/setup/orchestrator.ts b/src/cli/setup/orchestrator.ts index eb06724..8ad26f5 100644 --- a/src/cli/setup/orchestrator.ts +++ b/src/cli/setup/orchestrator.ts @@ -41,7 +41,7 @@ export async function runMenu(p: Prompter, builder: ConfigBuilder): Promise', '0'); const idx = parseInt(answer, 10); - if (idx === 0 || isNaN(idx)) break; + if (idx === 0 || isNaN(idx)) {break;} if (idx >= 1 && idx <= MENU_OPTIONS.length) { const section = MENU_OPTIONS[idx - 1].value; const handler = SECTION_HANDLERS[section]; diff --git a/src/cli/setup/prompts.ts b/src/cli/setup/prompts.ts index cd4444f..8a10641 100644 --- a/src/cli/setup/prompts.ts +++ b/src/cli/setup/prompts.ts @@ -25,7 +25,7 @@ export function createPrompter(rl: ReadlineInterface): Prompter { const hint = defaultYes ? '[Y/n]' : '[y/N]'; const answer = await rl.question(`${question} ${hint} `); const trimmed = answer.trim().toLowerCase(); - if (trimmed === '') return defaultYes; + if (trimmed === '') {return defaultYes;} return trimmed === 'y' || trimmed === 'yes'; }, @@ -34,9 +34,9 @@ export function createPrompter(rl: ReadlineInterface): Prompter { for (let i = 0; i < options.length; i++) { this.println(` ${i + 1}. ${options[i].label}`); } - const answer = await rl.question(`> `); + const answer = await rl.question('> '); const idx = parseInt(answer.trim(), 10) - 1; - if (idx >= 0 && idx < options.length) return options[idx].value; + if (idx >= 0 && idx < options.length) {return options[idx].value;} return options[0].value; }, diff --git a/src/cli/setup/providers.ts b/src/cli/setup/providers.ts index 75441fd..3869abe 100644 --- a/src/cli/setup/providers.ts +++ b/src/cli/setup/providers.ts @@ -41,11 +41,11 @@ async function configureProvider(p: Prompter, def: ProviderDef): Promise<{ provider: string; model: string; api_key?: string; endpoint?: string; }> { const help = PROVIDER_HELP[def.provider]; - if (help) p.println(` ${help}`); + if (help) {p.println(` ${help}`);} const config: Record = { provider: def.provider }; - if (def.needsApiKey) config.api_key = await p.password(def.apiKeyLabel ?? 'API key'); - if (def.needsEndpoint) config.endpoint = await p.ask('Host', def.defaultEndpoint); + if (def.needsApiKey) {config.api_key = await p.password(def.apiKeyLabel ?? 'API key');} + if (def.needsEndpoint) {config.endpoint = await p.ask('Host', def.defaultEndpoint);} config.model = await p.ask('Model', def.defaultModel); return config as { provider: string; model: string; api_key?: string; endpoint?: string }; } diff --git a/src/cli/setup/summary.ts b/src/cli/setup/summary.ts index ef2d9a1..5ea5315 100644 --- a/src/cli/setup/summary.ts +++ b/src/cli/setup/summary.ts @@ -9,11 +9,11 @@ export function renderSummary(config: Record): string { lines.push(` Models: ${tiers || 'none configured'}`); const channels: string[] = []; - if (config.server?.port) channels.push('webchat'); - if (config.telegram) channels.push('telegram'); - if (config.discord) channels.push('discord'); - if (config.slack) channels.push('slack'); - if (config.whatsapp) channels.push('whatsapp'); + if (config.server?.port) {channels.push('webchat');} + if (config.telegram) {channels.push('telegram');} + if (config.discord) {channels.push('discord');} + if (config.slack) {channels.push('slack');} + if (config.whatsapp) {channels.push('whatsapp');} lines.push(` Channels: ${channels.join(', ') || 'none'}`); const embedding = config.memory?.embedding; @@ -22,27 +22,27 @@ export function renderSummary(config: Record): string { const auto = config.automation ?? {}; const autoFeatures: string[] = []; - if (auto.cron?.length > 0) autoFeatures.push(`${auto.cron.length} cron jobs`); - if (auto.webhooks?.length > 0) autoFeatures.push('webhooks'); - if (auto.gmail?.enabled) autoFeatures.push('gmail'); - if (auto.gcal?.enabled) autoFeatures.push('gcal'); - if (auto.gdocs?.enabled) autoFeatures.push('gdocs'); - if (auto.gdrive?.enabled) autoFeatures.push('gdrive'); - if (auto.gtasks?.enabled) autoFeatures.push('gtasks'); - if (auto.heartbeat?.enabled) autoFeatures.push('heartbeat'); + if (auto.cron?.length > 0) {autoFeatures.push(`${auto.cron.length} cron jobs`);} + if (auto.webhooks?.length > 0) {autoFeatures.push('webhooks');} + if (auto.gmail?.enabled) {autoFeatures.push('gmail');} + if (auto.gcal?.enabled) {autoFeatures.push('gcal');} + if (auto.gdocs?.enabled) {autoFeatures.push('gdocs');} + if (auto.gdrive?.enabled) {autoFeatures.push('gdrive');} + if (auto.gtasks?.enabled) {autoFeatures.push('gtasks');} + if (auto.heartbeat?.enabled) {autoFeatures.push('heartbeat');} lines.push(` Automation: ${autoFeatures.join(', ') || 'none'}`); const secFeatures: string[] = []; secFeatures.push(`tools:${config.tools?.profile ?? 'full'}`); - if (config.sandbox?.enabled) secFeatures.push('sandbox'); - if (config.pairing?.enabled) secFeatures.push('pairing'); + if (config.sandbox?.enabled) {secFeatures.push('sandbox');} + if (config.pairing?.enabled) {secFeatures.push('pairing');} lines.push(` Security: ${secFeatures.join(', ')}`); const gw: string[] = []; gw.push(`port ${config.server?.port ?? 18800}`); - if (config.server?.token) gw.push('auth'); - if (config.server?.lock) gw.push('locked'); - if (config.server?.tailscale?.serve) gw.push('tailscale'); + if (config.server?.token) {gw.push('auth');} + if (config.server?.lock) {gw.push('locked');} + if (config.server?.tailscale?.serve) {gw.push('tailscale');} lines.push(` Gateway: ${gw.join(', ')}`); return lines.join('\n'); diff --git a/src/cli/shared.ts b/src/cli/shared.ts index 1ee27a9..547e660 100644 --- a/src/cli/shared.ts +++ b/src/cli/shared.ts @@ -21,7 +21,7 @@ export function getDataDir(): string { */ export function resolveOverlayPath(basePath: string): string | undefined { const env = process.env.FLYNN_ENV; - if (!env) return undefined; + if (!env) {return undefined;} const configDir = dirname(basePath); return join(configDir, `${env}.yaml`); } @@ -44,8 +44,8 @@ export function redactSecrets(config: Record): Record = {}; for (const [key, value] of Object.entries(obj as Record)) { diff --git a/src/cli/tui.ts b/src/cli/tui.ts index 063eea7..3013f98 100644 --- a/src/cli/tui.ts +++ b/src/cli/tui.ts @@ -27,9 +27,9 @@ function formatToolName(name: string): string { /** Format tool args as a compact, readable summary instead of raw JSON. */ function formatToolArgs(args: unknown): string { - if (!args || typeof args !== 'object') return ''; + if (!args || typeof args !== 'object') {return '';} const entries = Object.entries(args as Record); - if (entries.length === 0) return ''; + if (entries.length === 0) {return '';} const parts = entries.map(([key, value]) => { if (typeof value === 'string') { diff --git a/src/daemon/lifecycle.ts b/src/daemon/lifecycle.ts index 5d188b6..0ba92c5 100644 --- a/src/daemon/lifecycle.ts +++ b/src/daemon/lifecycle.ts @@ -14,7 +14,7 @@ export class Lifecycle { } async shutdown(): Promise { - if (this.shuttingDown) return; + if (this.shuttingDown) {return;} this.shuttingDown = true; this._isRunning = false; diff --git a/src/daemon/memory.ts b/src/daemon/memory.ts index 2cb1f58..8243ab7 100644 --- a/src/daemon/memory.ts +++ b/src/daemon/memory.ts @@ -57,7 +57,7 @@ export async function initMemory(deps: MemoryDeps): Promise { } const hash = contentHash(content); - if (vectorStore.hasContentHash(ns, hash)) continue; + if (vectorStore.hasContentHash(ns, hash)) {continue;} const chunks = chunkText(content, ns, { chunkSize: config.memory.embedding.chunk_size, diff --git a/src/daemon/models.ts b/src/daemon/models.ts index 377b505..14ae70f 100644 --- a/src/daemon/models.ts +++ b/src/daemon/models.ts @@ -12,7 +12,7 @@ function requireApiKey(cfg: ModelConfig, envVar: string): string { if (!key) { throw new Error( `API key required for ${cfg.provider}. ` + - `Set ${envVar} environment variable or provide api_key in config.` + `Set ${envVar} environment variable or provide api_key in config.`, ); } return key; @@ -118,7 +118,7 @@ export function anthropicToGitHubModel(anthropicModel: string): string | undefin 'claude-haiku-4-5-20251001': 'claude-haiku-4.5', }; - if (MAPPINGS[anthropicModel]) return MAPPINGS[anthropicModel]; + if (MAPPINGS[anthropicModel]) {return MAPPINGS[anthropicModel];} // Generic fallback: strip date suffix, then convert trailing -N to .N // only when preceded by another digit (i.e. "4-5" → "4.5", not "sonnet-5" → "sonnet.5") @@ -140,10 +140,10 @@ export function anthropicToGitHubModel(anthropicModel: string): string | undefin * Returns undefined if no mapping exists or the tier isn't Anthropic. */ export function createAutoFallbackClient(tierConfig: { provider: string; model: string }): ModelClient | undefined { - if (tierConfig.provider !== 'anthropic') return undefined; + if (tierConfig.provider !== 'anthropic') {return undefined;} const githubModel = anthropicToGitHubModel(tierConfig.model); - if (!githubModel) return undefined; + if (!githubModel) {return undefined;} return new GitHubModelsClient({ model: githubModel, @@ -202,7 +202,7 @@ export function createModelRouter(config: Config): ModelRouter { const autoFallbackTiers: string[] = []; for (const { tier, cfg } of tierConfigs) { - if (!cfg) continue; + if (!cfg) {continue;} const fallbackList: ModelClient[] = []; diff --git a/src/daemon/services.ts b/src/daemon/services.ts index c7caa9c..cf42611 100644 --- a/src/daemon/services.ts +++ b/src/daemon/services.ts @@ -92,7 +92,7 @@ export function loadSystemPrompt(config: Config, skillRegistry: SkillRegistry): // ── Pairing Manager ───────────────────────────────────────────── export function initPairingManager(config: Config, store?: PairingStore): PairingManager | undefined { - if (!config.pairing.enabled) return undefined; + if (!config.pairing.enabled) {return undefined;} const ttlMatch = config.pairing.code_ttl.match(/^(\d+)(s|m|h)$/); const codeTtlMs = ttlMatch diff --git a/src/frontends/telegram/bot.ts b/src/frontends/telegram/bot.ts index fd16431..edf062a 100644 --- a/src/frontends/telegram/bot.ts +++ b/src/frontends/telegram/bot.ts @@ -49,7 +49,7 @@ export function createTelegramBot(config: TelegramBotConfig): Bot { }); await ctx.editMessageText( ctx.callbackQuery.message?.text + `\n\n${parsed.approved ? '✅ Approved' : '❌ Denied'}`, - { parse_mode: 'Markdown' } + { parse_mode: 'Markdown' }, ); } else { await ctx.answerCallbackQuery({ text: 'Confirmation expired or not found' }); diff --git a/src/frontends/tui/commands.ts b/src/frontends/tui/commands.ts index 322f7a8..d359a78 100644 --- a/src/frontends/tui/commands.ts +++ b/src/frontends/tui/commands.ts @@ -16,7 +16,7 @@ export type Command = export function parseCommand(input: string): Command | null { const trimmed = input.trim(); - if (!trimmed) return null; + if (!trimmed) {return null;} // Quit if (trimmed === '/quit' || trimmed === '/exit') { @@ -65,12 +65,12 @@ export function parseCommand(input: string): Command | null { if (trimmed.startsWith('/model ')) { const args = trimmed.slice('/model '.length).trim(); const parts = args.split(/\s+/); - + // /model - change tier's provider/model if (parts.length === 2 && parts[1].includes('/')) { return { type: 'model', name: parts[0], providerModel: parts[1] }; } - + // /model - single word (backward compatibility) const name = parts[0]; return { type: 'model', name }; @@ -205,12 +205,12 @@ export const MODEL_TOOLTIPS: Record = { export function getCommandCompletions(partial: string): string[] { const trimmed = partial.trim(); - + // Complete /model if (trimmed.startsWith('/model ')) { const args = trimmed.slice('/model '.length).trim(); const parts = args.split(/\s+/); - + if (parts.length === 1) { // Single word - suggest model aliases const modelPartial = parts[0].toLowerCase(); @@ -225,23 +225,23 @@ export function getCommandCompletions(partial: string): string[] { .map(provider => `/model ${parts[0]} ${provider}`); } } - + // Complete slash commands if (trimmed.startsWith('/')) { return SLASH_COMMANDS.filter(cmd => cmd.startsWith(trimmed.toLowerCase())); } - + return []; } export function getCommandTooltip(partial: string): string | null { const trimmed = partial.trim().toLowerCase(); - + // Tooltip for /model arguments if (trimmed.startsWith('/model ')) { const args = trimmed.slice('/model '.length).trim(); const parts = args.split(/\s+/); - + if (parts.length === 1) { // Single word - model tier or provider const modelArg = parts[0].toLowerCase(); @@ -261,15 +261,15 @@ export function getCommandTooltip(partial: string): string | null { if (matches.length === 1) { return `Enter provider/model (e.g. ${matches[0]}/...)`; } - return `Enter provider/model (e.g. anthropic/claude-sonnet-4)`; + return 'Enter provider/model (e.g. anthropic/claude-sonnet-4)'; } } - + // Exact match tooltip if (COMMAND_TOOLTIPS[trimmed]) { return COMMAND_TOOLTIPS[trimmed]; } - + // Partial match - show tooltip if only one command matches if (trimmed.startsWith('/')) { const matches = SLASH_COMMANDS.filter(cmd => cmd.startsWith(trimmed)); @@ -277,7 +277,7 @@ export function getCommandTooltip(partial: string): string | null { return COMMAND_TOOLTIPS[matches[0]]; } } - + return null; } diff --git a/src/frontends/tui/components/App.tsx b/src/frontends/tui/components/App.tsx index 9f0e0a9..b365f50 100644 --- a/src/frontends/tui/components/App.tsx +++ b/src/frontends/tui/components/App.tsx @@ -21,9 +21,9 @@ function formatToolName(name: string): string { /** Format tool args as a compact, readable summary. */ function formatToolArgs(args: unknown): string { - if (!args || typeof args !== 'object') return ''; + if (!args || typeof args !== 'object') {return '';} const entries = Object.entries(args as Record); - if (entries.length === 0) return ''; + if (entries.length === 0) {return '';} const parts = entries.map(([key, value]) => { if (typeof value === 'string') { const display = value.length > 50 ? value.slice(0, 47) + '...' : value; @@ -71,7 +71,7 @@ export function App({ // This replaces the process.stdout.write callback (which corrupts Ink rendering) // with one that updates React state to show tool activity in the streaming area. useEffect(() => { - if (!agent) return; + if (!agent) {return;} const handleToolEvent = (event: ToolUseEvent) => { if (event.type === 'start') { @@ -137,7 +137,7 @@ export function App({ const handleSubmit = useCallback(async (value: string) => { const command = parseCommand(value); - if (!command) return; + if (!command) {return;} setInput(''); @@ -212,7 +212,7 @@ export function App({ return; case 'transfer': { - const xferMsg: Message = { role: 'assistant', content: `Transfer not supported in fullscreen mode.` }; + const xferMsg: Message = { role: 'assistant', content: 'Transfer not supported in fullscreen mode.' }; const xferWithTs = session.addMessage(xferMsg); setMessages(prev => [...prev, xferWithTs]); return; @@ -222,7 +222,7 @@ export function App({ break; // Continue to message handling } - if (command.type !== 'message' || isStreaming) return; + if (command.type !== 'message' || isStreaming) {return;} // Add user message to UI (and session if no agent — agent adds it internally) const userMessage: Message = { role: 'user', content: command.content }; diff --git a/src/frontends/tui/components/InputBar.tsx b/src/frontends/tui/components/InputBar.tsx index 33d79cc..10b856b 100644 --- a/src/frontends/tui/components/InputBar.tsx +++ b/src/frontends/tui/components/InputBar.tsx @@ -19,15 +19,15 @@ export const InputBar = memo(function InputBar({ placeholder = 'Type a message...', }: InputBarProps): React.ReactElement { const completions = useMemo(() => { - if (!value.startsWith('/')) return []; + if (!value.startsWith('/')) {return [];} return getCommandCompletions(value); }, [value]); - + const tooltip = useMemo(() => { - if (!value.startsWith('/')) return null; + if (!value.startsWith('/')) {return null;} return getCommandTooltip(value); }, [value]); - + const showTooltip = value.startsWith('/') && (tooltip || completions.length > 1); return ( @@ -36,14 +36,14 @@ export const InputBar = memo(function InputBar({ {showTooltip && ( - {tooltip + {tooltip ? `→ ${tooltip}` : `${completions.length} commands (Tab)` } )} - + {/* Input bar */} {'> '} diff --git a/src/frontends/tui/components/MessageList.tsx b/src/frontends/tui/components/MessageList.tsx index 49dc892..57d4d9c 100644 --- a/src/frontends/tui/components/MessageList.tsx +++ b/src/frontends/tui/components/MessageList.tsx @@ -19,26 +19,26 @@ function formatTimestamp(timestamp: number): string { const minutes = Math.floor(seconds / 60); const hours = Math.floor(minutes / 60); const days = Math.floor(hours / 24); - - if (seconds < 60) return 'just now'; - if (minutes < 60) return `${minutes}m ago`; - if (hours < 24) return `${hours}h ago`; - if (days < 7) return `${days}d ago`; + + if (seconds < 60) {return 'just now';} + if (minutes < 60) {return `${minutes}m ago`;} + if (hours < 24) {return `${hours}h ago`;} + if (days < 7) {return `${days}d ago`;} return new Date(timestamp).toLocaleDateString([], { month: 'short', day: 'numeric' }); } // Individual message component -const MessageItem = memo(function MessageItem({ - message, - index -}: { - message: Message; +const MessageItem = memo(function MessageItem({ + message, + index, +}: { + message: Message; index: number; }): React.ReactElement { const isUser = message.role === 'user'; const accentColor = isUser ? 'blue' : '#ff8c00'; const timestampText = message.timestamp ? formatTimestamp(message.timestamp) : ''; - + return ( | {timestampText} - + {/* Content */} {message.role === 'assistant' diff --git a/src/frontends/tui/fullscreen.ts b/src/frontends/tui/fullscreen.ts index 0ffa108..5106e2c 100644 --- a/src/frontends/tui/fullscreen.ts +++ b/src/frontends/tui/fullscreen.ts @@ -31,7 +31,7 @@ export async function startFullscreenTui(config: FullscreenTuiConfig): Promise - this.parser.parseInline(cell.tokens) + this.parser.parseInline(cell.tokens), ); const rowTexts = rows.map((row: Tokens.TableCell[]) => - row.map((cell: Tokens.TableCell) => this.parser.parseInline(cell.tokens)) + row.map((cell: Tokens.TableCell) => this.parser.parseInline(cell.tokens)), ); // Calculate column widths (strip ANSI for measurement) @@ -108,8 +108,8 @@ const terminalRenderer: RendererObject = { const pad = (text: string, width: number, alignment: string | null) => { const visible = stripAnsi(text).length; const diff = width - visible; - if (diff <= 0) return text; - if (alignment === 'right') return ' '.repeat(diff) + text; + if (diff <= 0) {return text;} + if (alignment === 'right') {return ' '.repeat(diff) + text;} if (alignment === 'center') { const left = Math.floor(diff / 2); return ' '.repeat(left) + text + ' '.repeat(diff - left); @@ -119,7 +119,7 @@ const terminalRenderer: RendererObject = { // Build header row const headerRow = ' ' + headerTexts.map((h: string, i: number) => - `\x1b[1m${pad(h, colWidths[i], align[i])}\x1b[0m` + `\x1b[1m${pad(h, colWidths[i], align[i])}\x1b[0m`, ).join(' │ '); // Build separator @@ -128,8 +128,8 @@ const terminalRenderer: RendererObject = { // Build data rows const dataRows = rowTexts.map((row: string[]) => ' ' + row.map((cell: string, i: number) => - pad(cell, colWidths[i], align[i]) - ).join(' │ ') + pad(cell, colWidths[i], align[i]), + ).join(' │ '), ); return [headerRow, separator, ...dataRows].join('\n') + '\n'; diff --git a/src/frontends/tui/minimal.ts b/src/frontends/tui/minimal.ts index e0214b1..a3dc8d3 100644 --- a/src/frontends/tui/minimal.ts +++ b/src/frontends/tui/minimal.ts @@ -63,21 +63,21 @@ export class MinimalTui { const completions = getCommandCompletions(line); const tooltip = getCommandTooltip(line); - + let hint = ''; - + if (completions.length === 1 && completions[0] !== line) { // Show the remaining part of the completion as a hint hint = completions[0].slice(line.length); } - + // Add tooltip if available if (tooltip) { hint += ` ${colors.gray}— ${tooltip}${colors.reset}`; } else if (completions.length > 1) { hint += ` ${colors.gray}[${completions.length} options, Tab to complete]${colors.reset}`; } - + if (hint && hint !== this.currentHint) { this.clearHint(); this.currentHint = hint; @@ -467,7 +467,7 @@ export class MinimalTui { fullContent += event.content; } if (event.type === 'fallback_warning' && event.fallbackReason) { - console.warn(`\n⚠ Using fallback model`); + console.warn('\n⚠ Using fallback model'); } if (event.type === 'done' && event.usage) { this.totalUsage.inputTokens += event.usage.inputTokens; diff --git a/src/gateway/auth.ts b/src/gateway/auth.ts index 8fa1478..7388925 100644 --- a/src/gateway/auth.ts +++ b/src/gateway/auth.ts @@ -80,7 +80,7 @@ function extractQueryToken(req: IncomingMessage): string | undefined { } function extractTailscaleIdentity(req: IncomingMessage, config: AuthConfig): string | undefined { - if (!config.tailscaleIdentity) return undefined; + if (!config.tailscaleIdentity) {return undefined;} const header = req.headers['tailscale-user-login']; if (typeof header === 'string' && header.length > 0) { return header; diff --git a/src/gateway/handlers/config.ts b/src/gateway/handlers/config.ts index 0528af8..5ceb7ce 100644 --- a/src/gateway/handlers/config.ts +++ b/src/gateway/handlers/config.ts @@ -18,9 +18,9 @@ export function redactConfig(config: Config): Record { // Helper: redact specified keys on an object if they exist and are non-nullish const redact = (obj: Record | undefined, ...keys: string[]) => { - if (!obj) return; + if (!obj) {return;} for (const key of keys) { - if (obj[key] !== undefined && obj[key] !== null) obj[key] = '***'; + if (obj[key] !== undefined && obj[key] !== null) {obj[key] = '***';} } }; @@ -102,22 +102,22 @@ export function redactConfig(config: Config): Record { /** Keys that are safe to update at runtime via config.patch. */ const PATCHABLE_KEYS: Record boolean> = { 'hooks.confirm': (config, value) => { - if (!Array.isArray(value) || !value.every((v) => typeof v === 'string')) return false; + if (!Array.isArray(value) || !value.every((v) => typeof v === 'string')) {return false;} config.hooks.confirm = value as string[]; return true; }, 'hooks.log': (config, value) => { - if (!Array.isArray(value) || !value.every((v) => typeof v === 'string')) return false; + if (!Array.isArray(value) || !value.every((v) => typeof v === 'string')) {return false;} config.hooks.log = value as string[]; return true; }, 'hooks.silent': (config, value) => { - if (!Array.isArray(value) || !value.every((v) => typeof v === 'string')) return false; + if (!Array.isArray(value) || !value.every((v) => typeof v === 'string')) {return false;} config.hooks.silent = value as string[]; return true; }, 'server.localhost': (config, value) => { - if (typeof value !== 'boolean') return false; + if (typeof value !== 'boolean') {return false;} config.server.localhost = value; return true; }, diff --git a/src/gateway/handlers/sessions.ts b/src/gateway/handlers/sessions.ts index eab3c74..2e1b98e 100644 --- a/src/gateway/handlers/sessions.ts +++ b/src/gateway/handlers/sessions.ts @@ -16,7 +16,7 @@ export function createSessionHandlers(deps: SessionHandlerDeps) { id, messageCount: deps.sessionManager.getSession( id.split(':')[0], - id.split(':').slice(1).join(':') + id.split(':').slice(1).join(':'), ).getHistory().length, })); return makeResponse(request.id, { sessions }); diff --git a/src/gateway/lane-queue.ts b/src/gateway/lane-queue.ts index f9109da..f994519 100644 --- a/src/gateway/lane-queue.ts +++ b/src/gateway/lane-queue.ts @@ -83,7 +83,7 @@ export class LaneQueue { */ cancel(laneId: string): void { const lane = this.lanes.get(laneId); - if (!lane) return; + if (!lane) {return;} const pending = lane.queue.splice(0); for (const entry of pending) { @@ -102,7 +102,7 @@ export class LaneQueue { */ private processNext(laneId: string): void { const lane = this.lanes.get(laneId); - if (!lane) return; + if (!lane) {return;} const entry = lane.queue.shift(); if (!entry) { diff --git a/src/gateway/protocol.ts b/src/gateway/protocol.ts index 15c38b6..7f124b8 100644 --- a/src/gateway/protocol.ts +++ b/src/gateway/protocol.ts @@ -109,7 +109,7 @@ export type OutboundMessage = GatewayResponse | GatewayError | GatewayEvent; // ── Validation helpers ───────────────────────────────────────── export function isValidRequest(msg: unknown): msg is GatewayRequest { - if (typeof msg !== 'object' || msg === null) return false; + if (typeof msg !== 'object' || msg === null) {return false;} const obj = msg as Record; return ( typeof obj.id === 'number' && @@ -121,7 +121,7 @@ export function isValidRequest(msg: unknown): msg is GatewayRequest { export function parseMessage(raw: string): GatewayRequest | null { try { const parsed = JSON.parse(raw); - if (isValidRequest(parsed)) return parsed; + if (isValidRequest(parsed)) {return parsed;} return null; } catch { return null; diff --git a/src/gateway/server.ts b/src/gateway/server.ts index 4bf2a14..9a84d65 100644 --- a/src/gateway/server.ts +++ b/src/gateway/server.ts @@ -327,7 +327,7 @@ export class GatewayServer { if (uiDir) { const served = await serveStatic(req, res, uiDir); - if (served) return; + if (served) {return;} } // No UI directory configured, or file not found @@ -344,7 +344,7 @@ export class GatewayServer { } // Inject connectionId into params so handlers can identify the client - if (!request.params) request.params = {}; + if (!request.params) {request.params = {};} request.params.connectionId = connectionId; const send = (msg: OutboundMessage) => this.send(ws, msg); diff --git a/src/gateway/session-bridge.ts b/src/gateway/session-bridge.ts index 9e7c021..96c3817 100644 --- a/src/gateway/session-bridge.ts +++ b/src/gateway/session-bridge.ts @@ -83,8 +83,8 @@ export class SessionBridge { /** Switch a connection to a different session (e.g. resuming an old session). */ switchSession(connectionId: string, sessionId: string): void { const client = this.clients.get(connectionId); - if (!client) throw new Error(`Unknown connection: ${connectionId}`); - if (client.busy) throw new Error('Cannot switch session while agent is busy'); + if (!client) {throw new Error(`Unknown connection: ${connectionId}`);} + if (client.busy) {throw new Error('Cannot switch session while agent is busy');} const agent = this.getOrCreateAgent(sessionId); client.sessionId = sessionId; @@ -109,13 +109,13 @@ export class SessionBridge { /** Mark a connection's agent as busy/idle. */ setBusy(connectionId: string, busy: boolean): void { const client = this.clients.get(connectionId); - if (client) client.busy = busy; + if (client) {client.busy = busy;} } /** Set onToolUse callback for a connection's agent. */ setOnToolUse(connectionId: string, callback: ((event: ToolUseEvent) => void) | undefined): void { const client = this.clients.get(connectionId); - if (client) client.agent.setOnToolUse(callback); + if (client) {client.agent.setOnToolUse(callback);} } /** List all active sessions with connection counts. */ @@ -159,7 +159,7 @@ export class SessionBridge { const seen = new Set(); for (const client of this.clients.values()) { - if (seen.has(client.sessionId)) continue; + if (seen.has(client.sessionId)) {continue;} seen.add(client.sessionId); const usage = client.agent.getUsage(); diff --git a/src/gateway/ui/app.js b/src/gateway/ui/app.js index dc3acc7..53fa3b2 100644 --- a/src/gateway/ui/app.js +++ b/src/gateway/ui/app.js @@ -65,12 +65,12 @@ export function initStatusIndicator() { client.onStatusChange((status) => { statusEl.textContent = status === 'connected' ? 'Connected' : - status === 'connecting' ? 'Connecting...' : 'Disconnected'; + status === 'connecting' ? 'Connecting...' : 'Disconnected'; statusEl.className = `conn-status ${status}`; }); // Set initial status statusEl.textContent = client.status === 'connected' ? 'Connected' : - client.status === 'connecting' ? 'Connecting...' : 'Disconnected'; + client.status === 'connecting' ? 'Connecting...' : 'Disconnected'; statusEl.className = `conn-status ${client.status}`; } diff --git a/src/gateway/ui/lib/ws-client.js b/src/gateway/ui/lib/ws-client.js index a66ca38..d14231c 100644 --- a/src/gateway/ui/lib/ws-client.js +++ b/src/gateway/ui/lib/ws-client.js @@ -165,18 +165,18 @@ export class FlynnClient { const handle = { on(event, callback) { - if (!events.has(event)) events.set(event, []); + if (!events.has(event)) {events.set(event, []);} events.get(event).push(callback); return handle; }, result: new Promise((resolve, reject) => { // Auto-wire done/error to resolve/reject the promise - if (!events.has('done')) events.set('done', []); + if (!events.has('done')) {events.set('done', []);} events.get('done').push((data) => { this._listeners.delete(id); resolve(data); }); - if (!events.has('error')) events.set('error', []); + if (!events.has('error')) {events.set('error', []);} events.get('error').push((data) => { this._listeners.delete(id); reject(new Error(data.message || 'Agent error')); diff --git a/src/gateway/ui/pages/chat.js b/src/gateway/ui/pages/chat.js index c1b2f28..bd83b0a 100644 --- a/src/gateway/ui/pages/chat.js +++ b/src/gateway/ui/pages/chat.js @@ -133,7 +133,7 @@ function createMessageActions(role) { copyBtn.innerHTML = COPY_ICON; copyBtn.addEventListener('click', () => { const msg = bar.closest('.message'); - if (!msg) return; + if (!msg) {return;} const text = getMessageText(msg); navigator.clipboard.writeText(text).then(() => { copyBtn.innerHTML = CHECK_ICON; @@ -154,7 +154,7 @@ function createMessageActions(role) { editBtn.innerHTML = EDIT_ICON; editBtn.addEventListener('click', () => { const msg = bar.closest('.message'); - if (!msg) return; + if (!msg) {return;} const text = getMessageText(msg); const input = _elements.input; if (input) { @@ -177,7 +177,7 @@ function setSearchMode(active) { _searchMode = active; const btn = _elements.searchBtn; const input = _elements.input; - if (!btn || !input) return; + if (!btn || !input) {return;} if (active) { btn.classList.add('active'); @@ -198,7 +198,7 @@ function getFilteredCommands(text) { function showSlashPopup(filtered) { const popup = _elements.slashPopup; - if (!popup) return; + if (!popup) {return;} popup.innerHTML = ''; if (filtered.length === 0) { @@ -225,13 +225,13 @@ function showSlashPopup(filtered) { function hideSlashPopup() { const popup = _elements.slashPopup; - if (popup) popup.classList.add('hidden'); + if (popup) {popup.classList.add('hidden');} _slashPopupIndex = -1; } function updatePopupSelection(filtered) { const popup = _elements.slashPopup; - if (!popup) return; + if (!popup) {return;} const items = popup.querySelectorAll('.slash-popup-item'); items.forEach((el, i) => { el.classList.toggle('selected', i === _slashPopupIndex); @@ -240,7 +240,7 @@ function updatePopupSelection(filtered) { function selectSlashCommand(name) { const input = _elements.input; - if (!input) return; + if (!input) {return;} input.value = name; hideSlashPopup(); input.focus(); @@ -248,14 +248,14 @@ function selectSlashCommand(name) { function handleSlashPopupInput() { const input = _elements.input; - if (!input) return; + if (!input) {return;} const text = input.value; // Show popup only when text starts with / and is at most a single word (the command itself) if (text.startsWith('/') && !text.includes(' ')) { const filtered = getFilteredCommands(text); // Clamp selection index - if (_slashPopupIndex >= filtered.length) _slashPopupIndex = filtered.length - 1; + if (_slashPopupIndex >= filtered.length) {_slashPopupIndex = filtered.length - 1;} showSlashPopup(filtered); } else { hideSlashPopup(); @@ -266,7 +266,7 @@ function handleSlashPopupInput() { function parseSlashCommand(text) { const trimmed = text.trim(); - if (!trimmed.startsWith('/')) return null; + if (!trimmed.startsWith('/')) {return null;} const parts = trimmed.split(/\s+/); const cmd = parts[0].toLowerCase(); @@ -405,7 +405,7 @@ async function handleSlashCommand(cmd, client) { async function loadSessions(client) { const select = _elements.sessionSelect; - if (!select) return; + if (!select) {return;} try { const result = await client.call('sessions.list'); @@ -425,7 +425,7 @@ async function loadSessions(client) { const opt = document.createElement('option'); opt.value = s.id; opt.textContent = `${s.id} (${s.messageCount} msgs)`; - if (s.id === current) opt.selected = true; + if (s.id === current) {opt.selected = true;} select.appendChild(opt); } } @@ -439,7 +439,7 @@ async function loadSessions(client) { async function loadHistory(client) { const msgs = _elements.messages; - if (!msgs || !_currentSession) return; + if (!msgs || !_currentSession) {return;} msgs.innerHTML = ''; @@ -464,21 +464,21 @@ async function loadHistory(client) { async function sendMessage(client, overrideText) { const input = _elements.input; const rawText = overrideText ?? input?.value?.trim(); - if (!rawText || _sending) return; + if (!rawText || _sending) {return;} // Check for slash commands first const cmd = parseSlashCommand(rawText); if (cmd) { - if (!overrideText) input.value = ''; + if (!overrideText) {input.value = '';} hideSlashPopup(); const handled = await handleSlashCommand(cmd, client); - if (handled) return; + if (handled) {return;} // If not fully handled (e.g. /compact), fall through to send as message } _sending = true; _elements.sendBtn.disabled = true; - if (!overrideText) input.value = ''; + if (!overrideText) {input.value = '';} // Apply search mode prefix let messageText = rawText; @@ -541,17 +541,17 @@ async function sendMessage(client, overrideText) { placeholder.textContent = `Error: ${err.message}`; } finally { _sending = false; - if (_elements.sendBtn) _elements.sendBtn.disabled = false; + if (_elements.sendBtn) {_elements.sendBtn.disabled = false;} scrollToBottom(); } } // ── Search SVG Icon ───────────────────────────────────────── -const SEARCH_ICON = ``; -const COPY_ICON = ``; -const CHECK_ICON = ``; -const EDIT_ICON = ``; +const SEARCH_ICON = ''; +const COPY_ICON = ''; +const CHECK_ICON = ''; +const EDIT_ICON = ''; // ── Page Export ────────────────────────────────────────────── diff --git a/src/gateway/ui/pages/dashboard.js b/src/gateway/ui/pages/dashboard.js index b96c967..8165c0a 100644 --- a/src/gateway/ui/pages/dashboard.js +++ b/src/gateway/ui/pages/dashboard.js @@ -14,17 +14,17 @@ function formatUptime(seconds) { const m = Math.floor((seconds % 3600) / 60); const s = seconds % 60; const parts = []; - if (d > 0) parts.push(`${d}d`); - if (h > 0) parts.push(`${h}h`); - if (m > 0) parts.push(`${m}m`); + if (d > 0) {parts.push(`${d}d`);} + if (h > 0) {parts.push(`${h}h`);} + if (m > 0) {parts.push(`${m}m`);} parts.push(`${s}s`); return parts.join(' '); } function timeAgo(timestamp) { const secs = Math.floor((Date.now() - timestamp) / 1000); - if (secs < 60) return `${secs}s ago`; - if (secs < 3600) return `${Math.floor(secs / 60)}m ago`; + if (secs < 60) {return `${secs}s ago`;} + if (secs < 3600) {return `${Math.floor(secs / 60)}m ago`;} return `${Math.floor(secs / 3600)}h ago`; } @@ -76,7 +76,7 @@ function renderSkeleton(el) { function updateCounters(metrics, health) { const el = document.getElementById('ops-counters'); - if (!el) return; + if (!el) {return;} const sessions = health?.sessions ?? 0; const errCount = metrics?.errors ?? 0; @@ -94,13 +94,13 @@ function updateCounters(metrics, health) { `
${c.label}
${c.value}
-
` + `, ).join(''); } function updateModelTable(metrics) { const el = document.getElementById('ops-model-table'); - if (!el) return; + if (!el) {return;} const mc = metrics?.modelCalls; const calls = mc?.recentCalls ?? []; @@ -155,7 +155,7 @@ function updateModelTable(metrics) { function updateEvents(eventsData) { const el = document.getElementById('ops-events'); - if (!el) return; + if (!el) {return;} const events = eventsData?.events ?? []; @@ -180,7 +180,7 @@ function updateEvents(eventsData) { function updateActiveRequests(requestsData) { const el = document.getElementById('ops-requests'); - if (!el) return; + if (!el) {return;} const requests = requestsData?.requests ?? []; @@ -219,7 +219,7 @@ function updateActiveRequests(requestsData) { function updateChannels(channelsData) { const el = document.getElementById('ops-channels'); - if (!el) return; + if (!el) {return;} const channels = channelsData?.channels ?? []; @@ -232,7 +232,7 @@ function updateChannels(channelsData) { `
${escapeHtml(ch.name)} -
` + `, ).join(''); } diff --git a/src/gateway/ui/pages/sessions.js b/src/gateway/ui/pages/sessions.js index 7ef371f..72f74f2 100644 --- a/src/gateway/ui/pages/sessions.js +++ b/src/gateway/ui/pages/sessions.js @@ -14,11 +14,11 @@ let _client = null; let _el = null; async function loadSessionList() { - if (!_client || !_el) return; + if (!_client || !_el) {return;} const listContainer = _el.querySelector('#sessions-list'); const detailContainer = _el.querySelector('#session-detail'); - if (detailContainer) detailContainer.innerHTML = ''; + if (detailContainer) {detailContainer.innerHTML = '';} try { const result = await _client.call('sessions.list'); @@ -78,7 +78,7 @@ async function loadSessionList() { async function viewSession(sessionId) { const detailContainer = _el.querySelector('#session-detail'); - if (!detailContainer) return; + if (!detailContainer) {return;} detailContainer.innerHTML = '
Loading...
'; diff --git a/src/gateway/ui/pages/settings.js b/src/gateway/ui/pages/settings.js index e11f98a..22584eb 100644 --- a/src/gateway/ui/pages/settings.js +++ b/src/gateway/ui/pages/settings.js @@ -15,7 +15,7 @@ let _client = null; let _el = null; async function loadSettings() { - if (!_client || !_el) return; + if (!_client || !_el) {return;} let config, tools, channels; @@ -154,7 +154,7 @@ async function saveHooks() { // Clear status after 5s setTimeout(() => { - if (status) status.textContent = ''; + if (status) {status.textContent = '';} }, 5000); } diff --git a/src/gateway/ui/pages/usage.js b/src/gateway/ui/pages/usage.js index 88ae6e8..5b4d743 100644 --- a/src/gateway/ui/pages/usage.js +++ b/src/gateway/ui/pages/usage.js @@ -13,14 +13,14 @@ function formatNumber(n) { } function formatCost(n) { - if (!n || n === 0) return '$0.00'; - if (n < 0.01) return `$${n.toFixed(4)}`; + if (!n || n === 0) {return '$0.00';} + if (n < 0.01) {return `$${n.toFixed(4)}`;} return `$${n.toFixed(2)}`; } function truncateId(id) { - if (!id) return '-'; - if (id.length <= 24) return id; + if (!id) {return '-';} + if (id.length <= 24) {return id;} return id.slice(0, 24) + '\u2026'; } @@ -95,7 +95,7 @@ async function loadUsage(el, client) { let delegationCell = '-'; if (delegationEntries.length > 0) { delegationCell = delegationEntries.map(([tier, stats]) => - `${tier} ${formatNumber(stats.inputTokens)}/${formatNumber(stats.outputTokens)}` + `${tier} ${formatNumber(stats.inputTokens)}/${formatNumber(stats.outputTokens)}`, ).join('
'); } diff --git a/src/logger.ts b/src/logger.ts index 1fa1424..1f4d123 100644 --- a/src/logger.ts +++ b/src/logger.ts @@ -30,15 +30,15 @@ function shouldLog(level: LogLevel): boolean { export const logger = { debug(...args: unknown[]): void { - if (shouldLog('debug')) console.debug(...args); + if (shouldLog('debug')) {console.debug(...args);} }, info(...args: unknown[]): void { - if (shouldLog('info')) console.log(...args); + if (shouldLog('info')) {console.log(...args);} }, warn(...args: unknown[]): void { - if (shouldLog('warn')) console.warn(...args); + if (shouldLog('warn')) {console.warn(...args);} }, error(...args: unknown[]): void { - if (shouldLog('error')) console.error(...args); + if (shouldLog('error')) {console.error(...args);} }, }; diff --git a/src/mcp/bridge.ts b/src/mcp/bridge.ts index e98a4ea..f70fab2 100644 --- a/src/mcp/bridge.ts +++ b/src/mcp/bridge.ts @@ -25,7 +25,7 @@ export function mcpToolName(serverName: string, toolName: string): string { */ export function parseMcpToolName(prefixedName: string): { serverName: string; toolName: string } | null { const match = prefixedName.match(/^mcp:([^:]+):(.+)$/); - if (!match) return null; + if (!match) {return null;} return { serverName: match[1], toolName: match[2] }; } diff --git a/src/mcp/manager.ts b/src/mcp/manager.ts index 57a2025..edbeb09 100644 --- a/src/mcp/manager.ts +++ b/src/mcp/manager.ts @@ -86,7 +86,7 @@ export class McpManager { */ async stopServer(name: string): Promise { const client = this.clients.get(name); - if (!client) return; + if (!client) {return;} // Unregister tools from the registry const toolNames = this.registeredToolNames.get(name) ?? []; @@ -133,7 +133,7 @@ export class McpManager { */ getServerState(name: string): McpServerState | undefined { const client = this.clients.get(name); - if (!client) return undefined; + if (!client) {return undefined;} const config = this.storedConfigs.get(name) ?? { name, command: '', args: [] }; @@ -165,7 +165,7 @@ export class McpManager { for (const toolNames of this.registeredToolNames.values()) { for (const name of toolNames) { const tool = this.toolRegistry.get(name); - if (tool) tools.push(tool); + if (tool) {tools.push(tool);} } } return tools; diff --git a/src/memory/vector-store.ts b/src/memory/vector-store.ts index d051945..6832899 100644 --- a/src/memory/vector-store.ts +++ b/src/memory/vector-store.ts @@ -52,7 +52,7 @@ export function cosineSimilarity(a: number[] | Float32Array, b: number[] | Float } const magnitude = Math.sqrt(normA) * Math.sqrt(normB); - if (magnitude === 0) return 0; + if (magnitude === 0) {return 0;} return dotProduct / magnitude; } @@ -129,7 +129,7 @@ export class VectorStore { throw new Error(`Chunks/embeddings length mismatch: ${chunks.length} vs ${embeddings.length}`); } - if (chunks.length === 0) return; + if (chunks.length === 0) {return;} const namespace = chunks[0].namespace; diff --git a/src/models/bedrock.ts b/src/models/bedrock.ts index 0f1001f..a15825b 100644 --- a/src/models/bedrock.ts +++ b/src/models/bedrock.ts @@ -89,10 +89,10 @@ export class BedrockClient implements ModelClient { // Map stop reason let stopReason: string = 'end_turn'; - if (response.stopReason === 'max_tokens') stopReason = 'max_tokens'; - else if (response.stopReason === 'tool_use') stopReason = 'tool_use'; - else if (response.stopReason === 'end_turn') stopReason = 'end_turn'; - else if (response.stopReason) stopReason = response.stopReason; + if (response.stopReason === 'max_tokens') {stopReason = 'max_tokens';} + else if (response.stopReason === 'tool_use') {stopReason = 'tool_use';} + else if (response.stopReason === 'end_turn') {stopReason = 'end_turn';} + else if (response.stopReason) {stopReason = response.stopReason;} return { content, diff --git a/src/models/gemini.test.ts b/src/models/gemini.test.ts index c12bc2e..793f905 100644 --- a/src/models/gemini.test.ts +++ b/src/models/gemini.test.ts @@ -27,12 +27,12 @@ function makeResponse(parts: unknown[], finishReason = 'STOP', usage = { promptT usageMetadata: usage, text: () => { const textParts = parts.filter((p: unknown) => typeof p === 'object' && p !== null && 'text' in p); - if (textParts.length === 0) throw new Error('No text parts'); + if (textParts.length === 0) {throw new Error('No text parts');} return textParts.map((p: unknown) => (p as { text: string }).text).join(''); }, functionCalls: () => { const fcParts = parts.filter((p: unknown) => typeof p === 'object' && p !== null && 'functionCall' in p); - if (fcParts.length === 0) return undefined; + if (fcParts.length === 0) {return undefined;} return fcParts.map((p: unknown) => (p as { functionCall: { name: string; args: object } }).functionCall); }, }, diff --git a/src/models/github.ts b/src/models/github.ts index d656665..38ffeab 100644 --- a/src/models/github.ts +++ b/src/models/github.ts @@ -72,7 +72,7 @@ export class GitHubModelsClient implements ModelClient { * callback is provided, invoke it to obtain a token (e.g. via OAuth device flow). */ private async ensureToken(): Promise { - if (this.tokenResolved) return; + if (this.tokenResolved) {return;} // Try resolving again (user might have logged in via /login since construction) const token = getGitHubToken(); @@ -85,7 +85,7 @@ export class GitHubModelsClient implements ModelClient { if (this.onLoginRequired) { const newToken = await this.onLoginRequired(); this.rebuildClient(newToken); - return; + } // No token and no callback — the API call will fail with an auth error diff --git a/src/models/local/llamacpp.ts b/src/models/local/llamacpp.ts index f4103c1..7415590 100644 --- a/src/models/local/llamacpp.ts +++ b/src/models/local/llamacpp.ts @@ -115,7 +115,7 @@ export function normalizeMessagesForLlamaCpp( } else if (block.type === 'tool_use') { const name = block.name as string; const id = block.id as string; - if (id) toolNameMap.set(id, name); + if (id) {toolNameMap.set(id, name);} let argsStr: string; try { argsStr = JSON.stringify(block.input); @@ -321,7 +321,7 @@ export class LlamaCppClient implements ModelClient { while (true) { const { done, value } = await reader.read(); - if (done) break; + if (done) {break;} buffer += decoder.decode(value, { stream: true }); const lines = buffer.split('\n'); @@ -329,10 +329,10 @@ export class LlamaCppClient implements ModelClient { for (const line of lines) { const trimmed = line.trim(); - if (!trimmed || !trimmed.startsWith('data: ')) continue; + if (!trimmed || !trimmed.startsWith('data: ')) {continue;} const data = trimmed.slice(6); - if (data === '[DONE]') continue; + if (data === '[DONE]') {continue;} try { const chunk = JSON.parse(data) as LlamaCppStreamChunk; @@ -352,8 +352,8 @@ export class LlamaCppClient implements ModelClient { }); } const acc = toolCallAccumulators.get(tc.index)!; - if (tc.function?.name) acc.name = tc.function.name; - if (tc.function?.arguments) acc.arguments += tc.function.arguments; + if (tc.function?.name) {acc.name = tc.function.name;} + if (tc.function?.arguments) {acc.arguments += tc.function.arguments;} } } diff --git a/src/models/local/ollama.ts b/src/models/local/ollama.ts index 757a2f1..ceb18aa 100644 --- a/src/models/local/ollama.ts +++ b/src/models/local/ollama.ts @@ -55,7 +55,7 @@ export function normalizeMessagesForOllama( } else if (block.type === 'tool_use') { const name = block.name as string; const id = block.id as string; - if (id) toolNameMap.set(id, name); + if (id) {toolNameMap.set(id, name);} toolCalls.push({ function: { name, @@ -135,7 +135,7 @@ export class OllamaClient implements ModelClient { * true (optimistic — let the server decide). */ private async checkToolSupport(): Promise { - if (this._supportsTools !== null) return this._supportsTools; + if (this._supportsTools !== null) {return this._supportsTools;} try { const info = await this.client.show({ model: this.model }); const caps: string[] = (info as any).capabilities ?? []; diff --git a/src/models/media.ts b/src/models/media.ts index 9aac52e..888d079 100644 --- a/src/models/media.ts +++ b/src/models/media.ts @@ -178,7 +178,7 @@ export function normalizeMessagesForLocal( for (const msg of messages) { const text = getMessageTextWithTools(msg); - if (!text) continue; // drop empty messages + if (!text) {continue;} // drop empty messages const last = result.length > 0 ? result[result.length - 1] : undefined; if (last && last.role === msg.role) { diff --git a/src/models/retry.test.ts b/src/models/retry.test.ts index b75cba9..c00a52c 100644 --- a/src/models/retry.test.ts +++ b/src/models/retry.test.ts @@ -134,7 +134,7 @@ describe('withRetry', () => { let callCount = 0; const fn = vi.fn().mockImplementation(() => { callCount++; - if (callCount < 3) return Promise.reject(new Error('fail')); + if (callCount < 3) {return Promise.reject(new Error('fail'));} return Promise.resolve('ok'); }); @@ -166,7 +166,7 @@ describe('withRetry', () => { const fn = vi.fn().mockImplementation(() => { timestamps.push(Date.now()); - if (timestamps.length < 3) return Promise.reject(new Error('fail')); + if (timestamps.length < 3) {return Promise.reject(new Error('fail'));} return Promise.resolve('ok'); }); diff --git a/src/models/router.test.ts b/src/models/router.test.ts index 4afa1dd..7baad40 100644 --- a/src/models/router.test.ts +++ b/src/models/router.test.ts @@ -72,7 +72,7 @@ describe('ModelRouter', () => { const response = await router.chat( { messages: [{ role: 'user', content: 'Hi' }] }, - 'fast' + 'fast', ); expect(response.content).toBe('Response from fast'); diff --git a/src/models/router.ts b/src/models/router.ts index 392c93b..a6d0e2e 100644 --- a/src/models/router.ts +++ b/src/models/router.ts @@ -41,9 +41,9 @@ export class ModelRouter implements ModelClient { } this.clients.set('default', config.default); - if (config.fast) this.clients.set('fast', config.fast); - if (config.complex) this.clients.set('complex', config.complex); - if (config.local) this.clients.set('local', config.local); + if (config.fast) {this.clients.set('fast', config.fast);} + if (config.complex) {this.clients.set('complex', config.complex);} + if (config.local) {this.clients.set('local', config.local);} if (config.labels) { for (const tier of ['fast', 'default', 'complex', 'local'] as ModelTier[]) { @@ -145,7 +145,7 @@ export class ModelRouter implements ModelClient { yield event; } - if (!hasError) return; + if (!hasError) {return;} } else { primaryError = 'Primary client does not support streaming'; } @@ -154,7 +154,7 @@ export class ModelRouter implements ModelClient { const tierFallbackList = this.tierFallbacks.get(useTier) ?? []; for (let i = 0; i < tierFallbackList.length; i++) { const fallbackClient = tierFallbackList[i]; - if (!fallbackClient.chatStream) continue; + if (!fallbackClient.chatStream) {continue;} const reason = `Primary model failed (${primaryError}), using tier fallback #${i + 1}`; logger.debug(reason); @@ -170,13 +170,13 @@ export class ModelRouter implements ModelClient { yield event; } - if (!hasError) return; + if (!hasError) {return;} } // Then try global fallback chain for (let i = 0; i < this.fallbackChain.length; i++) { const fallbackClient = this.fallbackChain[i]; - if (!fallbackClient.chatStream) continue; + if (!fallbackClient.chatStream) {continue;} const reason = `Primary model failed (${primaryError}), using global fallback #${i + 1}`; logger.debug(reason); @@ -192,7 +192,7 @@ export class ModelRouter implements ModelClient { yield event; } - if (!hasError) return; + if (!hasError) {return;} } yield { type: 'error', error: new Error('All streaming providers failed') }; diff --git a/src/sandbox/docker.ts b/src/sandbox/docker.ts index 03367b8..0dc5513 100644 --- a/src/sandbox/docker.ts +++ b/src/sandbox/docker.ts @@ -81,7 +81,7 @@ export class DockerSandbox { /** Force-remove the container. */ async destroy(): Promise { - if (!this._containerId) return; + if (!this._containerId) {return;} try { await this.dockerCmd(['rm', '-f', this._containerId]); @@ -98,8 +98,8 @@ export class DockerSandbox { execFile('docker', ['version', '--format', '{{.Server.Version}}'], { timeout: 5000, }, (error, stdout) => { - if (error) reject(error); - else resolve(stdout); + if (error) {reject(error);} + else {resolve(stdout);} }); }); return true; diff --git a/src/sandbox/manager.ts b/src/sandbox/manager.ts index 9af9b01..679f376 100644 --- a/src/sandbox/manager.ts +++ b/src/sandbox/manager.ts @@ -16,7 +16,7 @@ export class SandboxManager { /** Get or create a sandbox for a session. */ async getOrCreate(sessionId: string): Promise { let sandbox = this.sandboxes.get(sessionId); - if (sandbox) return sandbox; + if (sandbox) {return sandbox;} sandbox = new DockerSandbox({ sessionId, @@ -36,7 +36,7 @@ export class SandboxManager { /** Destroy a specific session's sandbox. */ async destroy(sessionId: string): Promise { const sandbox = this.sandboxes.get(sessionId); - if (!sandbox) return; + if (!sandbox) {return;} await sandbox.destroy(); this.sandboxes.delete(sessionId); diff --git a/src/session/manager.ts b/src/session/manager.ts index 23ec368..3b8aad3 100644 --- a/src/session/manager.ts +++ b/src/session/manager.ts @@ -13,7 +13,7 @@ export class ManagedSession implements Session { constructor( public readonly id: string, private store: SessionStore, - private history: Message[] = [] + private history: Message[] = [], ) {} addMessage(message: Message): Message { @@ -76,7 +76,7 @@ export class SessionManager { fromFrontend: string, fromUserId: string, toFrontend: string, - toUserId: string + toUserId: string, ): void { const fromSession = this.getSession(fromFrontend, fromUserId); const toSession = this.getSession(toFrontend, toUserId); diff --git a/src/session/store.ts b/src/session/store.ts index f756924..8a4e812 100644 --- a/src/session/store.ts +++ b/src/session/store.ts @@ -4,9 +4,9 @@ import type { PairingStore, ApprovedSender } from '../channels/pairing.js'; /** Parse a duration string like '30d', '7d', '12h' to milliseconds. Returns null if invalid or '0'. */ export function parseDuration(s: string): number | null { - if (s === '0' || s === 'false') return null; + if (s === '0' || s === 'false') {return null;} const match = s.match(/^(\d+)(h|d)$/); - if (!match) return null; + if (!match) {return null;} const [, n, unit] = match; return unit === 'h' ? Number(n) * 3600_000 : Number(n) * 86_400_000; } @@ -41,14 +41,14 @@ export class SessionStore { addMessage(sessionId: string, message: Message): void { const stmt = this.db.prepare( - 'INSERT INTO messages (session_id, role, content) VALUES (?, ?, ?)' + 'INSERT INTO messages (session_id, role, content) VALUES (?, ?, ?)', ); stmt.run(sessionId, message.role, message.content); } getMessages(sessionId: string): Message[] { const stmt = this.db.prepare( - 'SELECT role, content FROM messages WHERE session_id = ? ORDER BY id ASC' + 'SELECT role, content FROM messages WHERE session_id = ? ORDER BY id ASC', ); const rows = stmt.all(sessionId) as Array<{ role: string; content: string }>; return rows.map(row => ({ @@ -68,7 +68,7 @@ export class SessionStore { this.db.prepare('DELETE FROM messages WHERE session_id = ?').run(sessionId); // Re-insert in order const insert = this.db.prepare( - 'INSERT INTO messages (session_id, role, content) VALUES (?, ?, ?)' + 'INSERT INTO messages (session_id, role, content) VALUES (?, ?, ?)', ); for (const msg of messages) { insert.run(sessionId, msg.role, msg.content); @@ -96,7 +96,7 @@ export class SessionStore { HAVING MAX(created_at) < ? `).all(beforeTimestamp) as Array<{ session_id: string }>; - if (stale.length === 0) return []; + if (stale.length === 0) {return [];} const deleteStmt = this.db.prepare('DELETE FROM messages WHERE session_id = ?'); const transaction = this.db.transaction(() => { @@ -113,7 +113,7 @@ export class SessionStore { return { loadApproved: (): ApprovedSender[] => { const rows = this.db.prepare( - 'SELECT channel, sender_id, approved_at, code_used FROM pairing_approved' + 'SELECT channel, sender_id, approved_at, code_used FROM pairing_approved', ).all() as Array<{ channel: string; sender_id: string; approved_at: number; code_used: string }>; return rows.map(r => ({ channel: r.channel, @@ -130,7 +130,7 @@ export class SessionStore { }, removeApproved: (channel: string, senderId: string): void => { this.db.prepare( - 'DELETE FROM pairing_approved WHERE channel = ? AND sender_id = ?' + 'DELETE FROM pairing_approved WHERE channel = ? AND sender_id = ?', ).run(channel, senderId); }, }; diff --git a/src/skills/installer.ts b/src/skills/installer.ts index ad5e353..3157726 100644 --- a/src/skills/installer.ts +++ b/src/skills/installer.ts @@ -96,7 +96,7 @@ export class SkillInstaller { const entries = readdirSync(this.managedDir, { withFileTypes: true }); return entries .filter(entry => { - if (!entry.isDirectory()) return false; + if (!entry.isDirectory()) {return false;} const skillMd = resolve(this.managedDir, entry.name, 'SKILL.md'); return existsSync(skillMd); }) diff --git a/src/skills/registry.ts b/src/skills/registry.ts index 47c760f..1ac9760 100644 --- a/src/skills/registry.ts +++ b/src/skills/registry.ts @@ -13,7 +13,7 @@ export class SkillRegistry { register(skill: Skill): void { this.skills.set(skill.manifest.name, skill); console.log( - `Skill '${skill.manifest.name}' registered (${skill.manifest.tier}, ${skill.available ? 'available' : 'unavailable'})` + `Skill '${skill.manifest.name}' registered (${skill.manifest.tier}, ${skill.available ? 'available' : 'unavailable'})`, ); } diff --git a/src/tools/builtin/agents-list.ts b/src/tools/builtin/agents-list.ts index ec0b220..cb210c9 100644 --- a/src/tools/builtin/agents-list.ts +++ b/src/tools/builtin/agents-list.ts @@ -27,9 +27,9 @@ export function createAgentsListTool(registry: AgentConfigRegistry): Tool { const lines = configs.map((c) => { const parts = [`- **${c.name}**`]; - if (c.modelTier) parts.push(`tier=${c.modelTier}`); - if (c.toolProfile) parts.push(`profile=${c.toolProfile}`); - if (c.sandbox) parts.push('sandboxed'); + if (c.modelTier) {parts.push(`tier=${c.modelTier}`);} + if (c.toolProfile) {parts.push(`profile=${c.toolProfile}`);} + if (c.sandbox) {parts.push('sandboxed');} if (c.systemPrompt) { const preview = c.systemPrompt.slice(0, 80).replace(/\n/g, ' '); parts.push(`prompt="${preview}${c.systemPrompt.length > 80 ? '...' : ''}"`); diff --git a/src/tools/builtin/browser/manager.ts b/src/tools/builtin/browser/manager.ts index 25d5da7..ad1e02e 100644 --- a/src/tools/builtin/browser/manager.ts +++ b/src/tools/builtin/browser/manager.ts @@ -31,7 +31,7 @@ function findChrome(): string { ]; for (const candidate of candidates) { - if (existsSync(candidate)) return candidate; + if (existsSync(candidate)) {return candidate;} } throw new Error('Chrome/Chromium not found. Set browser.executable_path in config or install Chrome.'); @@ -120,7 +120,7 @@ export class BrowserManager { async shutdown(): Promise { for (const [, page] of this.pages) { try { - if (!page.isClosed()) await page.close(); + if (!page.isClosed()) {await page.close();} } catch { /* ignore */ } } this.pages.clear(); diff --git a/src/tools/builtin/cron.ts b/src/tools/builtin/cron.ts index 21b21a4..b5c760e 100644 --- a/src/tools/builtin/cron.ts +++ b/src/tools/builtin/cron.ts @@ -23,7 +23,7 @@ export function createCronTools(scheduler: CronScheduler): Tool[] { const lines = jobNames.map((name) => { const job = scheduler.getJob(name); - if (!job) return `- ${name}`; + if (!job) {return `- ${name}`;} return `- **${name}** — schedule: \`${job.schedule}\`, enabled: ${job.enabled}, output: ${job.output.channel}/${job.output.peer}\n message: "${job.message.length > 80 ? job.message.slice(0, 80) + '...' : job.message}"`; }); return { diff --git a/src/tools/builtin/gcal.test.ts b/src/tools/builtin/gcal.test.ts index 2714432..691b36f 100644 --- a/src/tools/builtin/gcal.test.ts +++ b/src/tools/builtin/gcal.test.ts @@ -62,8 +62,8 @@ function setupValidAuth() { mockExistsSync.mockReturnValue(true); mockReadFileSync.mockImplementation((path: unknown) => { const p = String(path); - if (p.includes('creds')) return JSON.stringify(fakeCredentials); - if (p.includes('token')) return JSON.stringify(fakeToken); + if (p.includes('creds')) {return JSON.stringify(fakeCredentials);} + if (p.includes('token')) {return JSON.stringify(fakeToken);} return ''; }); } diff --git a/src/tools/builtin/gcal.ts b/src/tools/builtin/gcal.ts index 329a43f..39145f2 100644 --- a/src/tools/builtin/gcal.ts +++ b/src/tools/builtin/gcal.ts @@ -101,9 +101,9 @@ function formatEvents(events: EventSummary[]): string { return events .map(e => { const parts = [`[${e.id}] ${e.summary}`, ` Time: ${e.start} — ${e.end}`]; - if (e.location) parts.push(` Location: ${e.location}`); - if (e.attendees.length > 0) parts.push(` Attendees: ${e.attendees.join(', ')}`); - if (e.htmlLink) parts.push(` Link: ${e.htmlLink}`); + if (e.location) {parts.push(` Location: ${e.location}`);} + if (e.attendees.length > 0) {parts.push(` Attendees: ${e.attendees.join(', ')}`);} + if (e.htmlLink) {parts.push(` Link: ${e.htmlLink}`);} return parts.join('\n'); }) .join('\n\n'); diff --git a/src/tools/builtin/gdocs.test.ts b/src/tools/builtin/gdocs.test.ts index ae52e07..0e8c94b 100644 --- a/src/tools/builtin/gdocs.test.ts +++ b/src/tools/builtin/gdocs.test.ts @@ -67,8 +67,8 @@ function setupValidAuth() { mockExistsSync.mockReturnValue(true); mockReadFileSync.mockImplementation((path: unknown) => { const p = String(path); - if (p.includes('creds')) return JSON.stringify(fakeCredentials); - if (p.includes('token')) return JSON.stringify(fakeToken); + if (p.includes('creds')) {return JSON.stringify(fakeCredentials);} + if (p.includes('token')) {return JSON.stringify(fakeToken);} return ''; }); } diff --git a/src/tools/builtin/gdocs.ts b/src/tools/builtin/gdocs.ts index 349381d..d55867d 100644 --- a/src/tools/builtin/gdocs.ts +++ b/src/tools/builtin/gdocs.ts @@ -67,8 +67,8 @@ function formatDocs(docs: DocSummary[]): string { .map(d => { const parts = [`[${d.id}] ${d.name}`]; parts.push(` Modified: ${d.modifiedTime}`); - if (d.owners.length > 0) parts.push(` Owners: ${d.owners.join(', ')}`); - if (d.webViewLink) parts.push(` Link: ${d.webViewLink}`); + if (d.owners.length > 0) {parts.push(` Owners: ${d.owners.join(', ')}`);} + if (d.webViewLink) {parts.push(` Link: ${d.webViewLink}`);} return parts.join('\n'); }) .join('\n\n'); @@ -76,7 +76,7 @@ function formatDocs(docs: DocSummary[]): string { /** Extract plain text from a Google Docs document body. */ function extractPlainText(body: import('googleapis').docs_v1.Schema$Body): string { - if (!body.content) return ''; + if (!body.content) {return '';} const parts: string[] = []; for (const structural of body.content) { diff --git a/src/tools/builtin/gdrive.test.ts b/src/tools/builtin/gdrive.test.ts index 05b45f8..2ffe190 100644 --- a/src/tools/builtin/gdrive.test.ts +++ b/src/tools/builtin/gdrive.test.ts @@ -65,8 +65,8 @@ function setupValidAuth() { mockExistsSync.mockReturnValue(true); mockReadFileSync.mockImplementation((path: unknown) => { const p = String(path); - if (p.includes('creds')) return JSON.stringify(fakeCredentials); - if (p.includes('token')) return JSON.stringify(fakeToken); + if (p.includes('creds')) {return JSON.stringify(fakeCredentials);} + if (p.includes('token')) {return JSON.stringify(fakeToken);} return ''; }); } diff --git a/src/tools/builtin/gdrive.ts b/src/tools/builtin/gdrive.ts index ca0f9bb..2aa5914 100644 --- a/src/tools/builtin/gdrive.ts +++ b/src/tools/builtin/gdrive.ts @@ -75,11 +75,11 @@ function friendlyMimeType(mimeType: string): string { /** Format file size in human-readable form. */ function formatSize(bytes: string | undefined): string { - if (!bytes) return ''; + if (!bytes) {return '';} const n = parseInt(bytes, 10); - if (isNaN(n)) return ''; - if (n < 1024) return `${n} B`; - if (n < 1024 * 1024) return `${(n / 1024).toFixed(1)} KB`; + if (isNaN(n)) {return '';} + if (n < 1024) {return `${n} B`;} + if (n < 1024 * 1024) {return `${(n / 1024).toFixed(1)} KB`;} return `${(n / (1024 * 1024)).toFixed(1)} MB`; } @@ -95,9 +95,9 @@ function formatFiles(files: FileSummary[]): string { parts.push(` Type: ${friendlyMimeType(f.mimeType)}`); parts.push(` Modified: ${f.modifiedTime}`); const size = formatSize(f.size); - if (size) parts.push(` Size: ${size}`); - if (f.owners.length > 0) parts.push(` Owners: ${f.owners.join(', ')}`); - if (f.webViewLink) parts.push(` Link: ${f.webViewLink}`); + if (size) {parts.push(` Size: ${size}`);} + if (f.owners.length > 0) {parts.push(` Owners: ${f.owners.join(', ')}`);} + if (f.webViewLink) {parts.push(` Link: ${f.webViewLink}`);} return parts.join('\n'); }) .join('\n\n'); diff --git a/src/tools/builtin/gmail.test.ts b/src/tools/builtin/gmail.test.ts index 68aa2b6..a5f3472 100644 --- a/src/tools/builtin/gmail.test.ts +++ b/src/tools/builtin/gmail.test.ts @@ -69,8 +69,8 @@ function setupValidAuth() { mockExistsSync.mockReturnValue(true); mockReadFileSync.mockImplementation((path: unknown) => { const p = String(path); - if (p.includes('creds')) return JSON.stringify(fakeCredentials); - if (p.includes('token')) return JSON.stringify(fakeToken); + if (p.includes('creds')) {return JSON.stringify(fakeCredentials);} + if (p.includes('token')) {return JSON.stringify(fakeToken);} return ''; }); } diff --git a/src/tools/builtin/gmail.ts b/src/tools/builtin/gmail.ts index 1ad9b69..321d55b 100644 --- a/src/tools/builtin/gmail.ts +++ b/src/tools/builtin/gmail.ts @@ -120,7 +120,7 @@ function extractTextBody(payload: { // Recurse into nested multipart if (part.parts) { const nested = extractTextBody(part as typeof payload); - if (nested) return nested; + if (nested) {return nested;} } } if (htmlFallback) { @@ -184,9 +184,9 @@ export function createGmailTools(config: NonNullable): Tool[] { const emails: EmailSummary[] = []; for (const msg of messages) { - if (!msg.id) continue; + if (!msg.id) {continue;} const details = await fetchMessageDetails(gmail, msg.id); - if (details) emails.push(details); + if (details) {emails.push(details);} } return { @@ -239,9 +239,9 @@ export function createGmailTools(config: NonNullable): Tool[] { const emails: EmailSummary[] = []; for (const msg of messages) { - if (!msg.id) continue; + if (!msg.id) {continue;} const details = await fetchMessageDetails(gmail, msg.id); - if (details) emails.push(details); + if (details) {emails.push(details);} } return { diff --git a/src/tools/builtin/gtasks.test.ts b/src/tools/builtin/gtasks.test.ts index c417a6c..416e458 100644 --- a/src/tools/builtin/gtasks.test.ts +++ b/src/tools/builtin/gtasks.test.ts @@ -65,8 +65,8 @@ function setupValidAuth() { mockExistsSync.mockReturnValue(true); mockReadFileSync.mockImplementation((path: unknown) => { const p = String(path); - if (p.includes('creds')) return JSON.stringify(fakeCredentials); - if (p.includes('token')) return JSON.stringify(fakeToken); + if (p.includes('creds')) {return JSON.stringify(fakeCredentials);} + if (p.includes('token')) {return JSON.stringify(fakeToken);} return ''; }); } diff --git a/src/tools/builtin/gtasks.ts b/src/tools/builtin/gtasks.ts index 209b211..5266c38 100644 --- a/src/tools/builtin/gtasks.ts +++ b/src/tools/builtin/gtasks.ts @@ -86,8 +86,8 @@ function formatTasks(tasks: TaskSummary[]): string { .map(t => { const checkbox = t.status === 'completed' ? '[x]' : '[ ]'; const parts = [`${checkbox} ${t.title}`]; - if (t.due) parts.push(` Due: ${t.due}`); - if (t.notes) parts.push(` Notes: ${t.notes}`); + if (t.due) {parts.push(` Due: ${t.due}`);} + if (t.notes) {parts.push(` Notes: ${t.notes}`);} parts.push(` ID: ${t.id}`); return parts.join('\n'); }) diff --git a/src/tools/builtin/image-analyze.test.ts b/src/tools/builtin/image-analyze.test.ts index 62f80a1..08d7abf 100644 --- a/src/tools/builtin/image-analyze.test.ts +++ b/src/tools/builtin/image-analyze.test.ts @@ -7,7 +7,7 @@ describe('image.analyze tool', () => { beforeEach(() => { mockClient = { - chat: vi.fn() + chat: vi.fn(), }; }); @@ -25,7 +25,7 @@ describe('image.analyze tool', () => { mockClient.chat = vi.fn().mockResolvedValueOnce({ content: 'This is a beautiful sunset over the ocean.', stopReason: 'end_turn', - usage: { inputTokens: 100, outputTokens: 50 } + usage: { inputTokens: 100, outputTokens: 50 }, }); const tool = createImageAnalyzeTool(mockClient); @@ -46,15 +46,15 @@ describe('image.analyze tool', () => { source: expect.objectContaining({ type: 'url', media_type: 'image/jpeg', - url: 'https://example.com/image.jpg' - }) - } - ]) - }) + url: 'https://example.com/image.jpg', + }), + }, + ]), + }), ]), system: expect.stringContaining('vision assistant'), - maxTokens: 1024 - }) + maxTokens: 1024, + }), ); }); @@ -63,13 +63,13 @@ describe('image.analyze tool', () => { mockClient.chat = vi.fn().mockResolvedValueOnce({ content: 'This is a sample image.', stopReason: 'end_turn', - usage: { inputTokens: 100, outputTokens: 20 } + usage: { inputTokens: 100, outputTokens: 20 }, }); const tool = createImageAnalyzeTool(mockClient); const result = await tool.execute({ data: base64Data, - media_type: 'image/png' + media_type: 'image/png', }); expect(result.success).toBe(true); @@ -87,15 +87,15 @@ describe('image.analyze tool', () => { source: expect.objectContaining({ type: 'base64', media_type: 'image/png', - data: base64Data - }) - } - ]) - }) + data: base64Data, + }), + }, + ]), + }), ]), system: expect.stringContaining('vision assistant'), - maxTokens: 1024 - }) + maxTokens: 1024, + }), ); }); @@ -103,13 +103,13 @@ describe('image.analyze tool', () => { mockClient.chat = vi.fn().mockResolvedValueOnce({ content: 'The image shows a cat sitting on a mat.', stopReason: 'end_turn', - usage: { inputTokens: 100, outputTokens: 30 } + usage: { inputTokens: 100, outputTokens: 30 }, }); const tool = createImageAnalyzeTool(mockClient); const result = await tool.execute({ url: 'https://example.com/cat.jpg', - prompt: 'What is in this image?' + prompt: 'What is in this image?', }); expect(result.success).toBe(true); @@ -120,11 +120,11 @@ describe('image.analyze tool', () => { expect.objectContaining({ content: expect.arrayContaining([ { type: 'text', text: 'What is in this image?' }, - expect.any(Object) - ]) - }) - ]) - }) + expect.any(Object), + ]), + }), + ]), + }), ); }); @@ -132,7 +132,7 @@ describe('image.analyze tool', () => { mockClient.chat = vi.fn().mockResolvedValueOnce({ content: 'This is the default prompt response.', stopReason: 'end_turn', - usage: { inputTokens: 100, outputTokens: 10 } + usage: { inputTokens: 100, outputTokens: 10 }, }); const tool = createImageAnalyzeTool(mockClient); @@ -144,11 +144,11 @@ describe('image.analyze tool', () => { expect.objectContaining({ content: expect.arrayContaining([ { type: 'text', text: 'Describe this image in detail.' }, - expect.any(Object) - ]) - }) - ]) - }) + expect.any(Object), + ]), + }), + ]), + }), ); }); @@ -165,7 +165,7 @@ describe('image.analyze tool', () => { const tool = createImageAnalyzeTool(mockClient); const result = await tool.execute({ url: 'https://example.com/image.jpg', - data: 'base64data' + data: 'base64data', }); expect(result.success).toBe(false); @@ -177,7 +177,7 @@ describe('image.analyze tool', () => { const tool = createImageAnalyzeTool(mockClient); const result = await tool.execute({ data: 'base64data', - prompt: 'Test' + prompt: 'Test', }); expect(result.success).toBe(false); @@ -189,7 +189,7 @@ describe('image.analyze tool', () => { const tool = createImageAnalyzeTool(mockClient); const result = await tool.execute({ data: 'base64data', - media_type: 'image/tiff' + media_type: 'image/tiff', }); expect(result.success).toBe(false); @@ -204,13 +204,13 @@ describe('image.analyze tool', () => { mockClient.chat = vi.fn().mockResolvedValueOnce({ content: 'Success', stopReason: 'end_turn', - usage: { inputTokens: 10, outputTokens: 10 } + usage: { inputTokens: 10, outputTokens: 10 }, }); const tool = createImageAnalyzeTool(mockClient); const result = await tool.execute({ data: 'base64data', - media_type: mediaType + media_type: mediaType, }); expect(result.success).toBe(true); @@ -245,12 +245,12 @@ describe('image.analyze tool', () => { const mockRequest = { messages: [] as any, system: '', - maxTokens: 1024 + maxTokens: 1024, }; const mockResponse = { content: 'Analysis complete.', stopReason: 'end_turn', - usage: { inputTokens: 100, outputTokens: 10 } + usage: { inputTokens: 100, outputTokens: 10 }, }; mockClient.chat = vi.fn().mockResolvedValue(mockResponse).mockImplementationOnce(async (r) => { @@ -260,7 +260,7 @@ describe('image.analyze tool', () => { const tool = createImageAnalyzeTool(mockClient); await tool.execute({ url: 'https://example.com/image.jpg', - prompt: 'Analyze the colors.' + prompt: 'Analyze the colors.', }); const callArgs = (mockClient.chat as any).mock.calls[0][0]; @@ -272,12 +272,12 @@ describe('image.analyze tool', () => { const mockRequest = { messages: [] as any, system: '', - maxTokens: 1024 + maxTokens: 1024, }; const mockResponse = { content: 'Short response', stopReason: 'end_turn', - usage: { inputTokens: 10, outputTokens: 10 } + usage: { inputTokens: 10, outputTokens: 10 }, }; mockClient.chat = vi.fn().mockResolvedValueOnce(mockResponse); @@ -294,7 +294,7 @@ describe('image.analyze tool', () => { mockClient.chat.mockResolvedValueOnce({ content: expectedContent, stopReason: 'end_turn', - usage: { inputTokens: 100, outputTokens: 100 } + usage: { inputTokens: 100, outputTokens: 100 }, }); const tool = createImageAnalyzeTool(mockClient); diff --git a/src/tools/builtin/image-analyze.ts b/src/tools/builtin/image-analyze.ts index 09d8361..ee69461 100644 --- a/src/tools/builtin/image-analyze.ts +++ b/src/tools/builtin/image-analyze.ts @@ -21,24 +21,24 @@ export function createImageAnalyzeTool(modelClient: ModelClient): Tool { properties: { url: { type: 'string', - description: 'URL of the image to analyze' + description: 'URL of the image to analyze', }, data: { type: 'string', - description: 'Base64-encoded image data (alternative to url)' + description: 'Base64-encoded image data (alternative to url)', }, media_type: { type: 'string', description: - 'MIME type of the image (required when using data). One of: image/jpeg, image/png, image/gif, image/webp' + 'MIME type of the image (required when using data). One of: image/jpeg, image/png, image/gif, image/webp', }, prompt: { type: 'string', description: - 'What to analyze or describe about the image. Default: "Describe this image in detail."' - } + 'What to analyze or describe about the image. Default: "Describe this image in detail."', + }, }, - required: [] + required: [], }, execute: async (rawArgs: unknown): Promise => { try { @@ -48,7 +48,7 @@ export function createImageAnalyzeTool(modelClient: ModelClient): Tool { return { success: false, output: '', - error: 'Either "url" or "data" must be provided' + error: 'Either "url" or "data" must be provided', }; } @@ -56,7 +56,7 @@ export function createImageAnalyzeTool(modelClient: ModelClient): Tool { return { success: false, output: '', - error: 'Cannot provide both "url" and "data" - choose one' + error: 'Cannot provide both "url" and "data" - choose one', }; } @@ -64,7 +64,7 @@ export function createImageAnalyzeTool(modelClient: ModelClient): Tool { return { success: false, output: '', - error: 'media_type is required when providing data' + error: 'media_type is required when providing data', }; } @@ -72,7 +72,7 @@ export function createImageAnalyzeTool(modelClient: ModelClient): Tool { return { success: false, output: '', - error: `Invalid media_type: ${args.media_type}. Must be one of: ${VALID_MEDIA_TYPES.join(', ')}` + error: `Invalid media_type: ${args.media_type}. Must be one of: ${VALID_MEDIA_TYPES.join(', ')}`, }; } @@ -80,42 +80,42 @@ export function createImageAnalyzeTool(modelClient: ModelClient): Tool { const imageSource = args.url ? { - type: 'url' as const, - media_type: args.media_type || 'image/jpeg', - url: args.url - } + type: 'url' as const, + media_type: args.media_type || 'image/jpeg', + url: args.url, + } : { - type: 'base64' as const, - media_type: args.media_type!, - data: args.data - }; + type: 'base64' as const, + media_type: args.media_type!, + data: args.data, + }; const message = { role: 'user' as const, content: [ { type: 'text' as const, text: prompt }, - { type: 'image' as const, source: imageSource } - ] + { type: 'image' as const, source: imageSource }, + ], }; const response = await modelClient.chat({ messages: [message], system: 'You are a vision assistant. Analyze the provided image according to the user\'s request. Provide detailed, helpful descriptions.', - maxTokens: 1024 + maxTokens: 1024, }); return { success: true, - output: response.content + output: response.content, }; } catch (error) { return { success: false, output: '', - error: error instanceof Error ? error.message : String(error) + error: error instanceof Error ? error.message : String(error), }; } - } + }, }; } diff --git a/src/tools/builtin/memory-search.ts b/src/tools/builtin/memory-search.ts index 8d727f2..0c22d41 100644 --- a/src/tools/builtin/memory-search.ts +++ b/src/tools/builtin/memory-search.ts @@ -43,7 +43,7 @@ export function createMemorySearchTool(store: MemoryStore, hybridSearch?: Hybrid const formatted = results.map((result) => { const sourceLabel = result.source === 'both' ? 'keyword+vector' : result.source === 'vector' ? 'vector' - : 'keyword'; + : 'keyword'; return `[${result.namespace}:${result.line}] (${sourceLabel}, score: ${result.score.toFixed(3)}) ${result.content}\n context: ${result.context}`; }).join('\n\n'); @@ -66,7 +66,7 @@ export function createMemorySearchTool(store: MemoryStore, hybridSearch?: Hybrid // Format each result as a readable block with namespace, line number, and context const formatted = results.map((result) => - `[${result.namespace}:${result.line}] ${result.content}\n context: ${result.context}` + `[${result.namespace}:${result.line}] ${result.content}\n context: ${result.context}`, ).join('\n\n'); return { diff --git a/src/tools/builtin/process/list.ts b/src/tools/builtin/process/list.ts index d6d2258..a37e43d 100644 --- a/src/tools/builtin/process/list.ts +++ b/src/tools/builtin/process/list.ts @@ -21,7 +21,7 @@ export function createProcessListTool(manager: ProcessManager): Tool { ? `${Math.round((Date.now() - p.startedAt) / 1000)}s` : 'N/A'; let line = `${p.id} PID=${p.pid} status=${p.status} uptime=${uptime} cmd="${p.command}"`; - if (p.exitCode !== undefined) line += ` exit=${p.exitCode}`; + if (p.exitCode !== undefined) {line += ` exit=${p.exitCode}`;} return line; }); diff --git a/src/tools/builtin/process/manager.test.ts b/src/tools/builtin/process/manager.test.ts index 51c55ef..4a07bf5 100644 --- a/src/tools/builtin/process/manager.test.ts +++ b/src/tools/builtin/process/manager.test.ts @@ -204,7 +204,7 @@ describe('Process tools', () => { let manager: ProcessManager; afterEach(async () => { - if (manager) await manager.shutdown(); + if (manager) {await manager.shutdown();} }); it('process.start tool creates and returns process info', async () => { diff --git a/src/tools/builtin/process/manager.ts b/src/tools/builtin/process/manager.ts index 754383a..4047072 100644 --- a/src/tools/builtin/process/manager.ts +++ b/src/tools/builtin/process/manager.ts @@ -39,7 +39,7 @@ export class RingBuffer { /** Read all buffered data as a UTF-8 string. */ read(): string { - if (this.length === 0) return ''; + if (this.length === 0) {return '';} if (this.length < this.capacity) { // Buffer not full — data is contiguous, starting at (writePos - length) @@ -202,15 +202,15 @@ export class ProcessManager { /** Get recent output from a process's ring buffer. */ getOutput(id: string): string { const proc = this.processes.get(id); - if (!proc) throw new Error(`Process ${id} not found`); + if (!proc) {throw new Error(`Process ${id} not found`);} return proc.outputBuffer.read(); } /** Kill a process by sending a signal. Returns true if signal was sent. */ kill(id: string, signal: NodeJS.Signals = 'SIGTERM'): boolean { const proc = this.processes.get(id); - if (!proc) throw new Error(`Process ${id} not found`); - if (proc.status !== 'running') return false; + if (!proc) {throw new Error(`Process ${id} not found`);} + if (proc.status !== 'running') {return false;} const child = this.childProcesses.get(id); if (child) { @@ -229,7 +229,7 @@ export class ProcessManager { runningCount(): number { let count = 0; for (const proc of this.processes.values()) { - if (proc.status === 'running') count++; + if (proc.status === 'running') {count++;} } return count; } @@ -254,7 +254,7 @@ export class ProcessManager { } const running = this.list().filter(p => p.status === 'running'); - if (running.length === 0) return; + if (running.length === 0) {return;} console.log(`[ProcessManager] Killing ${running.length} running process(es)...`); diff --git a/src/tools/builtin/process/status.ts b/src/tools/builtin/process/status.ts index 27c8dfc..887f959 100644 --- a/src/tools/builtin/process/status.ts +++ b/src/tools/builtin/process/status.ts @@ -32,9 +32,9 @@ export function createProcessStatusTool(manager: ProcessManager): Tool { info += `PID: ${proc.pid}\n`; info += `Status: ${proc.status}\n`; info += `Uptime: ${uptime}\n`; - if (proc.exitCode !== undefined) info += `Exit code: ${proc.exitCode}\n`; - if (proc.errorMessage) info += `Error: ${proc.errorMessage}\n`; - if (proc.cwd) info += `CWD: ${proc.cwd}\n`; + if (proc.exitCode !== undefined) {info += `Exit code: ${proc.exitCode}\n`;} + if (proc.errorMessage) {info += `Error: ${proc.errorMessage}\n`;} + if (proc.cwd) {info += `CWD: ${proc.cwd}\n`;} return { success: true, output: info.trimEnd() }; }, diff --git a/src/tools/executor.ts b/src/tools/executor.ts index b32a4b1..046a344 100644 --- a/src/tools/executor.ts +++ b/src/tools/executor.ts @@ -61,7 +61,7 @@ export class ToolExecutor { const result = await Promise.race([ tool.execute(args), new Promise((_, reject) => - setTimeout(() => reject(new Error(`Tool '${toolName}' timed out after ${this.defaultTimeoutMs}ms`)), this.defaultTimeoutMs) + setTimeout(() => reject(new Error(`Tool '${toolName}' timed out after ${this.defaultTimeoutMs}ms`)), this.defaultTimeoutMs), ), ]); diff --git a/src/tools/registry.ts b/src/tools/registry.ts index ec8199e..5f61c09 100644 --- a/src/tools/registry.ts +++ b/src/tools/registry.ts @@ -81,7 +81,7 @@ export class ToolRegistry { /** Return tools filtered by the policy for a given context. */ filteredList(context?: ToolPolicyContext): Tool[] { - if (!this._policy) return this.list(); + if (!this._policy) {return this.list();} return this._policy.filterTools(this.list(), context); } diff --git a/src/utils/html.ts b/src/utils/html.ts index f6687ae..6628e8b 100644 --- a/src/utils/html.ts +++ b/src/utils/html.ts @@ -21,7 +21,7 @@ const NAMED_ENTITIES: Record = { function decodeEntity(entity: string): string { // Named entity const named = NAMED_ENTITIES[entity.toLowerCase()]; - if (named) return named; + if (named) {return named;} // Decimal numeric entity: &#NNN; const decMatch = entity.match(/^&#(\d+);$/); @@ -52,7 +52,7 @@ function decodeEntity(entity: string): string { * @returns Clean plain text */ export function sanitizeHtml(text: string): string { - if (!text) return ''; + if (!text) {return '';} let result = text;