/** * Test script for shell completion installation feature. * * This script simulates the shell completion prompt that appears during * `openclaw update`. Use it to verify the completion installation flow * without running a full update. * * Run from repo root: * node --import tsx scripts/test-shell-completion.ts [options] * npx tsx scripts/test-shell-completion.ts [options] * bun scripts/test-shell-completion.ts [options] * * Options: * --shell Override shell detection (zsh, bash, fish, powershell) * --check-only Only check status, don't prompt to install * --force Skip the "already installed" check and prompt anyway * --help Show this help message * * Examples: * node --import tsx scripts/test-shell-completion.ts * node --import tsx scripts/test-shell-completion.ts --check-only * node --import tsx scripts/test-shell-completion.ts --shell bash * node --import tsx scripts/test-shell-completion.ts --force */ import os from "node:os"; import path from "node:path"; import { confirm, isCancel } from "@clack/prompts"; import { installCompletion } from "../src/cli/completion-runtime.js"; import { checkShellCompletionStatus } from "../src/commands/doctor-completion.js"; import { stylePromptMessage } from "../src/terminal/prompt-style.js"; import { theme } from "../src/terminal/theme.js"; const CLI_NAME = "openclaw"; interface Options { checkOnly: boolean; force: boolean; help: boolean; } function parseArgs(args: string[]): Options { const options: Options = { checkOnly: false, force: false, help: false, }; for (const arg of args) { if (arg === "--check-only") { options.checkOnly = true; } else if (arg === "--force") { options.force = true; } else if (arg === "--help" || arg === "-h") { options.help = true; } } return options; } function printHelp(): void { console.log(` ${theme.heading("Shell Completion Test Script")} This script simulates the shell completion checks that run during \`openclaw update\`, \`openclaw doctor\`, and \`openclaw onboard\`. ${theme.heading("Usage (run from repo root):")} node --import tsx scripts/test-shell-completion.ts [options] npx tsx scripts/test-shell-completion.ts [options] bun scripts/test-shell-completion.ts [options] ${theme.heading("Options:")} --check-only Only check status, don't prompt to install --force Skip the "already installed" check and prompt anyway --help, -h Show this help message ${theme.heading("Behavior:")} - If profile points at the retired completion cache: rewrites it - If no completion at all: prompts to install - If completion is already installed: nothing to do ${theme.heading("Examples:")} node --import tsx scripts/test-shell-completion.ts node --import tsx scripts/test-shell-completion.ts --check-only node --import tsx scripts/test-shell-completion.ts --force `); } function getShellProfilePath(shell: string): string { const home = process.env.HOME || os.homedir(); switch (shell) { case "zsh": return path.join(home, ".zshrc"); case "bash": return process.platform === "darwin" ? path.join(home, ".bash_profile") : path.join(home, ".bashrc"); case "fish": return path.join(home, ".config", "fish", "config.fish"); case "powershell": if (process.platform === "win32") { return path.join( process.env.USERPROFILE || home, "Documents", "PowerShell", "Microsoft.PowerShell_profile.ps1", ); } return path.join(home, ".config", "powershell", "Microsoft.PowerShell_profile.ps1"); default: return path.join(home, ".zshrc"); } } async function main() { const args = process.argv.slice(2); const options = parseArgs(args); if (options.help) { printHelp(); return; } console.log(theme.heading("Shell Completion Test")); console.log(""); // Get completion status using the same function used by doctor/update/onboard const status = await checkShellCompletionStatus(CLI_NAME); console.log(` Shell: ${theme.accent(status.shell)} ${theme.muted("(detected from $SHELL)")}`); console.log(` Platform: ${theme.muted(process.platform)} ${theme.muted(`(${os.release()})`)}`); console.log(` Profile: ${theme.muted(getShellProfilePath(status.shell))}`); console.log(""); console.log( ` Profile configured: ${status.profileInstalled ? theme.success("yes") : theme.warn("no")}`, ); console.log( ` Uses retired cache: ${status.usesRetiredCache ? theme.error("yes (needs rewrite)") : theme.success("no")}`, ); console.log(""); if (options.checkOnly) { console.log(theme.muted("Check-only mode, exiting.")); return; } if (status.usesRetiredCache) { console.log(theme.warn("Profile uses retired completion cache. Rewriting...")); await installCompletion(status.shell, false, CLI_NAME, { retiredCachePath: status.retiredCachePath, }); console.log(theme.success("Rewrote completion profile.")); return; } if (status.profileInstalled && !options.force) { console.log(theme.muted("Shell completion is fully configured. To test the prompt:")); console.log( theme.muted(" 1. Remove the '# OpenClaw Completion' block from your shell profile"), ); console.log(theme.muted(" 2. Re-run this script")); console.log(theme.muted(" Or use --force to prompt anyway")); console.log(""); return; } // No profile configured - prompt to install console.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) { console.log(theme.muted(`Skipped. Run \`openclaw completion --install\` later to enable.`)); return; } await installCompletion(status.shell, false, CLI_NAME); } main().catch((err) => { console.error(theme.error(`Error: ${String(err)}`)); process.exit(1); });