Skip to content

feat: shell completions for bash and zsh#17

Open
TimBeyer wants to merge 11 commits intomainfrom
feat/shell-completions
Open

feat: shell completions for bash and zsh#17
TimBeyer wants to merge 11 commits intomainfrom
feat/shell-completions

Conversation

@TimBeyer
Copy link
Owner

Summary

  • Add clawctl completions bash and clawctl completions zsh commands that generate shell completion scripts with dynamic instance name completion, per-command option completion, and openclaw subcommand completion
  • Cache openclaw's own completion scripts from the VM (completions update-oc) for deep subcommand support, with auto-population on first use and background refresh (>24h staleness) on VM-interacting commands (start, restart, shell, oc, create)
  • Add documentation, install hints, and 22 unit tests

Test plan

  • eval "$(clawctl completions bash)" — verify commands, options, instance names complete
  • eval "$(clawctl completions zsh)" — same for zsh
  • clawctl oc <TAB> shows openclaw subcommands (static fallback without cache, full completions with cache)
  • clawctl completions update-oc fetches and caches completions from a running VM
  • Cache auto-populates on first completions invocation when a VM is running
  • Stale cache refreshes silently after clawctl start, clawctl shell, etc.
  • bun test — 287 tests pass

🤖 Generated with Claude Code

TimBeyer and others added 11 commits March 13, 2026 14:09
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
New `clawctl completions <shell>` command generates completion scripts
with support for all commands, per-command options, dynamic instance
name completion (via python3), and openclaw subcommand completion.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
dedent v1 processes escape sequences on raw template strings, so `\\`
at end-of-line collapsed line continuations and `\\n` became a literal
newline. Fix by using `\${...}` for shell vars (prevents JS interpolation),
`${BS}` constant for zsh line continuations, and `chr(10)` instead of
`'\n'` in the python command.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Check stdout.isTTY instead of stderr.isTTY — when run via
eval "$(clawctl completions zsh)" in .zshrc, stderr is still a TTY
so the install hints were printing on every shell startup. Also remove
comment headers from generated scripts to reduce noise.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The previous approach called _arguments directly in each case branch
without shifting words/CURRENT, so positional specs like '1:...'
matched the subcommand itself rather than the cursor position.

Use the standard zsh pattern: _arguments -C '1:command:->cmd'
'*:: :->args' which automatically shifts words and CURRENT for
sub-argument processing. All positional and option completions now
fire correctly, including openclaw subcommands.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Two fixes:
- Extract openclaw subcommand completion into a standalone function
  (_clawctl_openclaw_subcommands) instead of inlining _describe as an
  _arguments action string — _describe doesn't work in eval'd action
  contexts where the completion system variables aren't accessible
- Fix instance array splitting to use ${(f)"$(...)"} (proper zsh
  parameter expansion) instead of "${(@f)$(...)"} which caused bad
  substitution errors

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace the 10-item placeholder list with the full set of openclaw CLI
commands (38 subcommands) sourced from the official documentation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The completions command is a one-time setup utility, not something
users reach for during normal workflows. Removing it from the
completion list reduces noise.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add `clawctl completions update-oc` which calls `openclaw completion`
inside the VM and caches the generated scripts to
~/.config/clawctl/oc-completions.{zsh,bash}. The completion templates
source the cache at load time — if present, openclaw's own completion
functions handle all subcommand/option completion with full depth. If
no cache exists, the static fallback list covers top-level commands.

Also restructure `completions` as a command group with `bash`, `zsh`,
and `update-oc` subcommands.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The binName interpolation produced invalid shell variable names like
_clawctl-dev_oc_cache (hyphens aren't allowed). The cache path is
always the same regardless of binary name, so inline it directly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Populate the oc-completions cache automatically:
- On first `completions` invocation if a running VM is available
- Background refresh when cache is stale (>24h) on start, restart,
  shell, openclaw, and create commands

Also adds `completions update-oc` subcommand for manual cache refresh
and removes unused refreshOcCompletionsIfStale from barrel export.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds first-class shell completion support to clawctl (bash + zsh), including deep openclaw subcommand completion via a cached script fetched from a running VM, plus documentation and tests.

Changes:

  • Add clawctl completions bash|zsh script generators and wire the new CLI command.
  • Implement caching/refresh of openclaw completion scripts (completions update-oc + refresh hooks after VM-interacting commands).
  • Add docs/UX hints and unit tests for the completion templates.

Reviewed changes

Copilot reviewed 16 out of 17 changed files in this pull request and generated 14 comments.

Show a summary per file
File Description
tasks/2026-03-13_1409_shell-completions/TASK.md Task plan/outcome notes for the feature.
src/templates/completions/zsh.ts New zsh completion script template with instance + openclaw completion.
src/templates/completions/index.ts Exports completion template generators.
src/templates/completions/completions.test.ts Unit tests for generated completion scripts.
src/templates/completions/bash.ts New bash completion script template with instance + openclaw completion.
src/steps/finish.tsx Adds post-setup hint about enabling completions.
src/commands/start.ts Triggers stale openclaw completion cache refresh after start.
src/commands/shell.ts Triggers stale openclaw completion cache refresh after shell.
src/commands/restart.ts Triggers stale openclaw completion cache refresh after restart.
src/commands/openclaw.ts Triggers stale openclaw completion cache refresh after openclaw/oc.
src/commands/index.ts Exports the new completions command handlers.
src/commands/create.ts Triggers stale openclaw completion cache refresh after create flows.
src/commands/completions.ts Implements completions generation, update-oc, caching, and staleness refresh logic.
install.sh Adds install-time hint to enable completions.
docs/getting-started.md Adds a “Shell completions” next-step mention.
bin/cli.tsx Adds the completions command + subcommands to the CLI.
README.md Documents shell completions and adds command table entry.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

You can also share your feedback on Copilot code review. Take the survey.

const updated = await fetchAndCacheOcCompletions(driver, entry.vmName);
if (updated) {
console.log("Cached openclaw completions for bash and zsh.");
console.log("Reload your shell to pick up the changes.");
Comment on lines +16 to +21
await refreshOcCompletionsIfStale(driver, entry.vmName);
process.exit(result.exitCode);
}

const result = await driver.shell(entry.vmName);
await refreshOcCompletionsIfStale(driver, entry.vmName);
const entry = await requireInstance(opts);
const command = shellQuote(["openclaw", ...args]);
const result = await driver.execInteractive(entry.vmName, command);
await refreshOcCompletionsIfStale(driver, entry.vmName);
fi
done

local commands="create list status start stop restart delete shell register openclaw oc use"
'--help[Show help]'
;;
openclaw|oc)
_${binName}_openclaw_dispatch
'--help[Show help]'
;;
completions)
_arguments '1:shell:(bash zsh)' '--help[Show help]'
expect(custom).toContain("_clawctl-dev_instances");
expect(custom).toContain("complete -F _clawctl-dev_completions clawctl-dev");
});

Comment on lines +6 to +10
_${binName}_instances() {
local registry="$HOME/.config/clawctl/instances.json"
if [[ -f "$registry" ]]; then
python3 -c "import json,sys; print(chr(10).join(json.load(open(sys.argv[1])).get('instances',{}).keys()))" "$registry" 2>/dev/null
fi
Comment on lines +8 to +12
#compdef ${binName}

_${binName}_instances() {
local registry="$HOME/.config/clawctl/instances.json"
if [[ -f "$registry" ]]; then
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants