Skip to content

fix(task): suggest similar commands when mistyping a CLI subcommand#8286

Merged
jdx merged 1 commit intomainfrom
fix/suggest-commands-on-typo
Feb 21, 2026
Merged

fix(task): suggest similar commands when mistyping a CLI subcommand#8286
jdx merged 1 commit intomainfrom
fix/suggest-commands-on-typo

Conversation

@jdx
Copy link
Owner

@jdx jdx commented Feb 21, 2026

Summary

  • When a user mistypes a CLI subcommand (e.g., mise bin-path instead of bin-paths), mise now suggests similar commands instead of showing a confusing "no tasks defined" error
  • Uses the existing fuzzy matcher (SkimMatcherV2) to find similar subcommand names and aliases
  • Works both when no tasks are defined (replaces confusing error) and when tasks exist but the name doesn't match any

Before:

$ mise bin-path
mise ERROR no tasks defined in ~/project. Are you in a project directory?

After:

$ mise bin-path
mise ERROR unknown command: bin-path

Did you mean:
  mise bin-paths

Test plan

  • Added e2e test test_unknown_command_suggestion covering:
    • mise bin-path → suggests bin-paths
    • mise instal → suggests install
    • Error message says "unknown command" not "no tasks defined"
  • Existing test_did_you_mean e2e test still passes
  • All 481 unit tests pass
  • Lint passes

Closes #8278

🤖 Generated with Claude Code


Note

Low Risk
Error-message-only behavior change using existing fuzzy matching; low risk aside from potentially misleading suggestions or edge-case formatting.

Overview
Improves the task-not-found error path to detect when the provided name is likely a mistyped CLI subcommand and, in that case, emit an "unknown command" message with up to 3 fuzzy-matched suggestions (including aliases) instead of misleading "no tasks defined" output.

Adds an e2e regression test (e2e/cli/test_unknown_command_suggestion) covering command-typo scenarios like bin-pathbin-paths and instalinstall, and asserting the new wording.

Written by Cursor Bugbot for commit 03960c0. This will update automatically on new commits. Configure here.

When a user types a misspelled command like `mise bin-path` (instead of
`bin-paths`), mise previously showed a confusing "no tasks defined" error
because it treated the unknown command as a task name. Now it uses fuzzy
matching to detect similar CLI subcommands and suggests them instead.

Closes #8278

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings February 21, 2026 17:02
@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @jdx, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the user experience of the mise CLI by introducing intelligent command suggestions for mistyped subcommands. It replaces a previously confusing "no tasks defined" error with clear, context-aware recommendations, making the CLI more robust and user-friendly by leveraging fuzzy matching for command identification.

Highlights

  • Improved CLI Error Handling: When a user mistypes a CLI subcommand, the system now suggests similar commands instead of a generic "no tasks defined" error.
  • Fuzzy Matching for Suggestions: Utilized the existing SkimMatcherV2 to intelligently find and propose similar subcommand names and aliases.
  • Enhanced User Experience: The change provides more helpful feedback to users, guiding them to the correct command even when no tasks are defined or when tasks exist but the command is unknown.
Changelog
  • e2e/cli/test_unknown_command_suggestion
    • Added a new end-to-end test to verify the unknown command suggestion feature.
  • src/task/task_list.rs
    • Introduced a new function suggest_similar_commands that uses fuzzy matching to find potential command matches.
    • Modified the err_no_task function to incorporate these suggestions, displaying them when a command is unknown, both when no tasks are defined and when tasks exist.
Activity
  • Added a new e2e test test_unknown_command_suggestion to cover various typo scenarios and error message content.
  • Confirmed that the existing test_did_you_mean e2e test continues to pass.
  • Verified that all 481 unit tests pass.
  • Ensured that linting checks pass.
  • Closed the related GitHub discussion Confusing error msg when wrong command to `mise` is provided #8278.
  • Noted that the changes were generated with Claude Code.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a helpful feature that suggests similar CLI subcommands when a user mistypes a command that is subsequently interpreted as a task. This significantly improves the user experience, especially when no tasks are defined, by providing a more relevant error message than the generic 'no tasks defined'.

I have identified a few areas for improvement:

  • Error Priority: The check for similar CLI commands should occur after checking for untrusted configuration files to ensure users are first notified about potential tasks they might have defined but not yet trusted.
  • Suggestion Quality: Filtering out hidden subcommands from the suggestions will prevent internal or plumbing commands from being recommended to users.
  • Wording Consistency: Minor adjustments to the suggestion prompts for better consistency across different error paths.

Comment on lines +89 to +98
// If the name matches a CLI subcommand closely, suggest that instead of
// the confusing "no tasks defined" message
if !similar_cmds.is_empty() {
let mut err_msg = format!("unknown command: {}", style::ered(name));
err_msg.push_str("\n\nDid you mean:");
for cmd_name in &similar_cmds {
err_msg.push_str(&format!("\n mise {cmd_name}"));
}
bail!(err_msg);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

high

The check for similar CLI commands currently takes precedence over the check for untrusted configuration files. If a user has tasks defined in an untrusted config but mistypes the task name (or if the name fuzzy-matches a CLI command), they will see an 'unknown command' error instead of being prompted to trust their config.

It is better to prioritize the untrusted config check, as this is a common reason why expected tasks are missing from the task list.

Comment on lines +69 to +70
cmd.get_subcommands()
.flat_map(|s| std::iter::once(s.get_name()).chain(s.get_all_aliases()))
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

It is recommended to filter out hidden subcommands from the suggestions. Internal or plumbing commands (such as hook-env, completion, etc.) are typically not intended for direct use and can make the suggestions less relevant to the user's intent.

    cmd.get_subcommands()
        .filter(|s| !s.is_hide_set())
        .flat_map(|s| std::iter::once(s.get_name()).chain(s.get_all_aliases()))

}

if !similar_cmds.is_empty() {
err_msg.push_str("\n\nDid you mean the command:");
Copy link
Contributor

Choose a reason for hiding this comment

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

medium

For consistency with the error message used when no tasks are defined (line 93), consider using the simpler 'Did you mean:' prompt here as well.

        err_msg.push_str("\n\nDid you mean:");

Copy link
Contributor

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

Improves the CLI UX when a user mistypes a top-level subcommand by suggesting similar known commands (via fuzzy matching) instead of showing the misleading “no tasks defined” task error.

Changes:

  • Added fuzzy matching against clap-defined subcommands/aliases to generate “unknown command” suggestions.
  • Updated task-not-found error path to prefer command suggestions when appropriate.
  • Added an e2e regression test for mistyped subcommands (bin-pathbin-paths, instalinstall).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

File Description
src/task/task_list.rs Adds suggest_similar_commands() and integrates command suggestions into err_no_task() messaging.
e2e/cli/test_unknown_command_suggestion New e2e test validating suggestions + “unknown command” wording for mistyped subcommands.

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

Comment on lines +76 to +78
})
.sorted_by_key(|(score, _)| -1 * *score)
.take(3)
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

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

Sorting via -1 * *score works but is brittle (potential overflow on i64::MIN) and less readable. Prefer sorting descending via std::cmp::Reverse(*score) (or an explicit comparator) for clarity and safety.

Copilot uses AI. Check for mistakes.
Comment on lines +66 to +70
use clap::CommandFactory;
let cmd = crate::cli::Cli::command();
let matcher = SkimMatcherV2::default().use_cache(true).smart_case();
cmd.get_subcommands()
.flat_map(|s| std::iter::once(s.get_name()).chain(s.get_all_aliases()))
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

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

suggest_similar_commands() rebuilds the full clap Cli::command() and a new SkimMatcherV2 on every error. Since this runs on the error path but can still be hit frequently (e.g., repeated task typos in scripts), consider caching the subcommand/alias list and/or the matcher in a static LazyLock similar to other fuzzy-match usage in the repo.

Copilot uses AI. Check for mistakes.
// the confusing "no tasks defined" message
if !similar_cmds.is_empty() {
let mut err_msg = format!("unknown command: {}", style::ered(name));
err_msg.push_str("\n\nDid you mean:");
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

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

The suggestion header here is Did you mean: (colon) while other suggestion messages in the codebase use a question mark (e.g., Did you mean? / Did you mean one of these?). Consider aligning the punctuation/wording for consistency, especially since tests/searches may rely on the exact phrasing.

Suggested change
err_msg.push_str("\n\nDid you mean:");
err_msg.push_str("\n\nDid you mean?");

Copilot uses AI. Check for mistakes.
}

if !similar_cmds.is_empty() {
err_msg.push_str("\n\nDid you mean the command:");
Copy link

Copilot AI Feb 21, 2026

Choose a reason for hiding this comment

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

This header says Did you mean the command: but multiple suggestions can be printed. Consider making this consistent with the earlier branch (and pluralizing if needed), e.g. use the same Did you mean… header in both places to avoid two different formats for the same kind of suggestion.

Suggested change
err_msg.push_str("\n\nDid you mean the command:");
err_msg.push_str("\n\nDid you mean one of these?");

Copilot uses AI. Check for mistakes.
@github-actions
Copy link

Hyperfine Performance

mise x -- echo

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.17 x -- echo 23.1 ± 0.5 22.0 26.1 1.00
mise x -- echo 24.2 ± 0.9 23.0 30.6 1.05 ± 0.04

mise env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.17 env 22.4 ± 0.5 21.5 28.3 1.00
mise env 22.6 ± 0.4 21.7 24.4 1.01 ± 0.03

mise hook-env

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.17 hook-env 22.9 ± 0.4 22.1 25.7 1.00
mise hook-env 23.2 ± 0.6 22.2 31.8 1.01 ± 0.03

mise ls

Command Mean [ms] Min [ms] Max [ms] Relative
mise-2026.2.17 ls 20.6 ± 0.7 19.7 30.4 1.00
mise ls 21.3 ± 0.7 20.2 26.8 1.03 ± 0.05

xtasks/test/perf

Command mise-2026.2.17 mise Variance
install (cached) 127ms 127ms +0%
ls (cached) 77ms 77ms +0%
bin-paths (cached) 82ms 82ms +0%
task-ls (cached) 812ms 820ms +0%

@jdx jdx merged commit 7dca602 into main Feb 21, 2026
41 checks passed
@jdx jdx deleted the fix/suggest-commands-on-typo branch February 21, 2026 17:21
jdx pushed a commit that referenced this pull request Feb 21, 2026
### 🚀 Features

- **(install)** auto-lock all platforms after tool installation by @jdx
in [#8277](#8277)

### 🐛 Bug Fixes

- **(config)** respect --yes flag for config trust prompts by @jdx in
[#8288](#8288)
- **(exec)** strip shims from PATH on Unix to prevent infinite recursion
by @jdx in [#8276](#8276)
- **(install)** validate --locked before --dry-run short-circuit by
@altendky in [#8290](#8290)
- **(release)** refresh PATH after mise up in release-plz by @jdx in
[#8292](#8292)
- **(schema)** replace unevaluatedProperties with additionalProperties
by @jdx in [#8285](#8285)
- **(task)** avoid duplicated stderr on task failure in replacing mode
by @jdx in [#8275](#8275)
- **(task)** use process groups to kill child process trees on Unix by
@jdx in [#8279](#8279)
- **(task)** run depends_post tasks even when parent task fails by @jdx
in [#8274](#8274)
- **(task)** suggest similar commands when mistyping a CLI subcommand by
@jdx in [#8286](#8286)
- **(task)** execute monorepo subdirectory prepare steps from root by
@jdx in [#8291](#8291)
- **(upgrade)** don't force-reinstall already installed versions by @jdx
in [#8282](#8282)
- **(watch)** restore terminal state after watchexec exits by @jdx in
[#8273](#8273)

### 📚 Documentation

- clarify that MISE_CEILING_PATHS excludes the ceiling directory itself
by @jdx in [#8283](#8283)

### Chore

- replace gen-release-notes script with communique by @jdx in
[#8289](#8289)

### New Contributors

- @altendky made their first contribution in
[#8290](#8290)

## 📦 Aqua Registry Updates

#### New Packages (4)

-
[`Skarlso/crd-to-sample-yaml`](https://github.com/Skarlso/crd-to-sample-yaml)
-
[`kunobi-ninja/kunobi-releases`](https://github.com/kunobi-ninja/kunobi-releases)
-
[`swanysimon/markdownlint-rs`](https://github.com/swanysimon/markdownlint-rs)
- [`tmux/tmux-builds`](https://github.com/tmux/tmux-builds)

#### Updated Packages (2)

-
[`firecow/gitlab-ci-local`](https://github.com/firecow/gitlab-ci-local)
- [`k1LoW/runn`](https://github.com/k1LoW/runn)
netbsd-srcmastr pushed a commit to NetBSD/pkgsrc that referenced this pull request Feb 22, 2026
## [2026.2.18](https://github.com/jdx/mise/compare/v2026.2.17..v2026.2.18) - 2026-02-21

### 🚀 Features

- **(install)** auto-lock all platforms after tool installation by @jdx in [#8277](jdx/mise#8277)

### 🐛 Bug Fixes

- **(config)** respect --yes flag for config trust prompts by @jdx in [#8288](jdx/mise#8288)
- **(exec)** strip shims from PATH on Unix to prevent infinite recursion by @jdx in [#8276](jdx/mise#8276)
- **(install)** validate --locked before --dry-run short-circuit by @altendky in [#8290](jdx/mise#8290)
- **(release)** refresh PATH after mise up in release-plz by @jdx in [#8292](jdx/mise#8292)
- **(schema)** replace unevaluatedProperties with additionalProperties by @jdx in [#8285](jdx/mise#8285)
- **(task)** avoid duplicated stderr on task failure in replacing mode by @jdx in [#8275](jdx/mise#8275)
- **(task)** use process groups to kill child process trees on Unix by @jdx in [#8279](jdx/mise#8279)
- **(task)** run depends_post tasks even when parent task fails by @jdx in [#8274](jdx/mise#8274)
- **(task)** suggest similar commands when mistyping a CLI subcommand by @jdx in [#8286](jdx/mise#8286)
- **(task)** execute monorepo subdirectory prepare steps from root by @jdx in [#8291](jdx/mise#8291)
- **(upgrade)** don't force-reinstall already installed versions by @jdx in [#8282](jdx/mise#8282)
- **(watch)** restore terminal state after watchexec exits by @jdx in [#8273](jdx/mise#8273)

### 📚 Documentation

- clarify that MISE_CEILING_PATHS excludes the ceiling directory itself by @jdx in [#8283](jdx/mise#8283)

### Chore

- replace gen-release-notes script with communique by @jdx in [#8289](jdx/mise#8289)

### New Contributors

- @altendky made their first contribution in [#8290](jdx/mise#8290)

### 📦 Aqua Registry Updates

#### New Packages (4)

- [`Skarlso/crd-to-sample-yaml`](https://github.com/Skarlso/crd-to-sample-yaml)
- [`kunobi-ninja/kunobi-releases`](https://github.com/kunobi-ninja/kunobi-releases)
- [`swanysimon/markdownlint-rs`](https://github.com/swanysimon/markdownlint-rs)
- [`tmux/tmux-builds`](https://github.com/tmux/tmux-builds)

#### Updated Packages (2)

- [`firecow/gitlab-ci-local`](https://github.com/firecow/gitlab-ci-local)
- [`k1LoW/runn`](https://github.com/k1LoW/runn)

## [2026.2.17](https://github.com/jdx/mise/compare/v2026.2.16..v2026.2.17) - 2026-02-19

### 🚀 Features

- **(prepare)** update mtime of outputs after command is run by @halms in [#8243](jdx/mise#8243)

### 🐛 Bug Fixes

- **(install)** use backend bin paths for per-tool postinstall hooks by @jdx in [#8234](jdx/mise#8234)
- **(use)** write to config.toml instead of config.local.toml by @jdx in [#8240](jdx/mise#8240)
- default legacy .mise.backend installs to non-explicit by @jean-humann in [#8245](jdx/mise#8245)

### 🚜 Refactor

- **(config)** consolidate flat task_* settings into nested task.* by @jdx in [#8239](jdx/mise#8239)

### Chore

- **(prepare)** refactor common code into ProviderBase by @halms in [#8246](jdx/mise#8246)

### 📦 Aqua Registry Updates

#### Updated Packages (1)

- [`namespacelabs/foundation/nsc`](https://github.com/namespacelabs/foundation/nsc)
risu729 pushed a commit to risu729/mise that referenced this pull request Feb 27, 2026
…dx#8286)

## Summary
- When a user mistypes a CLI subcommand (e.g., `mise bin-path` instead
of `bin-paths`), mise now suggests similar commands instead of showing a
confusing "no tasks defined" error
- Uses the existing fuzzy matcher (SkimMatcherV2) to find similar
subcommand names and aliases
- Works both when no tasks are defined (replaces confusing error) and
when tasks exist but the name doesn't match any

**Before:**
```
$ mise bin-path
mise ERROR no tasks defined in ~/project. Are you in a project directory?
```

**After:**
```
$ mise bin-path
mise ERROR unknown command: bin-path

Did you mean:
  mise bin-paths
```

## Test plan
- [x] Added e2e test `test_unknown_command_suggestion` covering:
  - `mise bin-path` → suggests `bin-paths`
  - `mise instal` → suggests `install`
  - Error message says "unknown command" not "no tasks defined"
- [x] Existing `test_did_you_mean` e2e test still passes
- [x] All 481 unit tests pass
- [x] Lint passes

Closes jdx#8278

🤖 Generated with [Claude Code](https://claude.com/claude-code)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Error-message-only behavior change using existing fuzzy matching; low
risk aside from potentially misleading suggestions or edge-case
formatting.
> 
> **Overview**
> Improves the task-not-found error path to detect when the provided
name is likely a mistyped CLI subcommand and, in that case, emit an
**"unknown command"** message with up to 3 fuzzy-matched suggestions
(including aliases) instead of misleading **"no tasks defined"** output.
> 
> Adds an e2e regression test
(`e2e/cli/test_unknown_command_suggestion`) covering command-typo
scenarios like `bin-path`→`bin-paths` and `instal`→`install`, and
asserting the new wording.
> 
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
03960c0. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
risu729 pushed a commit to risu729/mise that referenced this pull request Feb 27, 2026
### 🚀 Features

- **(install)** auto-lock all platforms after tool installation by @jdx
in [jdx#8277](jdx#8277)

### 🐛 Bug Fixes

- **(config)** respect --yes flag for config trust prompts by @jdx in
[jdx#8288](jdx#8288)
- **(exec)** strip shims from PATH on Unix to prevent infinite recursion
by @jdx in [jdx#8276](jdx#8276)
- **(install)** validate --locked before --dry-run short-circuit by
@altendky in [jdx#8290](jdx#8290)
- **(release)** refresh PATH after mise up in release-plz by @jdx in
[jdx#8292](jdx#8292)
- **(schema)** replace unevaluatedProperties with additionalProperties
by @jdx in [jdx#8285](jdx#8285)
- **(task)** avoid duplicated stderr on task failure in replacing mode
by @jdx in [jdx#8275](jdx#8275)
- **(task)** use process groups to kill child process trees on Unix by
@jdx in [jdx#8279](jdx#8279)
- **(task)** run depends_post tasks even when parent task fails by @jdx
in [jdx#8274](jdx#8274)
- **(task)** suggest similar commands when mistyping a CLI subcommand by
@jdx in [jdx#8286](jdx#8286)
- **(task)** execute monorepo subdirectory prepare steps from root by
@jdx in [jdx#8291](jdx#8291)
- **(upgrade)** don't force-reinstall already installed versions by @jdx
in [jdx#8282](jdx#8282)
- **(watch)** restore terminal state after watchexec exits by @jdx in
[jdx#8273](jdx#8273)

### 📚 Documentation

- clarify that MISE_CEILING_PATHS excludes the ceiling directory itself
by @jdx in [jdx#8283](jdx#8283)

### Chore

- replace gen-release-notes script with communique by @jdx in
[jdx#8289](jdx#8289)

### New Contributors

- @altendky made their first contribution in
[jdx#8290](jdx#8290)

## 📦 Aqua Registry Updates

#### New Packages (4)

-
[`Skarlso/crd-to-sample-yaml`](https://github.com/Skarlso/crd-to-sample-yaml)
-
[`kunobi-ninja/kunobi-releases`](https://github.com/kunobi-ninja/kunobi-releases)
-
[`swanysimon/markdownlint-rs`](https://github.com/swanysimon/markdownlint-rs)
- [`tmux/tmux-builds`](https://github.com/tmux/tmux-builds)

#### Updated Packages (2)

-
[`firecow/gitlab-ci-local`](https://github.com/firecow/gitlab-ci-local)
- [`k1LoW/runn`](https://github.com/k1LoW/runn)
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