diff --git a/scripts/postinstall.js b/scripts/postinstall.js index d6896d9ac3..32b6eb3fa6 100644 --- a/scripts/postinstall.js +++ b/scripts/postinstall.js @@ -248,12 +248,37 @@ function applyPatchFile({ patchPath, targetDir }) { applyPatchSet({ patchText, targetDir }); } +function trySetupCompletion(repoRoot) { + // Skip in CI or if explicitly disabled + if (process.env.CI || process.env.OPENCLAW_SKIP_COMPLETION_SETUP) return; + + const binPath = path.join(repoRoot, "openclaw.mjs"); + if (!fs.existsSync(binPath)) return; + + // In development, dist might not exist yet during postinstall + const distEntry = path.join(repoRoot, "dist", "index.js"); + if (!fs.existsSync(distEntry)) return; + + try { + // Run with OPENCLAW_SKIP_POSTINSTALL to avoid any weird recursion, + // though distinct from this script. + spawnSync(process.execPath, [binPath, "completion", "--install", "--yes"], { + cwd: repoRoot, + stdio: "inherit", + env: { ...process.env, OPENCLAW_SKIP_POSTINSTALL: "1" }, + }); + } catch (err) { + // Ignore errors to not break install + } +} + function main() { const repoRoot = getRepoRoot(); process.chdir(repoRoot); ensureExecutable(path.join(repoRoot, "dist", "entry.js")); setupGitHooks({ repoRoot }); + trySetupCompletion(repoRoot); if (!shouldApplyPnpmPatchedDependenciesFallback()) { return; diff --git a/src/wizard/onboarding.ts b/src/wizard/onboarding.ts index ef2e349c67..c2391261ad 100644 --- a/src/wizard/onboarding.ts +++ b/src/wizard/onboarding.ts @@ -42,6 +42,7 @@ import { finalizeOnboardingWizard } from "./onboarding.finalize.js"; import { configureGatewayForOnboarding } from "./onboarding.gateway-config.js"; import type { QuickstartGatewayDefaults, WizardFlow } from "./onboarding.types.js"; import { WizardCancelledError, type WizardPrompter } from "./prompts.js"; +import { installCompletion } from "../cli/completion-cli.js"; async function requireRiskAcknowledgement(params: { opts: OnboardOptions; @@ -448,4 +449,16 @@ export async function runOnboardingWizard( prompter, runtime, }); + + const installShell = await prompter.confirm({ + message: "Install shell completion script?", + initialValue: true, + }); + + if (installShell) { + const shell = process.env.SHELL?.split("/").pop() || "zsh"; + // We pass 'yes=true' to skip any double-confirmation inside the helper, + // as the wizard prompt above serves as confirmation. + await installCompletion(shell, true); + } }