From 5116dbeb60119d52178227d0a8fa97532f45c78d Mon Sep 17 00:00:00 2001 From: Val Alexander Date: Tue, 17 Mar 2026 23:52:29 -0500 Subject: [PATCH] Plugins: use var for reserved commands to avoid bundler TDZ; scope expand button to assistant; wire cron run navigate --- src/plugins/commands.ts | 92 +++++++++++++++++++---------------------- ui/src/ui/views/cron.ts | 15 +++++-- 2 files changed, 54 insertions(+), 53 deletions(-) diff --git a/src/plugins/commands.ts b/src/plugins/commands.ts index 70275f4d4b1..a44cbc26e7e 100644 --- a/src/plugins/commands.ts +++ b/src/plugins/commands.ts @@ -35,56 +35,15 @@ let registryLocked = false; const MAX_ARGS_LENGTH = 4096; /** - * Reserved command names that plugins cannot override. - * These are built-in commands from commands-registry.data.ts. + * Reserved command names that plugins cannot override (built-in commands). * - * Lazily initialized to avoid TDZ errors when the bundler places this - * module's body after call sites within the same output chunk. + * Constructed lazily inside validateCommandName to avoid TDZ errors: the + * bundler can place this module's body after call sites within the same + * output chunk, so any module-level const/let would be uninitialized when + * first accessed during plugin registration. */ -let _reservedCommands: Set | undefined; -function getReservedCommands(): Set { - return (_reservedCommands ??= new Set([ - // Core commands - "help", - "commands", - "status", - "whoami", - "context", - "btw", - // Session management - "stop", - "restart", - "reset", - "new", - "compact", - // Configuration - "config", - "debug", - "allowlist", - "activation", - // Agent control - "skill", - "subagents", - "kill", - "steer", - "tell", - "model", - "models", - "queue", - // Messaging - "send", - // Execution - "bash", - "exec", - // Mode toggles - "think", - "verbose", - "reasoning", - "elevated", - // Billing - "usage", - ])); -} +// eslint-disable-next-line no-var -- var avoids TDZ when bundler reorders module bodies in a chunk +var reservedCommands: Set | undefined; /** * Validate a command name. @@ -103,8 +62,41 @@ export function validateCommandName(name: string): string | null { return "Command name must start with a letter and contain only letters, numbers, hyphens, and underscores"; } - // Check reserved commands - if (getReservedCommands().has(trimmed)) { + reservedCommands ??= new Set([ + "help", + "commands", + "status", + "whoami", + "context", + "btw", + "stop", + "restart", + "reset", + "new", + "compact", + "config", + "debug", + "allowlist", + "activation", + "skill", + "subagents", + "kill", + "steer", + "tell", + "model", + "models", + "queue", + "send", + "bash", + "exec", + "think", + "verbose", + "reasoning", + "elevated", + "usage", + ]); + + if (reservedCommands.has(trimmed)) { return `Command name "${trimmed}" is reserved by a built-in command`; } diff --git a/ui/src/ui/views/cron.ts b/ui/src/ui/views/cron.ts index d105bbf0b2c..ee839c24274 100644 --- a/ui/src/ui/views/cron.ts +++ b/ui/src/ui/views/cron.ts @@ -675,7 +675,7 @@ export function renderCron(props: CronProps) { ` : html`
- ${runs.map((entry) => renderRun(entry, props.basePath))} + ${runs.map((entry) => renderRun(entry, props.basePath, props.onNavigateToChat))}
` } @@ -1710,7 +1710,11 @@ function runDeliveryLabel(value: string): string { } } -function renderRun(entry: CronRunLogEntry, basePath: string) { +function renderRun( + entry: CronRunLogEntry, + basePath: string, + onNavigateToChat?: (sessionKey: string) => void, +) { const chatUrl = typeof entry.sessionKey === "string" && entry.sessionKey.trim().length > 0 ? `${pathForTab("chat", basePath)}?session=${encodeURIComponent(entry.sessionKey)}` @@ -1750,7 +1754,12 @@ function renderRun(entry: CronRunLogEntry, basePath: string) { } ${ chatUrl - ? html`
${t("cron.runEntry.openRunChat")}
` + ? html`
{ + if (onNavigateToChat && entry.sessionKey) { + e.preventDefault(); + onNavigateToChat(entry.sessionKey); + } + }}>${t("cron.runEntry.openRunChat")}
` : nothing } ${entry.error ? html`
${entry.error}
` : nothing}