From dbaf0a8ae27cb8a75d21b391e20378a0a1f5d1d7 Mon Sep 17 00:00:00 2001 From: Shakker Date: Wed, 4 Feb 2026 15:37:51 +0000 Subject: [PATCH] update: use shared completion helpers for shell completion setup - Replace inline completion logic with `checkShellCompletionStatus` and `ensureCompletionCacheExists` - Auto-upgrade old slow dynamic patterns silently during update - Auto-regenerate cache if profile exists but cache is missing - Prompt to install if no completion is configured --- src/cli/update-cli.ts | 72 ++++++++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 22 deletions(-) diff --git a/src/cli/update-cli.ts b/src/cli/update-cli.ts index e898aec86..45fe7ddf2 100644 --- a/src/cli/update-cli.ts +++ b/src/cli/update-cli.ts @@ -4,6 +4,10 @@ import { spawnSync } from "node:child_process"; import fs from "node:fs/promises"; import os from "node:os"; import path from "node:path"; +import { + checkShellCompletionStatus, + ensureCompletionCacheExists, +} from "../commands/doctor-completion.js"; import { formatUpdateAvailableHint, formatUpdateOneLiner, @@ -51,7 +55,7 @@ import { renderTable } from "../terminal/table.js"; import { theme } from "../terminal/theme.js"; import { replaceCliName, resolveCliName } from "./cli-name.js"; import { formatCliCommand } from "./command-format.js"; -import { installCompletion, isCompletionInstalled, resolveShellFromEnv } from "./completion-cli.js"; +import { installCompletion } from "./completion-cli.js"; import { formatHelpExamples } from "./help-format.js"; export type UpdateCommandOptions = { @@ -234,32 +238,56 @@ async function tryInstallShellCompletion(opts: { return; } - const shell = resolveShellFromEnv(); - const installed = await isCompletionInstalled(shell, CLI_NAME); - if (installed) { - return; - } + const status = await checkShellCompletionStatus(CLI_NAME); - defaultRuntime.log(""); - defaultRuntime.log(theme.heading("Shell completion")); - - const shouldInstall = await confirm({ - message: stylePromptMessage(`Enable ${shell} shell completion for ${CLI_NAME}?`), - initialValue: true, - }); - - if (isCancel(shouldInstall) || !shouldInstall) { - if (!opts.skipPrompt) { - defaultRuntime.log( - theme.muted( - `Skipped. Run \`${replaceCliName(formatCliCommand("openclaw completion --install"), CLI_NAME)}\` later to enable.`, - ), - ); + // Profile uses slow dynamic pattern - upgrade to cached version + if (status.usesSlowPattern) { + defaultRuntime.log(theme.muted("Upgrading shell completion to cached version...")); + // Ensure cache exists first + const cacheGenerated = await ensureCompletionCacheExists(CLI_NAME); + if (cacheGenerated) { + await installCompletion(status.shell, true, CLI_NAME); } return; } - await installCompletion(shell, opts.skipPrompt, CLI_NAME); + // Profile has completion but no cache - auto-fix silently + if (status.profileInstalled && !status.cacheExists) { + defaultRuntime.log(theme.muted("Regenerating shell completion cache...")); + await ensureCompletionCacheExists(CLI_NAME); + return; + } + + // No completion at all - prompt to install + if (!status.profileInstalled) { + defaultRuntime.log(""); + defaultRuntime.log(theme.heading("Shell completion")); + + const shouldInstall = await confirm({ + message: stylePromptMessage(`Enable ${status.shell} shell completion for ${CLI_NAME}?`), + initialValue: true, + }); + + if (isCancel(shouldInstall) || !shouldInstall) { + if (!opts.skipPrompt) { + defaultRuntime.log( + theme.muted( + `Skipped. Run \`${replaceCliName(formatCliCommand("openclaw completion --install"), CLI_NAME)}\` later to enable.`, + ), + ); + } + return; + } + + // Generate cache first (required for fast shell startup) + const cacheGenerated = await ensureCompletionCacheExists(CLI_NAME); + if (!cacheGenerated) { + defaultRuntime.log(theme.warn("Failed to generate completion cache.")); + return; + } + + await installCompletion(status.shell, opts.skipPrompt, CLI_NAME); + } } async function isEmptyDir(targetPath: string): Promise {