Skip to content

feat(cli): suggest closest command for unknown subcommands #83999

Description

@HannesOberreiter

This was generated by AI during triage.

Summary

Suggest the closest known CLI command when openclaw receives an unknown root command.

Problem to solve

Mistyped root commands currently fail with a generic unknown-command message and leave users to inspect openclaw --help manually. For near misses such as openclaw upate or common terminology such as openclaw upgrade, OpenClaw already has enough command metadata to point users at the intended command (openclaw update) without changing behavior or auto-running anything.

This is a small CLI UX improvement: reduce friction, avoid unnecessary docs/help lookup, and make mistakes recoverable in one terminal turn.

Proposed solution

Add npm-style suggestions for unknown root commands:

  • Keep a small explicit alias map for known/common terms where edit distance is not enough, e.g. upgrade -> update.
  • Compute simple Levenshtein distance from the unknown root command to known built-in root command names.
  • Consider both core commands and sub-CLI roots from the existing descriptor catalogs.
  • Only suggest when the best match is close enough, e.g. distance < input.length * 0.4, similar to npm's threshold.
  • Do not auto-correct or auto-run. Only append a hint to the existing error output.
  • Keep plugin-command policy behavior intact. If existing plugin allowlist/ownership diagnostics apply, preserve them and append suggestions only where appropriate.

Example desired output:

OpenClaw does not know the command "upate".
Did you mean this?
  openclaw update
Try: openclaw --help
Plugin command? openclaw plugins list
Docs: https://docs.openclaw.ai/cli

For explicit aliases:

OpenClaw does not know the command "upgrade".
Did you mean this?
  openclaw update

Alternatives considered

  • Only print openclaw --help: current behavior; correct but slower for common typos.
  • Auto-run the corrected command: risky and surprising, especially for commands that mutate state.
  • Rely only on Levenshtein: misses semantic/common aliases such as upgrade -> update.
  • Add Commander aliases for typo terms: would execute misspelled commands instead of teaching the canonical command, and risks preserving accidental API surface.

Impact

Affected users: CLI users who mistype root commands or use common adjacent terminology.

Severity: Low/medium UX friction; not blocking, but it causes extra manual help lookup.

Frequency: Intermittent but common for commands like update, doctor, config, plugins, channels, and models.

Consequence: Extra terminal turns and worse first-run CLI experience.

Evidence/examples

npm uses a similar approach:

  • explicit alias/typo mapping in its command list, e.g. upgrade -> update, udpate -> update
  • fastest-levenshtein for close matches
  • close threshold roughly distance(input, command) < input.length * 0.4
  • prints suggestions but does not auto-run unknown commands

Relevant OpenClaw implementation surfaces observed in the repo:

  • src/cli/program/core-command-descriptors.ts exposes core command names via getCoreCliCommandNames().
  • src/cli/program/subcli-descriptors.ts exposes sub-CLI roots via getSubCliEntries() and includes update.
  • src/cli/run-main.ts has the early unknown-primary path: resolveUnownedCliPrimaryMessage().
  • src/cli/program/error-output.ts formats Commander unknown-command parse errors.
  • src/cli/program/error-output.test.ts already covers unknown-command formatting.
  • src/cli/run-main.exit.test.ts has related CLI dispatch/error coverage.

Additional information

Suggested implementation shape:

  1. Add a small pure helper, e.g. src/cli/program/command-suggestions.ts:
    • CLI_COMMAND_ALIASES = { upgrade: "update" }
    • levenshteinDistance(a, b) implemented locally to avoid a new dependency
    • suggestCliCommands(input, candidates) returns up to 3 close command names
    • default candidates from getCoreCliCommandNames() plus getSubCliEntries().map(entry => entry.name)
  2. Use the helper in formatCliParseErrorOutput() for Commander parse errors.
  3. Use the helper in resolveUnownedCliPrimaryMessage() so the early plugin-aware unknown-primary path gets the same suggestion.
  4. Add tests for:
    • upate -> update
    • upgrade -> update
    • weak matches do not suggest
    • existing plugin-policy diagnostics remain unchanged except for optional appended suggestion when applicable

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Low-priority cleanup, docs, polish, ergonomics, or speculative work.clawsweeper:linked-pr-openClawSweeper found an open linked pull request for this issue.clawsweeper:no-new-fix-prClawSweeper does not recommend queueing a new automated fix PR for this issue.clawsweeper:source-reproClawSweeper found a high-confidence source-level issue reproduction.issue-rating: 🦞 diamond lobsterVery strong issue quality with high-confidence source-level or clear reproduction.

    Type

    No type

    Fields

    Priority

    None yet

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions