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
This commit is contained in:
Shakker
2026-02-04 15:37:51 +00:00
committed by Shakker
parent 5bd63b012c
commit dbaf0a8ae2

View File

@@ -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<boolean> {