ai-tools

OpenClaw CLI: Shell Completion Is Eating 3.7 Seconds of Your Startup

The OpenClaw CLI boots a full Node.js runtime on every terminal open to generate static shell completions. A simple caching strategy with auto-invalidation eliminates the 3.7-second hit entirely.

AuthorAlex Raihelgaus
Date
OpenClaw CLI: Shell Completion Is Eating 3.7 Seconds of Your Startup

The Problem

As part of my work with Knostic on OpenClaw, I installed the CLI and immediately noticed something was off - opening a new terminal started taking noticeably longer, accompanied by a Node.js deprecation warning:

(node:30759) [DEP0040] DeprecationWarning: The `punycode` module is deprecated.
Please use a userland alternative instead.

After profiling my shell startup, one line stood out:

⏱ openclaw                                   3744ms

3.7 seconds. A single command, responsible for 62.5% of my entire shell startup time.

Root Cause

The recommended setup in the OpenClaw docs suggests adding this to your shell config:

source <(openclaw completion --shell zsh)

This runs the openclaw Node.js process on every single terminal open to dynamically generate shell completions. The command:

  1. Boots an entire Node.js runtime (~3.7 seconds)
  2. Triggers the punycode deprecation warning because the CLI (or one of its dependencies) imports the deprecated built-in punycode module instead of a userland alternative
  3. Generates the same static completion output every time - the completions don't change between runs unless the CLI itself is updated

This is a pattern seen across many Node.js CLIs that offer shell completions. The completion definitions are static, but the generation runs a full Node.js boot cycle on every shell init. It's a known issue - in some setups the hit is even worse (~24s) because the completion command eagerly loads the full plugin system via jiti runtime transpilation.

The Fix

Step 1: Cache the completions

Generate the completion output once to a static file instead of running Node.js every time:

openclaw completion --shell zsh > ~/.openclaw-completion.zsh

Then replace the dynamic source line:

# Before (3744ms every terminal open)
source <(openclaw completion --shell zsh)

# After (< 1ms - just reading a file)
source ~/.openclaw-completion.zsh

Step 2: Auto-invalidate the cache

The naive approach requires you to manually regenerate completions after every openclaw update. Forget that. Zsh's -nt (newer-than) test compares file modification times, so we can auto-detect when the binary has been updated:

_openclaw_cache=~/.openclaw-completion.zsh
if [[ ! -f "$_openclaw_cache" ]] || [[ "$(which openclaw)" -nt "$_openclaw_cache" ]]; then
  openclaw completion --shell zsh > "$_openclaw_cache"
fi
source "$_openclaw_cache"
unset _openclaw_cache

How it works:

  • "$(which openclaw)" -nt "$_openclaw_cache" checks if the openclaw binary is newer than the cached file
  • If the binary has been reinstalled, upgraded via npm/brew, or the cache doesn't exist yet, it regenerates
  • Otherwise it sources the static file (~1ms)
  • You pay the 3.7s cost once after an upgrade, then it's cached until the next one

This covers all real-world update scenarios (brew upgrade, npm update, manual reinstall) because they all touch the binary's modification time.

The punycode Warning

The deprecation warning is a separate issue in the OpenClaw codebase. Somewhere in the dependency tree, code is importing the deprecated built-in punycode module:

const punycode = require('punycode'); // built-in, deprecated since Node 21

Instead of the userland package:

const punycode = require('punycode/'); // npm package, not deprecated

This should be fixed upstream in OpenClaw or its dependencies. Caching the completions eliminates the warning from shell startup entirely since Node.js no longer runs on every init.

Takeaway

The pattern applies to any Node.js CLI with shell completions: if the completion output is static, cache it. If you want it maintenance-free, use -nt to auto-invalidate when the binary updates.

This isn't specific to OpenClaw. Any CLI that tells you to add source <(some-cli completion --shell zsh) to your shell config is doing the same thing - booting a runtime on every terminal open to generate static output. Cache it.

If your terminal feels sluggish, profile it. It takes 5 minutes.

Tags

#shell#nodejs#cli#performance#zsh#developer-tools#openclaw